Android学习笔记之ContentProvider

1.ContentProvider 内容提供者

  • 内容提供者:主要用于在不同的应用程序之间实现数据共享的功能,允许一个程序访问另一个程序中的数据,同时还能保证被访问数据的安全性。与其他存储方式不同的是:可以选择哪部分数据分享,从而保证隐私数据的安全性。

1.1ContentProvider 特点

  1. 四大组件之一,需要在 AndroidMainifest.xml 文件中进行注册。
  2. 接口的统一,是为数据的获取、添加、修改的操作提供统一的接口。
  3. 跨进程供多个应用程序共享数据;
  4. 设备存储数据:通讯录、图片;
  5. 数据更新监听:UI更新;

1.2ContentProvider 的优缺点

  1. 数据访问统一接口(存储方式都不用管)优点。
  2. 跨进程数据的访问,优点。
  3. 无法单独使用,必须与其他的存储方式结合使用,缺点。

1.3ContentProvider 如何提供数据,实现共享?

  1. 新建类去继承ContentProvider,然后覆写 query、insert、update、delete 等6个抽象方法。
  2. 必须在 AndroidManifest 文件中进行注册。
  3. 把自己的数据通过 uri 的形式共享出去。
  4. 借助UriMather.match()方法实现匹配内容URI的功能。
  5. 第三方可以通过 ContentResolver 来访问该 Provider。

1.4ContentProvider工作流程

  1. 应用程序A(提供者)定义了 ContentProvider,定义了一些数据库和文件来存放数据。
  2. 应用程序B(访问者)获得 ContentResolver 对象,根据Uri访问指定的 ContentProvider。
  3. ContentProvider 就会访问底层的存储方式,并返回数据给 ContentProvider。
  4. ContentProvider 将数据返回给 ContentResolver,ContentResolver 再转到应用B。

2.ContentResolver 内容访问者

  • 想要访问内容提供器中的共享数据,就一定要借助 ContentResolver 类,可以通过 getContentResolver() 方法获取该类实例,并且提供了一系列CRUD操作(insert、update、delete、query),不同于其他存储方式的是不接收表名参数,而是用 Uri 参数来代替(由协议声明、authority和path组成)。

2.1ContentResolver如何实现数据的访问

  • ContentResolver的Contex.getContentResolver().query()
    • 提供了CRUD操作;
    • Transport implements;
    • IContentProvider;
  • Uri,ContentResolver中的增删查改都是用Uri来代替表名参数。
    • 概念:统一资源标识符;
    • 组成:协议、域名authority、路径path;
    • content://com.example.app.authority/path
    • 作用:访问ContentProvider;
    • 不同:与其他组件交互不同的地方(Intent);
//简单演示获取数据都代码
Cursor mCursor = getContentResolver().query(uri,projection,selection,selectionArgs,orderBy);
if (mCursor != null) {
     while (mCursor.moveToNext()) {
        String column1 = mCursor.getString(mCursor.getColumnIndex("column1"));
        int column2 = mCursor.getInt(mCursor.getColumnIndex("column2"));
    }
    mCursor.close();
}

3.ContentObserver 内容观察者

Android 内部提供了一种 ContentObserver 来监听数据库内容的变化。作用是观察(捕捉)特定 Uri 引起的数据库的变化,继而做一些相应的处理。

3.1如何定义ContentObserver

  • 第一步:创建一个 ContentObserver 的子类,实现 onChange() 方法。
    • 监听的 Uri 中的数据发生变化的时候,会调用 onchange() 方法。
    • 构造方法:public void ContentObserver(Handler handler)参数需要一个 Hanlder,因为 ContentObserver 内部使用了一个实现 Runnable 接口的内部类 NotificationRunnable,需要通过 Handler 去做比如 UI 变化。(可以从主线程传入一个 handler。)
  • 第二步:通过 registerContentObserver 注册 ContentObserver。
  • 第三步:用完后记得取消注册 unregisterContentObserver。
  • 第四步:ContentProvider数据源发送改变后,通知ContentObserver。
//第一步: 创建一个 ContentObserver 的子类,实现 onChange() 方法。
public class MyContentObserver extends ContentObserver{
    private Handler handler;
    public MyContentObserver(Handler handler) {
        super(handler);
        this.handler=handler;
    }
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        //  在onChange方法里面我们就可以执行变化的操作了
    }
}
//第二步:将这个监听者与我们所要监听的对象绑定
//通过 registerContentObserver 注册 ContentObserver。
MyContentObserver mObserver = new MyContentObserver(); 
getContentResolver().registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer);
//第三步:取消注册 unregisterContentObserver
getContentResolver().unregisterContentObserver(mObserver)
//ContentProvider数据源发送改变后,通知ContentObserver。
getContext().getContentResolver().notifyChange(uri, null);

3.2也可以这样简单写


