Android进程间通信之ContentProvider

        UriMatcher:

        当我们在使用ContentProvide进行进程间通信时,那我们就必然会用到UriMatcher这个类,这个类的功能可以这样去理解,就是一段字符串与一个数值对应起来,只不过这段字符串是分段存储的,比如"path/csdn",那么在存的时候会先“/”对这个字符串进行分割然后在存起来,先来一段代码:

private static final String AUTHORITY = "com.zzq.uri";
matcher = new UriMatcher(UriMatcher.NO_MATCH);
matcher.addURI(AUTHORITY,"path1/zzq1",1);
matcher.addURI(AUTHORITY,"path1/zzq2",2);
matcher.addURI(AUTHORITY,"path2/zzq3/*",3);
matcher.addURI(AUTHORITY,"path2/zzq4/#",4);
matcher.match(Uri.parse("content://com.zzq.uri/path1/zzq2"));

它的存储图形:

                                                 

我们先来说它是如何匹配的,如果是我们自己定义的字符串匹配,那么这个协议头content://是可以随意取的,实际在这个匹配过程中,这个协议头实际是没有用到的,接下来就是看“com.zzq.uri/path1/zzq2”,内部匹配的时候实际也是讲这个字符串分成了三段,com.zzq.uri、path1、zzq2,分成三段后,先是第一段com.zzq.uri去匹配,匹配到的就只有一个,接下来在这个路径去匹配第二段,匹配到path1,再在path1路径下去匹配第三段,匹配到的结果就是zzq2,zzq2里面有记录添加时对应的数字,到这返回就是2了,对于添加时含有符号是#或*,那么在匹配时,#号对应的位置只要是数字就都可以匹配,而*号对应的位置只要是字符串就都可以匹配。这个类的作用有点像map集合,键是字符串,值是数字。如果还是不太清楚的可以去看源码。

UriMatcher的源码比较短。这里就直接贴出来了:

public class UriMatcher
{
    public static final int NO_MATCH = -1;
    /**
     * Creates the root node of the URI tree.
     *
     * @param code the code to match for the root URI
     */
    public UriMatcher(int code)
    {
        mCode = code;
        mWhich = -1;
        mChildren = new ArrayList<UriMatcher>();
        mText = null;
    }

    private UriMatcher()
    {
        mCode = NO_MATCH;
        mWhich = -1;
        mChildren = new ArrayList<UriMatcher>();
        mText = null;
    }

    /**
     * Add a URI to match, and the code to return when this URI is
     * matched. URI nodes may be exact match string, the token "*"
     * that matches any text, or the token "#" that matches only
     * numbers.
     * <p>
     * Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
     * this method will accept a leading slash in the path.
     *
     * @param authority the authority to match
     * @param path the path to match. * may be used as a wild card for
     * any text, and # may be used as a wild card for numbers.
     * @param code the code that is returned when a URI is matched
     * against the given components. Must be positive.
     */
    public void addURI(String authority, String path, int code)
    {
        if (code < 0) {
            throw new IllegalArgumentException("code " + code + " is invalid: it must be positive");
        }

        String[] tokens = null;
        if (path != null) {
            String newPath = path;
            // Strip leading slash if present.
            if (path.length() > 0 && path.charAt(0) == '/') {
                newPath = path.substring(1);
            }
            tokens = newPath.split("/");
        }

        int numTokens = tokens != null ? tokens.length : 0;
        UriMatcher node = this;
        for (int i = -1; i < numTokens; i++) {
            String token = i < 0 ? authority : tokens[i];
            ArrayList<UriMatcher> children = node.mChildren;
            int numChildren = children.size();
            UriMatcher child;
            int j;
            for (j = 0; j < numChildren; j++) {
                child = children.get(j);
                if (token.equals(child.mText)) {
                    node = child;
                    break;
                }
            }
            if (j == numChildren) {
                // Child not found, create it
                child = new UriMatcher();
                if (token.equals("#")) {
                    child.mWhich = NUMBER;
                } else if (token.equals("*")) {
                    child.mWhich = TEXT;
                } else {
                    child.mWhich = EXACT;
                }
                child.mText = token;
                node.mChildren.add(child);
                node = child;
            }
        }
        node.mCode = code;
    }

    /**
     * Try to match against the path in a url.
     *
     * @param uri       The url whose path we will match against.
     *
     * @return  The code for the matched node (added using addURI), 
     * or -1 if there is no matched node.
     */
    public int match(Uri uri)
    {
        final List<String> pathSegments = uri.getPathSegments();
        final int li = pathSegments.size();

        UriMatcher node = this;

        if (li == 0 && uri.getAuthority() == null) {
            return this.mCode;
        }

        for (int i=-1; i<li; i++) {
            String u = i < 0 ? uri.getAuthority() : pathSegments.get(i);
            ArrayList<UriMatcher> list = node.mChildren;
            if (list == null) {
                break;
            }
            node = null;
            int lj = list.size();
            for (int j=0; j<lj; j++) {
                UriMatcher n = list.get(j);
          which_switch:
                switch (n.mWhich) {
                    case EXACT:
                        if (n.mText.equals(u)) {
                            node = n;
                        }
                        break;
                    case NUMBER:
                        int lk = u.length();
                        for (int k=0; k<lk; k++) {
                            char c = u.charAt(k);
                            if (c < '0' || c > '9') {
                                break which_switch;
                            }
                        }
                        node = n;
                        break;
                    case TEXT:
                        node = n;
                        break;
                }
                if (node != null) {
                    break;
                }
            }
            if (node == null) {
                return NO_MATCH;
            }
        }

        return node.mCode;
    }

