一、概述
ContentProvider 相信我们大家都很熟悉,我用一句话去描述 ContentProvider ,是不同应用程序之间交换数据的标准的 API ,ContentProvider 以某种 Uri 的形式对外提供数据,其他应用程序可以通过 ContentResolver 根据 Uri 去访问和操作指定的数据。 在网上有一种说法是 ContentProvider 可以存储数据,在我看来,ContentProvider 只是操作我们手机中的一些数据,那下面我们先看一下基本的使用方法二、常用的使用方法介绍
我们先看一下我们最基本的 ContentProvider 下面看一下代码:
/**
* Created by andy on 2017/7/11.
*/
public class testContentProvider 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) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
这段代码中我们可以看到,有几个主要的方法分别是 query 、delete、update、insert 四个方法,这四个方法从方法名我们就可以看出来分别是查询,删除,更新,插入,我们可以在四个方法中对我们的数据库进行操作,那当我们调用我们的 ContentResolver 的对应方法的时候,表面上调用的是 ContentProvider 实际上调用的是数据的相关操作。
接下来说一下这些方法中的参数的意义是什么,那这个 Uri 相当于我们要访问哪个 ContentProvider。projection 相当于我们要返回的数据,selection,相当于 SQL 语句中的 where 这个主要是对属性的限制。selectionArgs,配合 selection 使用的限制条件也就是这个是限制的值是什么。sortOrder,相当于 order by。这个 ContentValues 需要说明一些,这个 ContentValue 相当于一个 HashMap<String,Object> 这个数据也是我们要写入数据库的数据,我们可以通过 contentValue.put 加入数据。
接下来看一下 ContentResolver 下面我们还是先看代码:
ContentResolver contentResolver = getContentResolver();
query(@RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)
delete(@RequiresPermission.Write @NonNull Uri url, @Nullable String where, @Nullable String[] selectionArgs) { Preconditions.checkNotNull(url, "url");
pdate(@RequiresPermission.Write @NonNull Uri uri, @Nullable ContentValues values, @Nullable String where, @Nullable String[] selectionArgs)
insert(@RequiresPermission.Write @NonNull Uri url, @Nullable ContentValues values)
之后我们可以根绝 contentResolver 这个对象去调用下面四个方法完成我们对 ContentProvider 的数据的访问。
在我们上面两节中都有提到这个 Uri ,那这个 Uri 到底是什么东西呢,下面一节我们会做一些大致的介绍。
三、Uri 介绍
在我们平时的工作中,我们通常会听说 Url 这个概念,那我们这个 Uri 是什么东西呢,其实 Url 是我们 Uri 中的一种,我们先看一个 Uri 的例子:
ehuodi://andya.top:80/deleteinformation.html
从这个域名中我们可以把他分为几部分,第一部分 ehuodi:// 这个部分是我们的 scheme 的部分,我们访问 ContentProvider 的 Scheme 规定为 content://
andya.top:相当于域名
:80 表示端口
deleteinformation:表示网站的资源,也就是我们常说的路径。
那这几部分组在一起就组成了我们的 Uri 。
Uri 的工具类
UriMatcher:这个工具类主要是判断我们发送的 Uri 是否会有回应,主要是两个方法下面看代码:
addURI(String authority, String path, int code)
match(Uri uri)
第一个方法主要是想 uriMatcher 中加入 Uri ,authority 和 path 组成一个 Uri ,code 代表返回码。
第二个方法是判断我们的 Uri 是否有返回值,如果有返回值那说明有对应的 ContentProvider。
ContentUris:这个类主要是操作 Uri
Uri withAppendedId(Uri contentUri, long id)
long parseId(Uri contentUri)
第一个方法主要是在 contentUri 后面加入一个 id 值
然后第二个方法就是获取这个 Uri 中的 id 值。
操作 Uri 的工具类就说道这里了,下面我们说一下监听数据变化。
四、contentObserver 监听数据变化
说道监听 ContentProvider 的数据变化,我们不得不提到我们的 contentObserver 这个类,通过这个类我们可以,根据数据的变化操作我们的 UI ,那现在我们来看一下监听数据的步骤:
1、当我们数据变化的时候调用 getContext.getContentResolver.notifiChange(uri,null)
2、继承 ContentObserver 这个类在 onChange 方法中处理数据变化
下面看代码:
public class ObserverTest1 extends ContentObserver{
/**
* Creates a content observer.
*
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
public ObserverTest1(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}
}
contentResolver.registerContentObserver(Words.word.DICT_CONTENT,true,new ObserverTest1(new Handler()));
可以看到我们的注册的时候传递了一个 handler 进去,因为我们的这个操作是在线程中,所以我们需要传递一个 handler 处理 UI。
然后在我们的 ObserverTest1 中的 onchange 方法中处理我们数据变化的 UI 。
五、使用 contentProvider 的优缺点
优点: 提供统一的接口来进行访问。 可以进行跨进程的访问。 缺点: 无法单独使用六、原理
我们首先说一下我们的 contentResolver 访问 contentProvider 的数据的
首先我们先看一下 query 方法:
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
我们可以看到,我们在这个方法中有一个 IContentProvider 这个对象,那这个对象有什么作用呢,我们看一下我们的 ContentProvider 类中有个 Transport 类:
class Transport extends ContentProviderNative {
AppOpsManager mAppOpsManager = null;
int mReadOp = AppOpsManager.OP_NONE;
int mWriteOp = AppOpsManager.OP_NONE;
ContentProvider getContentProvider() {
return ContentProvider.this;
}
@Override
public String getProviderName() {
return getContentProvider().getClass().getName();
}
@Override
public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
// The caller has no access to the data
如果你点进入 ContentProviderNative 类,这个类实现了其实实现了我们的 IContentProvider ,所以我们调用了 IContentProvider 的方法其实就是调用了 Transport 的方法,这也是多态的一种实现。其实其他方法也是一样的。
那我们在 ContentResolve 中是怎么获取这个 IContentProvider 的呢 ,我们看一张图就知道了
从图中我们可以看到,我们的应用程序其实是通过 AcivityManagerService 跨进程去访问的,然后给我们返回了 IContentProvider 对象的。
总结:
四大组件基本上写的差不多了,那这篇总结了 ContentProvider 和 ContentResolver 的基本用法,那监听数据的原理有哪位大神知道请指导一下,下周将总结一下我们的广播的知识,如有兴趣请关注,谢谢。
雅歌不会编代码 2017/0711