02、ContentURI、UriMatcher与MIME类型
02、Content URI、UriMatcher 与 MIME 类型
一、Content URI 是什么
Content URI 是 ContentProvider 暴露数据的地址。
标准格式:
content://authority/path/id
示例:
content://com.example.databasetest.provider/book
content://com.example.databasetest.provider/book/1
拆开看:
| 部分 | 示例 | 说明 |
|---|---|---|
| scheme | content:// |
表示这是 ContentProvider URI |
| authority | com.example.databasetest.provider |
Provider 的唯一标识 |
| path | /book |
表示访问哪类数据 |
| id | /1 |
可选,表示访问某一条具体数据 |
二、authority 的作用
authority 用来唯一标识一个 ContentProvider。
一般写法:
应用包名.provider
例如:
com.example.databasetest.provider
Manifest 中注册时也要写同一个 authority:
<provider
android:name=".DatabaseProvider"
android:authorities="com.example.databasetest.provider"
android:exported="true" />
调用方访问时:
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
系统就是通过 authority 找到对应的 Provider。
三、path 的作用
path 用来区分 Provider 内部的不同数据集合。
例如同一个 Provider 中有两张表:
Book
Category
可以设计成:
content://com.example.databasetest.provider/book
content://com.example.databasetest.provider/category
这样 book 和 category 就分别表示不同资源。
四、集合 URI 与单条 URI
ContentProvider 通常会设计两类 URI。
1. 集合 URI
表示访问一类数据:
content://com.example.databasetest.provider/book
含义:
访问 Book 表中的多条数据
2. 单条 URI
表示访问某一条数据:
content://com.example.databasetest.provider/book/1
含义:
访问 Book 表中 id = 1 的数据
五、Uri.parse() 的正确写法
正确写法:
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
错误写法:
Uri uri = new Uri.parse("content://com.example.databasetest.provider/book");
原因:
parse() 是 Uri 的静态方法,不需要 new。
六、ContentUris 工具类
如果要拼接 ID,不建议手动字符串拼接,可以使用 ContentUris。
拼接 ID
Uri uri = Uri.parse("content://com.example.databasetest.provider/book");
Uri itemUri = ContentUris.withAppendedId(uri, 1);
结果:
content://com.example.databasetest.provider/book/1
解析 ID
long id = ContentUris.parseId(itemUri);
适合处理这种 URI:
content://com.example.databasetest.provider/book/1
七、为什么需要 UriMatcher
Provider 收到调用时,只拿到一个 Uri。
它需要判断:
这个 URI 是访问 book 列表?
还是访问 book 中某一条?
还是访问 category 列表?
还是访问 category 中某一条?
这就需要 UriMatcher。
八、UriMatcher 通配符
UriMatcher 支持两个常见通配符:
| 通配符 | 含义 | 示例 |
|---|---|---|
* |
匹配任意文本 | book/* |
# |
匹配数字 | book/# |
常用规则:
book -> Book 集合
book/# -> Book 单条数据
category -> Category 集合
category/# -> Category 单条数据
九、UriMatcher 基本写法
public class DatabaseProvider extends ContentProvider {
public static final int BOOK_DIR = 0;
public static final int BOOK_ITEM = 1;
public static final int CATEGORY_DIR = 2;
public static final int CATEGORY_ITEM = 3;
public static final String AUTHORITY = "com.example.databasetest.provider";
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);
uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
}
}
注意:
final
不要写成:
fianl
十、match() 的使用
在 query() 中:
@Override
public Cursor query(
Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder
) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return db.query("Book", projection, selection, selectionArgs, null, null, sortOrder);
case BOOK_ITEM:
String bookId = uri.getPathSegments().get(1);
return db.query("Book", projection, "id = ?", new String[]{bookId}, null, null, sortOrder);
case CATEGORY_DIR:
return db.query("Category", projection, selection, selectionArgs, null, null, sortOrder);
case CATEGORY_ITEM:
String categoryId = uri.getPathSegments().get(1);
return db.query("Category", projection, "id = ?", new String[]{categoryId}, null, null, sortOrder);
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
uri.getPathSegments() 的结果示例:
URI: content://com.example.databasetest.provider/book/1
pathSegments[0] = book
pathSegments[1] = 1
十一、getType() 是什么
getType(Uri uri) 用于返回某个 URI 对应的数据 MIME 类型。
它不是最常用的方法,但自定义 Provider 时必须实现。
十二、MIME 类型规则
Android 对 ContentProvider 的 MIME 类型有固定约定。
1. 多条数据
如果 URI 表示一个数据集合,使用:
vnd.android.cursor.dir/vnd.authority.path
示例:
content://com.example.databasetest.provider/book
对应:
vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book
2. 单条数据
如果 URI 表示一条具体数据,使用:
vnd.android.cursor.item/vnd.authority.path
示例:
content://com.example.databasetest.provider/book/1
对应:
vnd.android.cursor.item/vnd.com.example.databasetest.provider.book
重点区别:
| URI 类型 | MIME 中间部分 |
|---|---|
| 集合 URI | vnd.android.cursor.dir |
| 单条 URI | vnd.android.cursor.item |
十三、getType() 示例
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case BOOK_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";
case BOOK_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";
case CATEGORY_DIR:
return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";
case CATEGORY_ITEM:
return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
}
十四、常见 URI 设计错误
1. authority 不一致
Manifest:
android:authorities="com.example.app.provider"
代码访问:
Uri.parse("content://com.example.other.provider/book")
这样系统找不到 Provider。
2. path 没有加入 UriMatcher
访问:
content://com.example.app.provider/user
但是只注册了:
uriMatcher.addURI(AUTHORITY, "book", BOOK_DIR);
结果 match() 会返回 NO_MATCH。
3. 单条 URI 没有处理 ID
注册了:
uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);
但是在代码中仍然当成集合查询,没有取出 ID,就会造成逻辑错误。
4. MIME 类型写错
单条 URI 应该返回:
vnd.android.cursor.item/...
不是:
vnd.android.cursor.dir/...
十五、核心记忆
content://authority/path/id
对应关系:
| 部分 | 记忆 |
|---|---|
content:// |
ContentProvider 专用 scheme |
authority |
找哪个 Provider |
path |
找 Provider 中哪类数据 |
id |
找某一条数据 |
UriMatcher |
把 URI 映射成业务代码 |
getType() |
根据 URI 返回 MIME 类型 |
02、ContentURI、UriMatcher与MIME类型
https://lautung.com/archives/02%E3%80%81contenturi%E3%80%81urimatcher%E4%B8%8Emime%E7%B1%BB%E5%9E%8B