Uri工具:UriMatcher和ContentUris

QuibblerQuibbler 2020-10-09 约 17 分钟 1690 次阅读

Uri工具:UriMatcher和ContentUris

Android中的Uri用处很多,在ContentProvider中用于访问特定数据、用于启动系统特定界面等Activity。关于Uri另一篇笔记有详细的记录,详见Android Uri。

Android系统很庞大,类库众多,提供了非常多的工具类,有两个Uri相关的工具类需要了解一下:UriMatcher和ContentUris。

1、UriMatcher

UriMatcher最常用的地方是用在ContentProvider中,用来匹配Uri,ContentProvider的使用去这里看看ContentProvider笔记。自API Level 1开始就有。

1.1、方法

一般先用公有构造函数创建一个UriMatcher,传入UriMatcher.NO_MATCH常量值为-1。UriMatcher有两个构造函数,另一个为私有构造函数后面会提到,addURI的时候会创建UriMatcher。

UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

UriMatcher只有两个方法,用法示例见ContentProvider用法。

①addURI(String authority, String path, int code):添加一个要匹配的URI,并添加与该URI匹配时返回的代码。 URI节点可以是完全匹配的字符串;或使用通配符:“ *”与任何文本匹配,“#”仅与数字匹配。

②int match(Uri uri):返回匹配节点中的code(使用上面addURI添加的),如果没有匹配节点,返回-1。

1.2、源码解读

UriMatcher数据结构类似于树,先创建一个根节点UriMatcher:

UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

抛去addURI()和match()这两个方法UriMatcher只有一些成员变量:

public class UriMatcher {
        public static final int NO_MATCH = -1;
        private static final int EXACT = 0;
        private static final int NUMBER = 1;
        private static final int TEXT = 2;
        
        ...
        private int mCode;
        private final int mWhich;
        
        private final String mText;
        
        private ArrayList<UriMatcher> mChildren;
    }

先来看看addURI()方法的源码如下

/**
     * 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.
     * 从API 18开始,path支持/开头
     *
     * @param authority 匹配的authority
     * @param path 匹配的path. *可用作任何文本的通配符,#可用作数字的通配符。
     * @param code URI与给定UriMatcher匹配时返回的code。必须是正数。
     */
    public void addURI(String authority, String path, int code)
    {
        //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() > 1 && 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) {
                // 子UriMatcher不存在,则创建
                child = createChild(token);
                node.mChildren.add(child);
                node = child;
            }
        }
        
        //设置当前UriMatcher code为传入的参数code值
        node.mCode = code;
    }

创建子节点UriMatcher时调用内部私有方法createChild(),用到通配符以及私有mWhich字段:

private static UriMatcher createChild(String token) {
        switch (token) {
            case "#":
                return new UriMatcher(NUMBER, "#");
            case "*":
                return new UriMatcher(TEXT, "*");
            default:
                return new UriMatcher(EXACT, token);
        }
    }

再来看看match()方法的源码是如何匹配Uri的

/**
     * Try to match against the path in a url.
     *
     * @param uri       用来匹配的uri
     *
     * @return  匹配到UriMatcher节点的code,如果没有匹配的UriMatcher节点,则返回-1。
     */
    public int match(Uri uri)
    {
        //获取Uri的所有path集合,及大小
        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);
            //当前UriMatcher节点的所有子节点
            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:
                //匹配mWitch成员变量,只定义了三种类型
                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) {
                //没有匹配的UriMatcher节点,返回-1
                return NO_MATCH;
            }
        }
        
        //返回匹配到节点的code
        return node.mCode;
    }

2、ContentUris

ContentUris这个Uri工具类用的不多,内部有四个静态方法,专门用来处理scheme为content://的内容Uri,通用格式如下:

content://authority/path/id

2.1、简单介绍

①long parseId(Uri contentUri):将最后一个路径段转换为long,可能会抛出NumberFormatException异常

public static long parseId(@NonNull Uri contentUri) {
        String last = contentUri.getLastPathSegment();
        return last == null ? -1 : Long.parseLong(last);
    }

②Uri.Builder appendId(Uri.Builder builder, long id):将给定的ID附加到路径的末尾

public static Uri.Builder appendId(Uri.Builder builder, long id) {
        return builder.appendEncodedPath(String.valueOf(id));
    }

③Uri withAppendedId(Uri contentUri, long id):给contentUri路径追加id,Uri的buildUpon()方法详见Android Uri

public static Uri withAppendedId(Uri contentUri, long id) {
        return appendId(contentUri.buildUpon(), id).build();
    }

④Uri removeId(Uri contentUri):从contentUri路径末尾删除ID

相关资料:

         Reference > UriMatcher">Android Developers > Reference > UriMatcher

         Reference > ContentUris">Android Developers > Reference > ContentUris

        

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w