public class Main2Activity extends AppCompatActivity {
    Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //在主线程更新UI  adapter.notifyChangeSet();
        }
    };
    ContentObserver observer = new ContentObserver(mHandler) {
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            //Uri数据改变,非Ui线程不能直接更新
            //发送消息
        }
    };
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //注册内容观察者
        this.getContentResolver().registerContentObserver(uri, notifyForDescendants, observer);
    }
}

3.3为什么自定义的 ContentProvider 数据源发生改变后,却没有监听到任何反应?

每个ContentProvider数据源发生改变后,如果想通知其监听对象, 例如ContentObserver时,必须在其对应方法 update / insert / delete 时,显式地调用 this.getContentReslover().notifychange(uri,null) 方法,回调监听处理逻辑。否则,我们的 ContentObserver 是不会监听到数据发生改变的。

4.说说ContentProvider、ContentResolver、ContentObserver 之间的关系

  • ContentProvider内容提供者,用于对外提供数据,提供数据的增删改查操作,数据源可以是数据库、文件、XML、网络等,ContentProvider为这些数据的访问提供了统一的接口,可以用来做进程间数据共享。
  • ContentResolver内容解析者,用于获取内容提供者提供的数据。ContentResolver可以不同URI操作不同的ContentProvider中的数据,外部进程可以通过ContentResolver与ContentProvider进行交互。
  • ContentObserver内容监听器,可以监听数据的改变状态,当ContentProvider数据源发生改变时,如果想通知其监听对象, 例如ContentObserver时,就必须在其调用对应方法 update / insert / delete 时,显式地调用 this.getContentReslover().notifychange(uri,null) 方法,回调监听处理逻辑。

5.为什么要用 ContentProvider?它和 sql 的实现上有什么差别?

  • ContentProvider 屏蔽了数据存储的细节,内部实现对用户完全透明,用户只需要关心操作数据的
    uri 就可以了,ContentProvider 可以实现不同 app 之间共享。

  • Sql 也有增删改查的方法,但是 sql 只能操作本应用下的数据库。而 ContentProvider 还可
    以去增删改查本地文件. xml 文件的读取等。

6.多个进程同时调用一个ContentProvider的query获取数据,ContentPrvoider是如何反应的呢?

尽管ContentResolver与ContentProvider类隐藏了实现细节,ContentProvider除了onCreate()是在UI线程运行,其余所提供的query(),insert(),delete(),update()都是在ContentProvider进程的线程池中被调用执行的,而不是进程的主线程中。因为那些方法可能同时被多个线程所调用,所以他们都应该是线程安全的。这个线程池是由Binder创建和维护的,其实使用的就是每个应用进程中的Binder线程池。

7.你觉得Android设计ContentProvider的目的是什么呢?

  1. 隐藏数据的实现方式,对外提供统一的数据访问接口;
  2. 更好的数据访问权限管理。ContentProvider可以对开发的数据进行权限设置,不同的URI可以对应不同的权限,只有符合权限要求的组件才能访问到ContentProvider的具体操作。
  3. ContentProvider封装了跨进程共享的逻辑,我们只需要Uri即可访问数据。由系统来管理ContentProvider的创建、生命周期及访问的线程分配,简化我们在应用间共享数据(进程间通信)的方式。我们只管通过ContentResolver访问ContentProvider所提示的数据接口,而不需要担心它所在进程是启动还是未启动。

8.运行在主线程的ContentProvider为什么不会影响主线程的UI操作?

  • ContentProvider的onCreate()是运行在UI线程的,而query(),insert(),delete(),update()是运行在线程池中的工作线程的,所以调用这几个方法并不会阻塞ContentProvider所在进程的主线程,但可能会阻塞调用者所在的进程的UI线程!
  • 所以,调用ContentProvider的操作仍然要放在子线程中去做。虽然直接的CRUD的操作是在工作线程的,但系统会让你的调用线程等待这个异步的操作完成,你才可以继续线程之前的工作。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  • Nginx Nginx是一个高性能的HTTP和反向代理服务器,也是一个 IMAP/POP3/SMTP代理服务器,以...
    fcharming阅读 597评论 0 0
  • 这个世界容许了太多自做多情,有人被女人斜看眼睛瞟了一眼,就觉得自己是万人迷。《我是猫》 作家 夏目漱石●●● 为什...
    健公堂生活有道阅读 498评论 0 1
  • 感赏女儿今天按照昨晚的约定时间提早出门,早晨顺利地起床、洗刷、吃饭、出门女人谢谢你给妈妈的支持,女儿的鼻炎在我这几...
    爱娃林丽华aw阅读 186评论 0 0
  • 2018年做了一个新的启程,就是参加了HRPC的一人一门成名课的培训。3天的培训,让我受益颇多。 曾经的我...
    娇娇O_f36b阅读 205评论 0 4
  • 2016年11月19日 阴转晴 早上起床想像往常一样窝在床上,因为实在太喜欢摊在那儿了,可是儿子的身上抓了很多包,...
    小翻抖阅读 269评论 0 0