在开发中,我们经常会调用系统联系人,系统相册,视频等。联系人,相册这些算是一个独立的应用,那我们自己的应用怎么能调用其他应用的数据呢?
这其实就是ContentProvider的功劳了,ContentProvider:Android四大组件之一,其作用相当于两个不同应用之前数据交互的桥梁,通过它可以实现跨应用数据共享。
实现步骤
第一步:被调用方提供操作数据的接口,通过继承ContentProvider并重写insert(),delete(),query(),update()完成增删查改的功能。
注意:这里的这几个方法看起来跟数据库的很像,但是这并不意味着数据源必须是数据库,也可以是文件,SharedPreference等,只是大部分数据源都是用的数据库而已。
第二步:被调用方在Manifest.xml文件中注册上一步实现的ContentProvider。
第三步:调用方通过ContentResolver用Uri与第一步创建的ContentProvider连接,并调用暴露出的方法实现数据交互。
下面用代码来说明ContentProvider的使用,顺便吐槽一句,好多人写博客,明明是讲这个,非要在里面揉一大推其他的东西,讲个ContentProvider,三分之二都是SqlLite的操作,除了篇幅多了还有什么卵用。
创建ContentProvider,都是需要重新的父类方法
public class MyContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(Uri uri) {
Log.v("out" , "getType方法执行了");
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.v("out" , "Insert方法执行了");
return uri;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.v("out" , "getType方法执行了");
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
在Manifest.xml中注册
<provider
android:authorities="com.wangliang160616.androidtest.hello"
android:name="com.wangliang160616.androidtest.others.MyContentProvider"
android:exported="true"/>
* 创建一个Activity调用这个ContentProvider,为了实现跨进程调用,我们配置了这个Activity的process属性让它运行在另外一个进程中*
public class ContentProviderTestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int pid = android.os.Process.myPid();
Log.v("out" , "新Activity的进程:"+pid);
setContentView(R.layout.activity_content_provider_test);
//创建ContentResolver对象
ContentResolver contentResolver = getContentResolver();
Uri uri = Uri.parse("content://"+"com.wangliang160616.androidtest.hello/a/b/10");
ContentValues contentValues = new ContentValues();
Uri returnUri = contentResolver.insert(uri , contentValues);
Log.v("out" , "返回的uri:"+returnUri);
}
}
看一下结果:
最后,还有一个技能点需要get,就是Uri的处理,上面我们将Uri传到了ContentProvider,拿到Uri后需要解析。
UriMatcher和ContentMatcher(下面的代码是网上扒来的,非原创)
uriMatcher用于匹配Uri
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.bing.procvide.personprovider/person路径,返回匹配码为1
sMatcher.addURI("com.bing.procvide.personprovider", "person", 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配content://com.bing.provider.personprovider/person/230路径,返回匹配码为2
sMatcher.addURI("com.bing.provider.personprovider", "person/#", 2);//#号为通配符
switch (sMatcher.match(Uri.parse("content://com.ljq.provider.personprovider/person/10"))) {
case 1
break;
case 2
break;
default://不匹配
break;
}
ContentUris类用于操作Uri路径后面的ID部分:
Uri uri = Uri.parse("content://com.bing.provider.personprovider/person")
Uri resultUri = ContentUris.withAppendedId(uri, 10);
//生成后的Uri为:content://com.bing.provider.personprovider/person/10
//parseId(uri)方法用于从路径中获取ID部分:
Uri uri = Uri.parse("content://com.ljq.provider.personprovider/person/10")
long personid = ContentUris.parseId(uri);//获取的结果为:10
补充:还有很重要的一点,数据变化和监听
设置监听也很简单,ContentResolver调用registerContentObserver()方法即可。
//注册监听
//这个true代表了只要是uri的派生出的Uri变化了也会通知
contentResolver.registerContentObserver(uri, true, new ContentObserver(null) {
//onChange还有一个更多参数的重载方法,但是没什么用,还是调用的这个方法,所以只需要重写这个就可以了
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.v("out" , "我知道你变了");
}
});
然后在数据变化的地方开启通知:
getContext().getContentResolver().notifyChange(uri , null);