    private static final int EXACT = 0;
    private static final int NUMBER = 1;
    private static final int TEXT = 2;

    private int mCode;
    private int mWhich;
    private String mText;
    private ArrayList<UriMatcher> mChildren;
}

        ContentProvide:

        ContentProvide是四大组件之一,所以也是需要在清单文件中去注册的,比如下面注册:
<provider
    android:authorities="com.example.zzq"
    android:name=".MyCOntentProvide"
    android:enabled="true"
    android:exported="true"/>
对于注册的这个ContentProvide,别的应用如何找到呢?那这个就要看authorities这个属性了,我们访问ContentProvide都会用到getContentResolver()这个 方法,调用这个方法后返回的是ContentResolver对象,拿到这个对象后去访问ContentProvider需要我们传一个Uri进去,这里访问我们MyCOntentProvider的Uri是Content://com.example.zzq,这里用到的Uri就是authorities属性,到这里,虽说我们可以访问到ContentProvider,但是这个Uri并没指明他到ContentProvider去做什么,这个时候我们就可以在这个uri后面再添加路径,表明你这个uri是干嘛的,就好比https://baidu.com,这是百度的首页,如果我们要访问android百科,那么就是https://baike.baidu.com/item/Android/60243?fr=aladdin,在https://baidu.com后面加其他的路径,这样我们就可以访问到,ContentProvider用到的其实也是这个思路,只不过这后面的路径是需要我们自己去解析的,根据解析到的结果去做对应的处理,这里处理路径用到的就是UriMatcher。
        这里先上一段继承自ContentProvider的简单事例:
public class MyCOntentProvide extends ContentProvider {
    private static final String TAG = "MyCOntentProvide";

    private static final String authority = "com.example.zzq";
    private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

    private static final String path1 = "path1";
    private static final String path2 = "path2";
    private static final String path3 = "path3";
    private static final String path4 = "path4";
    private static final String path5 = "path5";

    private static final int code1 = 1;
    private static final int code2 = 2;
    private static final int code3 = 3;
    private static final int code4 = 4;
    private static final int code5 = 5;

    static{
        matcher.addURI(authority,path1,code1);
        matcher.addURI(authority,path2,code2);
        matcher.addURI(authority,path3,code3);
        matcher.addURI(authority,path4,code4);
        matcher.addURI(authority,path5,code5);
    }

    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        int match = matcher.match(uri);
        switch (match){
            case code1:
                //你的处理
                break;
            case code2:
                //你的处理
                break;
            case code3:
                //你的处理
                break;
            case code4:
                //你的处理
                break;
            case code5:
                //你的处理
                break;
        }
        MatrixCursor cursor = new MatrixCursor(new String[]{"name","age"});
        cursor.addRow(new String[]{"zzq","12"});
        return cursor;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return "根据需要返回,也可以不做处理";
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        int match = matcher.match(uri);
        switch (match){
            case code1:
                //你的处理
                break;
            case code2:
                //你的处理
                break;
            case code3:
                //你的处理
                break;
            case code4:
                //你的处理
                break;
            case code5:
                //你的处理
                break;
        }
        return uri;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 1;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 2;
    }
}
当我们需要访问这个ContentProvider的时候,只需要调用getContentResolver()返回ContentResolver对象,然后调用ContentProvider的方法中对应的方就可以访问到,方法中需要我们传一个uri,比如上面这个事例,我们的uri中只要是content://com.example.zzq/...都是可以访问到这个ContentProvider的,那后面携带什么路径以及做什么对应的处理,那就是我们自己做定义了。比如我们传过来的是一个uri是content://com.example.zzq/path2,那么在调用match.match(uri)后,返回的就是2,和上面我们在UriMatcher中添加path2的数字对应上,这个时候我们可以看出UriMatcher其实就是一个工具类,如果不想用这个类,那也可以用Map来替代,我们做好对应的处理就ok了。在query()这个方法中需要我们返回的是一个Cursor,这时候我们可以自己构建一个MatrixCursor,然后往里面添加需要返回的数据就可以了。在这里做的操作不一定是访问数据库,任何操作都是你可以做的。耗时操作不要在这里处理,否者会报ANR。

        对于ContentProvider的使用,这里可以分为两个步骤,一是ContentProvider的注册,二是uri解析:

        1、对于ContentProvider的注册,其主要目的是对外提供访问这个ContentProvider的路径,也就是它的基路径,一般都是content://authorities的值,这个authorities就是注册这个ContentProvider的时候里面的一个属性。

        2、有了基路径content://authoritie后,后面可以在加一些其他的路径content://authorities/...,这样访问到的也是你定义的这个ContentProvider,后面加的参数是什么参数,要做什么处理,那就是你自己定义了,Android为我们提供了对这个路径的处理,那就是UriMatcher。        



猜你喜欢

转载自blog.csdn.net/tangedegushi/article/details/80341675