「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」
上一篇文章,我们没有按照介绍的顺序介绍Content Provider,而是介绍了广播BroadCast,那么这篇文章,我们来介绍最后一个组件,Content Provider,它作为Android开发的四大组件,肯定是很重要的,虽然在开发过程中很少使用,但是不代表就不需要知道,那么我们开始吧。
Content Provider的定义
ContentProvider怎么说呢,说它是数据库嘛,又不只是增删改查那么简单的,说它是Binder,又不只是跨进程,所以它是什么呢,ContentProvider是为不同的应用之间实现数据共享,提供统一的接口,也就是说ContentProvider可以实现进程间的数据共享,实现跨进程通信。那么它能干嘛呢?
1.封装数据,提供统一接口,当项目需求需要修改数据源的时候,节省时间和人力
2.提供一种跨进程数据共享的方式
3.数据更新通知机制。
Content Provider的用法及功能
对于Content Provider我们可以使用系统自带的用法,也可以自定义一个Content Provider来使用。 获取Content Provider的基本方法类似数据库,得到contentResolver类对象:ContentResolver cr = getContentResolver(),然后定义要查询的字段String数组,在使用Query方法返回一个cursor对象,通过遍历获取cursoe里面的内容。
上面我们提到Content Provider实现了数据共享的功能,那么它是怎么实现数据共享的呢,Content Provider通过将我们获取或者保存的数据通过Uri的方式进行数据共享。上面还说的,它类似数据库,但是它不是数据库,它不仅可以进行本应用的数据增删改查,还可以实现不同App直接的数据共享,同时还可以增删改查本地文件和XML文件。而数据库只能增删改查本应用下的数据。
Content Provider的使用
Content Provider和其他组件一样,也是需要在清单文件里面进行注册
<provider
android:authorities="com.test.provider"
android:name=".MyContentProvider"
android:exported="true"/>
既然它的主要功能是实现不同应用间的数据共享,那么我们就针对不同应用间做数据更新,看看效果:
A应用做数据处理,B应用做数据更新:
A应用:
public class MyContentProvider extends ContentProvider {
private String TAG = "MyContentProvider";
//这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities,这里的authorities可以随便写
private static final String AUTHORITY = "com.test.provider";
//匹配成功后的匹配码
private static final int MATCH_ALL_CODE = 1;
private static final int MATCH_ONE_CODE = 2;
private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/test");
private static final UriMatcher uriMatcher;
//在静态代码块中添加要匹配的 Uri
static {
//匹配不成功返回NO_MATCH(-1)
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* uriMatcher.addURI(authority, path, code); 其中
* authority:主机名(用于唯一标示一个ContentProvider,这个需要和清单文件中的authorities属性相同)
* path:路径路径(可以用来表示我们要操作的数据,路径的构建应根据业务而定)
* code:返回值(用于匹配uri的时候,作为匹配成功的返回值)
*/
uriMatcher.addURI(AUTHORITY, "test", MATCH_ALL_CODE);// 匹配记录集合
uriMatcher.addURI(AUTHORITY, "test/#", MATCH_ONE_CODE);// 匹配单条记录
}
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
switch (uriMatcher.match(uri)) {
/**
* 这里如果匹配是uriMatcher.addURI(AUTHORITY, "test",
* MATCH_SUCCESS_CODE);中的Uri,则查询全部
*
*/
case MATCH_ALL_CODE:
Log.d(TAG, "queryAll");
break;
/**
* 这里如果匹配是uriMatcher.addURI(AUTHORITY,
* "test/#",MATCH_ONE_CODE);中的Uri,查询一个
*/
case MATCH_ONE_CODE:
break;
default:
}
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
/**
* 插入 使用UriMatch的实例中的match方法对传过来的 Uri进行匹配。 这里通过ContentResolver传过来一个Uri,
* 用这个传过来的Uri跟在ContentProvider中静态代码块中uriMatcher.addURI加入的Uri进行匹配
* 根据匹配的是否成功会返回相应的值,在上述静态代码块中调用uriMatcher.addURI(AUTHORITY,
* "test",MATCH_CODE)这里的MATCH_CODE
* 就是匹配成功的返回值,也就是说假如返回了MATCH_CODE就表示这个Uri匹配成功了
*/
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
switch (uriMatcher.match(uri)) {
case MATCH_ALL_CODE:
String provider = (String) values.get("provider");
Log.d(TAG, "insertAll--" + provider);
//通知ContentObserver数据发生变化了
notifyDataChanged();
break;
case MATCH_ONE_CODE:
break;
default:
}
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
case MATCH_ALL_CODE:
Log.d(TAG, "deleteAll");
break;
case MATCH_ONE_CODE:
break;
default:
}
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
switch (uriMatcher.match(uri)) {
case MATCH_ALL_CODE:
Log.d(TAG, "updateAll");
break;
case MATCH_ONE_CODE:
break;
default:
}
return 0;
}
//通知指定URI数据已改变
private void notifyDataChanged() {
getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
}
}
再来看B应用:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private String TAG = "provider";
private static final String AUTHORITY = "com.test.provider";
private static final Uri URI = Uri.parse("content://" + AUTHORITY + "/test");
private ContentResolver contentResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initData() {
contentResolver = getContentResolver();
//注册内容观察者
contentResolver.registerContentObserver(URI, true, new MyContentObserver(null));
}
private void initView() {
findViewById(R.id.add).setOnClickListener(this);
findViewById(R.id.delete).setOnClickListener(this);
findViewById(R.id.update).setOnClickListener(this);
findViewById(R.id.query).setOnClickListener(this);
}
@Override
public void onClick(View v) {
ContentValues contentValues = new ContentValues();
contentValues.put(TAG, "provider test");
//通过内容解析者操作内容观察者
switch (v.getId()) {
case R.id.add:
contentResolver.insert(URI, contentValues);
break;
case R.id.delete:
contentResolver.delete(URI, null, null);
break;
case R.id.update:
contentResolver.update(URI, contentValues, null, null);
break;
case R.id.query:
contentResolver.query(URI, null, null, null, null);
break;
}
}
//定义内容观察者
class MyContentObserver extends ContentObserver {
public MyContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(TAG, "改变了");
}
}
}
运行上面代码,可以看到,由于应用A的insert方法,通知了ContentObserver,所以应用B可以收到数据改变的通知。
/provider: insertAll--provider
/provider: 改变了
/MyContentProvider: deleteAll
/MyContentProvider: updateAll
/MyContentProvider: queryAll
相对于其他三个组件,Content Provider的使用没有那么简单,但是很清楚易懂,正常的观察者模式,主要是进程间的通信和数据共享,如果项目中有类似的需求,可以考虑使用。
Content Provider使用过程中的注意事项,最重要的就是开始定义的authorities,一定要统一,保持一致,否是就会报错
安卓开发的四大组件,最后一个也介绍完了,按照惯例,下一篇就是总结啦,欢迎莅临指导指正,留言评论,谢谢