在我们Android开发中难免会用到Content Provider,主要是为了实现进程间访问数据,数据库是Android开发中最基本的数据保存方式,但由于数据库的私有性,我们无法对外提供或获取信息,当两个应用需要实现数据共享时,此时就需要本篇文章的主题——ContentProvider
一.提供数据端-contentprovider
1.首先新建一个自己的content provider类继承自ContentProvider,重写onCreate insert query update delete getType方法,在里面做一些自己的对数据操作方法供外部调用,另外需要用UriMatcher 类对访问数据路径Uri进行初始话,指明authorities,Uri路径类似content://authorities/path。
public class CourseContentProvider extends ContentProvider {
private final String TAG = this.getClass().getName();
private Context mContext;
private DBHelper mDbHelper = null;
private SQLiteDatabase db = null;
public static final String AUTOHORITY = "com.lmy.provider";
public static final String COURSE_PATH = "course";
private static final int PROVIDE_COURSE = 1;
private static final UriMatcher mMatcher;
static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(AUTOHORITY, COURSE_PATH, PROVIDE_COURSE);
// 若URI资源路径 = content://com.lmy.provider/course ,则返回注册码 PROVIDE_COURSE
}
/**
* 初始化ContentProvider
*/
@Override
public boolean onCreate() {
Log.d(TAG,"onCerate,current thread:"+Thread.currentThread().getName());
return true;
}
/**
* 添加数据
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d(TAG,"insert,current thread:"+Thread.currentThread().getName());
return uri;
}
/**
* 查询数据
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Log.d(TAG,"query,current thread:"+Thread.currentThread().getName());
return null;
}
/**
* 更新数据
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.d(TAG,"update,current thread:"+Thread.currentThread().getName());
return 0;
}
/**
* 删除数据
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.d(TAG,"delete,current thread:"+Thread.currentThread().getName());
return 0;
}
@Override
public String getType(Uri uri) {
Log.d(TAG,"getType,current thread:"+Thread.currentThread().getName());
return null;
}
/**
* 根据URI匹配 URI_CODE,从而匹配ContentProvider中相应的表名
*/
private String getTableName(Uri uri) {
String tableName = null;
switch (mMatcher.match(uri)) {
case 1:
tableName = DBHelper.USER_TABLE_NAME;
break;
case 2:
tableName = DBHelper.JOB_TABLE_NAME;
break;
}
return tableName;
}
}
2.就是在AndroidManifest里面声明content provider,代码如下
<provider
android:name="com.example.contentproviderdemo.CourseContentProvider"
android:authorities="com.lmy.provider"
android:exported="true"
android:readPermission="com.example.contentproviderdemo.READ"
android:writePermission="com.example.contentproviderdemo.WRITE" />
这里需要注意的就是自定义权限,如果这里没有添加readPermission writePermission,那么第三方应用在调用时也不需要申请此权限就可以访问数据库进行读写;如果这里添加了这两个权限,那么首先我们要明确这两个是属于自定义权限,需要在此处(也是就content provider 端)将这两个权限添加到系统中,这样第三方应用在申请这两个权限时系统才知道是要申请哪个权限,然后就是第三方应用需要像平时那样申请权限的方式将这两个自定义权限申请一下,这里需要注意一个坑,这种自定义权限系统只会在安装您的应用时,才会隐式允许此请求,也就是说当你第一次编译安装apk时没有去申请这两个权限,那么后来你添加了之后再编译是不生效的,只能删了apk重新再编译安装apk才可以生效(困了我一下午的坑)。
3.在系统中注册自定义权限
<permission
android:name="com.example.contentproviderdemo.READ"
android:label="provider pomission"
android:protectionLevel="normal" />
<permission
android:name="com.example.contentproviderdemo.WRITE"
android:label="provider pomission"
android:protectionLevel="normal" />
二.访问数据端-contentresolver
1.首先在AndroidManifest中申请provider的自定义权限,代码如下:
<uses-permission android:name="com.example.contentproviderdemo.READ"/>
<uses-permission android:name="com.example.contentproviderdemo.WRITE"/>
2.在使用的地方通过 ContentResolver 类访问 provider 提供的数据库数据,此处的Uri路径要保证跟前面provider中的一致,贴代码:
public class MainActivity extends AppCompatActivity {
private ContentResolver mContentResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContentResolver = this.getContentResolver();
}
public void onClickTest(View view) {
Uri uri = Uri.parse("content://com.lmy.provider/course");
mContentResolver.query(uri, null, null, null, null);
// mContentResolver.delete(uri,null,null);
// mContentResolver.insert(uri, null);
}
}
最后测试可以走到query中,对应输出日志如下,可以看到onCreate 和 query不在一个进程中,一个在main线程,一个在Binder线程:
2021-03-04 19:05:30.769 31059-31059/com.example.contentproviderdemo D/com.example.contentproviderdemo.CourseContentProvider: onCerate,current thread:main
2021-03-04 19:06:07.391 31059-31077/com.example.contentproviderdemo D/com.example.contentproviderdemo.CourseContentProvider: query,current thread:Binder:31059_1
三.总结
到此文章已经讲解完毕,这里只是简单的进行了不同进程间访问数据的一个调用demo,仅仅是已输出日志形式来看的,具体操作数据库那块没有添加,感兴趣的小伙伴可以用 SQLiteDatabase 进行操作,大家有什么问题欢迎在下面留言评论,我们下期见!