Android ContentProvider理解与实践

一、概述

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 的 getContentResolver 获取我们应用的 ContentResolver

之后我们可以根绝 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




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值