1 自定义内容提供器
首先新建一个继承自 ContentProvider 的类, 实现它的 6 个抽象方法:
方法 | 说明 |
---|---|
public boolean onCreate() | 初始化时被调用,只有 ContentResolver 尝试访问我们 APP 的程序数据时才会执行初始化操作;在此完成创建与升级数据库的操作,返回 true 表示初始化成功。 |
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) | 查询数据;uri 确定要查询的表;projection 确定查询列;selection 和 selectionArgs 约束条件;sortOrder 确定排序。 |
public String getType(Uri uri) | 根据传入的的内容 Uri 返回相应的 MIME 类型。 |
public Uri insert(Uri uri, ContentValues values) | 新增数据;uri 确定要新增的表;values 存放需要添加的数据。 |
public int delete(Uri uri, String selection, String[] selectionArgs) | 删除数据;uri 确定要更新的表;selection 和 selectionArgs 约束条件;返回受影响的行数。 |
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) | 更新数据;uri 确定要删除的表;selection 和 selectionArgs 约束条件;返回受影响的行数。 |
内容 URI 的写法类型有这些:
类型 | 写法 | 示例 | 说明 |
---|---|---|---|
标准 | content:// | content://net.deniro.app/table | 访问应用中 table 表的所有数据。 |
加 id | content:// | content://net.deniro.app/table/1 | 访问应用中 table 表 id 为 1 的数据。 |
还可以使用通配符来匹配以上两种格式的内容 URI:
通配符 | 说明 |
---|---|
* | 匹配任意长度的任意字符。 |
# | 匹配任意长度的数字。 |
示例:
content://net.deniro.app/*
: 访问应用中任意表.
content://net.deniro.app/table/#
: 访问应用中 table 表的任意一行数据.
getType() 方法返回的 MIME 类型字符串由三个部分组成:
以 vnd 开头.
如果内容 URI 以路径结尾, 则后接 android.cursor.dir/; 如果内容 URI 以 id 结尾, 则后接
- android.cursor.item/
- .
最后为
- vnd.<authority>.<path>
- .
比如内容 URI 为
content://net.deniro.app/table
的 MIME 类型字符串是:
- vnd.android.cursor.dir/vnd.net.deniro.app.table
- .
2 跨 APP 数据共享
假设有一个工程管理项目, 本身包含对人员的 CRUD 操作, 现在把这些操作开放给其他 APP .
使用 IDEA 创建一个内容提供器, 右键点击包名 New Other Content Provider:
在弹出的对话框中, 输入类名与 URI 权限路径:
Exported: 是否允许外部 APP 访问这个内容提供器.
Enabed: 是否启用这个内容提供器.
以上两项全部勾选.
- /**
- * 自定义内容提供器
- */
- public class CustomContentProvider extends ContentProvider {
- private static final String TAG = "CustomContentProvider";
- /**
- * 表代码
- */
- public static final int DIR = 0;
- /**
- * 记录代码
- */
- public static final int ITEM = 1;
- /**
- * URI 权限
- */
- public static final String AUTHORITY = "net.deniro.app.provider";
- /**
- * 表名
- */
- public static final String TABLE_NAME = "people";
- /**
- * 内容 URI 前缀
- */
- public static final String CONTENT_PREFIX = "content://";
- /**
- * MIME 类型前缀
- */
- public static final String MIME_PREFIX = "vnd.";
- /**
- * MIME 类型前缀 (所有记录)
- */
- public static final String MIME_INFIX_DIR = "android.cursor.dir";
- /**
- * MIME 类型前缀 (某个 ID 记录)
- */
- public static final String MIME_INFIX_ITEM = "android.cursor.item";
- private static UriMatcher matcher;
- static {
- matcher = new UriMatcher(UriMatcher.NO_MATCH);
- matcher.addURI(AUTHORITY, TABLE_NAME, DIR);
- matcher.addURI(AUTHORITY, TABLE_NAME + "/#", ITEM);
- }
- private PeopleDatabaseHelper helper;
- @Override
- public boolean onCreate() {
- Log.d(TAG, "onCreate:");
- helper = new PeopleDatabaseHelper(getContext(), "People.db", null, 3);
- return true;
- }
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- Log.d(TAG, "insert:"+values);
- SQLiteDatabase db = helper.getWritableDatabase();
- Uri result = null;
- switch (matcher.match(uri)) {
- case DIR:
- case ITEM:
- long id = db.insert(TABLE_NAME, null, values);
- result = Uri.parse(CONTENT_PREFIX + AUTHORITY + "/" + TABLE_NAME + "/" + id);
- break;
- default:
- break;
- }
- return result;
- }
- @Override
- public int update(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- SQLiteDatabase db = helper.getWritableDatabase();
- int rows = 0;
- switch (matcher.match(uri)) {
- case DIR:// 更新带条件约束表记录
- rows = db.update(TABLE_NAME, values, selection, selectionArgs);
- break;
- case ITEM:// 更新指定 ID 的表记录
- String id = uri.getPathSegments().get(1);
- rows = db.update(TABLE_NAME, values, "id = ?", new String[]{id});
- break;
- default:
- break;
- }
- return rows;
- }
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- SQLiteDatabase db = helper.getWritableDatabase();
- int rows = 0;
- switch (matcher.match(uri)) {
- case DIR:// 删除带条件约束表记录
- rows = db.delete(TABLE_NAME, selection, selectionArgs);
- break;
- case ITEM:// 删除指定 ID 的表记录
- String id = uri.getPathSegments().get(1);
- rows = db.delete(TABLE_NAME, "id = ?", new String[]{id});
- break;
- default:
- break;
- }
- return rows;
- }
- @Override
- public String getType(Uri uri) {
- switch (matcher.match(uri)) {
- case DIR:
- return MIME_PREFIX + MIME_INFIX_DIR + "/" + MIME_PREFIX + AUTHORITY + "." + TABLE_NAME;
- case ITEM:
- return MIME_PREFIX + MIME_INFIX_ITEM + "/" + MIME_PREFIX + AUTHORITY + "." + TABLE_NAME;
- }
- return null;
- }
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- SQLiteDatabase db = helper.getReadableDatabase();
- Cursor cursor = null;
- switch (matcher.match(uri)) {
- case DIR:
- cursor = db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder);
- break;
- case ITEM:
- String id = uri.getPathSegments().get(1);
- cursor = db.query(TABLE_NAME, projection, "id = ?", new String[]{id}, null, null, sortOrder);
- break;
- default:
- break;
- }
- return cursor;
- }
- }
这里在静态代码块中, 使用了 UriMatcher 类来实现匹配内容 URI 的功能. 它的 addURI() 方法接收 authority,path 和自定义码.
Uri 的 getPathSegments() 会把内容 URI 权限之后的部分以 / 进行分割, 所以索引位置为 0 存放的是路径, 索引位置为 1 存放的 id.
注意: 内容 URI 前缀是 content://, 它是大小写敏感的, Content:// 或 contents:// 都是错误的写法.
最后一步是配置自定义的内容提供器, 因为我们是使用 IDEA 添加的, 所以在 AndroidManifest.xml 中已经自动添加咯:
<provider
android:name=".CustomContentProvider"
android:authorities="net.deniro.app.provider"
android:enabled="true"
android:exported="true"></provider>
关闭这个项目 A, 重新建立一个新项目 B, 在这个新项目 B 中通过自定义内容提供器来访问之前的项目 A.
布局:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/activity_main"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <Button
- android:id="@+id/add"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="新增" />
- <Button
- android:id="@+id/query"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="查询" />
- <Button
- android:id="@+id/update"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="修改" />
- <Button
- android:id="@+id/delete"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="删除" />
- </LinearLayout>
布局很简单, 放置 4 个按钮, 点击它们触发另一个 APP 的相关操作.
- Activity:
- public class MainActivity extends AppCompatActivity {
- private static final String TAG = "MainActivity";
- private String id;
- /**
- * 内容 URI
- */
- public static final String URI = "content://net.deniro.app.provider/people";
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- /**
- * 新增
- */
- findViewById(R.id.add).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- ContentValues values = new ContentValues();
- values.put("name", "jack");
- values.put("age", 20);
- values.put("weight", 120.5);
- Uri uri = getContentResolver().insert(Uri.parse(URI), values);
- id = uri.getPathSegments().get(1);
- }
- });
- /**
- * 查询
- */
- findViewById(R.id.query).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Cursor cursor = getContentResolver().query(Uri.parse(URI), null, null, null, null);
- if (cursor != null) {
- while (cursor.moveToNext()) {
- Log.d(TAG, "姓名:" + cursor.getString(cursor.getColumnIndex("name")));
- Log.d(TAG, "年龄:" + cursor.getInt(cursor.getColumnIndex("age")));
- Log.d(TAG, "体重 (斤):" + cursor.getDouble(cursor.getColumnIndex("weight")));
- }
- cursor.close();
- }
- }
- });
- /**
- * 修改
- */
- findViewById(R.id.update).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- ContentValues values = new ContentValues();
- values.put("age", 25);
- values.put("weight", 140.25);
- getContentResolver().update(Uri.parse(URI + "/" + id), values, null, null);
- }
- });
- /**
- * 删除
- */
- findViewById(R.id.delete).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- getContentResolver().delete(Uri.parse(URI + "/" + id), null, null);
- }
- });
- }
- }
这些方法都是通过 getContentResolver() 获得 ContentResolver 后, 调用 ContentResolver 的 CRUD 方法来实现相应操作的.
进行测试阶段, 首先安装项目 A, 然后安装项目 B, 在项目 B 中点击 CRUD 按钮:
点击 [新增] 按钮后, 查看日志:
D/CustomContentProvider: insert: name=jack weight=120.5 age=20
点击 [查询] 按钮后, 查看日志:
D/MainActivity: 姓名: jack
D/MainActivity: 年龄: 20
D/MainActivity: 体重 (斤): 120.5
点击 [修改] 按钮后, 查看日志:
D/MainActivity: 姓名: jack
D/MainActivity: 年龄: 25
D/MainActivity: 体重 (斤): 140.25
是不是很酷呀 O(_)O 哈哈~
来源: http://www.jianshu.com/p/a5de880973f3