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

