Android基础知识——ContentProvider

本文参考了https://blog.csdn.net/qq_35578940/article/details/52445170 在此感谢作者

内容提供者ContentProvider

? ? ? 在Android中如果想将自己应用的数据(多为数据库中的数据)提供给第三方应用,那么我们只能通过ContentProvider来实现了。

? ContentProvider是应用程序之间共享数据的接口,使用的时候首先自定义一个类继承ContentProvider,然后重写query,insert,updata,delete等方法。

ContentProvider?封装了数据的跨进程传输,我们可以直接使用?getContentResolver()?拿到?ContentResolver?进行增删改查即可。

ContentProvider?以一个或多个表(与在关系型数据库中的表类似)的形式将数据呈现给外部应用。 行表示提供程序收集的某种数据类型的实例,行中的每个列表示为实例收集的每条数据。

实现一个?ContentProvider?时需要实现以下几个方法:

onCreate():初始化 provider

query():查询数据

insert():插入数据到 provider

update():更新 provider 的数据

delete():删除 provider 中的数据

getType():返回 provider 中的数据的 MIME 类型

注意:?

1.?onCreate()?默认执行在主线程,别做耗时操作,query()?也最好异步执行?

2. 上面的 4 个增删改查操作都可能会被多个线程并发访问,因此需要注意线程安全

ContentProvider 与 URI

ContentProvider?使用 URI 标识要操作的数据,这里的内容 URI 主要包括两部分:

authority:整个提供程序的符号名称

path:指向表的名称/路径

内容 URI 统一的形式就是:

content://authority/path

1

例如:

content://user_dictionary/words

1

当你调用?ContentResolver?方法来访问?ContentProvider?中的表时,需要传递要操作表的 URI。

在通过?ContentResolver?进行数据请求时(比如?contentResolver.insert(uri, contentValues);), 系统会检查指定 URI 的 authority 信息,然后将请求传递给注册监听这个 authority 的?ContentProvider?。这个?ContentProvider?可以监听 URI 想要操作的内容,Android 中为我们提供了?UriMatcher?来解析 URI。

因为内容提供者是四大组件之一,因此必须在AndroidMainfest文件中进行注册。

AndroidMainfest中注册:

<provider?android:name=".StudentProvider"3 android:authorities="com.example.contentproviderdemo.StudentProvider">? </provider?>

下面通过一个示例来讲解一下ContentProvider,在这个例子中,需要用到SQLite数据库来存储数据,定义了一个StudentDAO类,用于进行对SQLite的CRUD操作,这里就不提供数据访问的源码了,有兴趣的朋友可以在下载源码查看:

  ContentProvider实现:

package com.example.contentproviderdemo;

import com.example.dao.StudentDAO;

import android.content.ContentProvider;

import android.content.ContentUris;

import android.content.ContentValues;

import android.content.UriMatcher;

import android.database.Cursor;

import android.net.Uri;

import android.os.Bundle;

import android.util.Log;

public class StudentProvider extends ContentProvider {

? ? private final String TAG = "main";

? ? private StudentDAO studentDao = null;

? ? private static final UriMatcher URI_MATCHER = new UriMatcher(

? ? ? ? ? ? UriMatcher.NO_MATCH);

? ? private static final int STUDENT = 1;

? ? private static final int STUDENTS = 2;

? ? static {

? ? ? ? //添加两个URI筛选

? ? ? ? URI_MATCHER.addURI("com.example.contentproviderdemo.StudentProvider",

? ? ? ? ? ? ? ? "student", STUDENTS);

? ? ? ? //使用通配符#,匹配任意数字

? ? ? ? URI_MATCHER.addURI("com.example.contentproviderdemo.StudentProvider",

? ? ? ? ? ? ? ? "student/#", STUDENT);? ? ? ?

? ? }

? ? public StudentProvider() {

? ? }? ?


? ? @Override

? ? public boolean onCreate() {

? ? ? ? // 初始化一个数据持久层

? ? ? ? studentDao = new StudentDAO(getContext());

? ? ? ? Log.i(TAG, "---->>onCreate()被调用");

? ? ? ? return true;

? ? }

? ? @Override

? ? public Uri insert(Uri uri, ContentValues values) {

? ? ? ? Uri resultUri = null;

? ? ? ? //解析Uri,返回Code

? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? if (flag == STUDENTS) {

? ? ? ? ? ? long id = studentDao.insertStudent(values);

? ? ? ? ? ? Log.i(TAG, "---->>插入成功, id="+id);

? ? ? ? ? ? resultUri = ContentUris.withAppendedId(uri, id);

? ? ? ? }

? ? ? ? return resultUri;

? ? }

? ? @Override

? ? public int delete(Uri uri, String selection, String[] selectionArgs) {

? ? ? ? int count = -1;

? ? ? ? try {

? ? ? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? ? ? switch (flag) {

? ? ? ? ? ? case STUDENT:

? ? ? ? ? ? ? ? // delete from student where id=?

? ? ? ? ? ? ? ? //单条数据,使用ContentUris工具类解析出结尾的Id

? ? ? ? ? ? ? ? long id = ContentUris.parseId(uri);

? ? ? ? ? ? ? ? String where_value = "id = ?";

? ? ? ? ? ? ? ? String[] args = { String.valueOf(id) };

? ? ? ? ? ? ? ? count = studentDao.deleteStudent(where_value, args);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case STUDENTS:

? ? ? ? ? ? ? ? count = studentDao.deleteStudent(selection, selectionArgs);? ? ? ? ? ? ? ?

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? Log.i(TAG, "---->>删除成功,count="+count);

? ? ? ? return count;

? ? }

? ? @Override

? ? public int update(Uri uri, ContentValues values, String selection,

? ? ? ? ? ? String[] selectionArgs) {

? ? ? ? int count = -1;

? ? ? ? try {? ? ? ? ? ?

? ? ? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? ? ? switch (flag) {

? ? ? ? ? ? case STUDENT:

? ? ? ? ? ? ? ? long id = ContentUris.parseId(uri);

? ? ? ? ? ? ? ? String where_value = " id = ?";

? ? ? ? ? ? ? ? String[] args = { String.valueOf(id) };

? ? ? ? ? ? ? ? count = studentDao.updateStudent(values, where_value, args);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case STUDENTS:

? ? ? ? ? ? ? ? count = studentDao.updateStudent(values, selection,

? ? ? ? ? ? ? ? ? ? ? ? selectionArgs);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? Log.i(TAG, "---->>更新成功,count="+count);

? ? ? ? return count;

? ? }

? ? @Override

? ? public Cursor query(Uri uri, String[] projection, String selection,

? ? ? ? ? ? String[] selectionArgs, String sortOrder) {

? ? ? ? Cursor cursor = null;

? ? ? ? try {

? ? ? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? ? ? switch (flag) {

? ? ? ? ? ? case STUDENT:

? ? ? ? ? ? ? ? long id = ContentUris.parseId(uri);

? ? ? ? ? ? ? ? String where_value = " id = ?";

? ? ? ? ? ? ? ? String[] args = { String.valueOf(id) };

? ? ? ? ? ? ? ? cursor = studentDao.queryStudents(where_value, args);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case STUDENTS:

? ? ? ? ? ? ? ? cursor = studentDao.queryStudents(selection, selectionArgs);

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? }

? ? ? ? } catch (Exception e) {

? ? ? ? ? ? e.printStackTrace();

? ? ? ? }

? ? ? ? Log.i(TAG, "---->>查询成功,Count="+cursor.getCount());

? ? ? ? return cursor;

? ? }

? ? @Override

? ? public String getType(Uri uri) {

? ? ? ? int flag = URI_MATCHER.match(uri);

? ? ? ? String type = null;

? ? ? ? switch (flag) {

? ? ? ? case STUDENT:

? ? ? ? ? ? type = "vnd.android.cursor.item/student";

? ? ? ? ? ? Log.i(TAG, "----->>getType return item");

? ? ? ? ? ? break;

? ? ? ? case STUDENTS:

? ? ? ? ? ? type = "vnd.android.cursor.dir/students";

? ? ? ? ? ? Log.i(TAG, "----->>getType return dir");

? ? ? ? ? ? break;

? ? ? ? }

? ? ? ? return type;

? ? }

? ? @Override

? ? public Bundle call(String method, String arg, Bundle extras) {

? ? ? ? Log.i(TAG, "------>>"+method);

? ? ? ? Bundle bundle=new Bundle();

? ? ? ? bundle.putString("returnCall", "call被执行了");

? ? ? ? return bundle;

? ? }

}

ContentProvider可以理解为一个Android应用对外开放的接口,只要是符合它所定义的Uri格式的请求,均可以正常访问执行操作。其他的Android应用可以使用ContentResolver对象通过与ContentProvider同名的方法请求执行,被执行的就是ContentProvider中的同名方法。所以ContentProvider很多对外可以访问的方法,在ContentResolver中均有同名的方法,是一一对应的

Uri

  在Android中,Uri是一种比较常见的资源访问方式。而对于ContentProvider而言,Uri也是有固定格式的:

 <srandard_prefix>   ://<authority>/<data_path>/<id>

<srandard_prefix>:ContentProvider的srandard_prefix始终是content://。

<authority>:ContentProvider的名称。

<data_path>:请求的数据类型。

<id>:指定请求的特定数据。


ContentProvider

ContentProvider也是Android应用的四大组件之一,所以也需要在AndroidManifest.xml文件中进行配置。而且某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过这个接口来操作它的内部数据。

  Android附带了许多有用的ContentProvider,但是本篇博客不会涉及到这些内容的,以后有时间会再讲解。Android附带的ContentProvider包括:

Browser:存储如浏览器的信息。

CallLog:存储通话记录等信息。

Contacts:存储联系人等信息。

MediaStore:存储媒体文件的信息。

Settings:存储设备的设置和首选项信息。

  在Android中,如果要创建自己的内容提供者的时候,需要扩展抽象类ContentProvider,并重写其中定义的各种方法。然后在AndroidManifest.xml文件中注册该ContentProvider即可。

  ContentProvider是内容提供者,实现Android应用之间的数据交互,对于数据操作,无非也就是CRUD而已。下面是ContentProvider必须要实现的几个方法:

onCreate():初始化提供者。

query(Uri, String[], String, String[], String):查询数据,返回一个数据Cursor对象。

insert(Uri, ContentValues):插入一条数据。

update(Uri, ContentValues, String, String[]):根据条件更新数据。

delete(Uri, String, String[]):根据条件删除数据。

getType(Uri) 返回MIME类型对应内容的URI。

除了onCreate()和getType()方法外,其他的均为CRUD操作,这些方法中,Uri参数为与ContentProvider匹配的请求Uri,剩下的参数可以参见SQLite的CRUD操作,基本一致,SQLite的内容在另外一篇博客中有讲解:Android--数据持久化之SQLite。、

  Tips:还有两个非常有意思的方法,必须要提一下,call()和bulkInsert()方法,使用call,理论上可以在ContentResolver中执行ContentProvider暴露出来的任何方法,而bulkInsert()方法用于插入多条数据。


  在ContentProvider的CRUD操作,均会传递一个Uri对象,通过这个对象来匹配对应的请求。那么如何确定一个Uri执行哪项操作呢?需要用到一个UriMatcher对象,这个对象用来帮助内容提供者匹配Uri。它所提供的方法非常简单,仅有两个:

void addURI(String authority,String path,int code):添加一个Uri匹配项,authority为AndroidManifest.xml中注册的ContentProvider中的authority属性;path为一个路径,可以设置通配符,#表示任意数字,*表示任意字符;code为自定义的一个Uri代码。

int match(Uri uri):匹配传递的Uri,返回addURI()传递的code参数。


  在创建好一个ContentProvider之后,还需要在AndroidManifest.xml文件中对ContentProvider进行配置,使用一个节点,一般只需要设置两个属性即可访问,一些额外的属性就是为了设置访问权限而存在的,后面会详细讲解:

android:name:provider的响应类。

android:authorities:Provider的唯一标识,用于Uri匹配,一般为ContentProvider类的全名。

getType()中的MIME

  MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型。在ContentProvider中的getType方法,返回的就是一个MIME类型的字符串。如果支持需要使用ContentProvider来访问数据,就上面这个Demo,getType()完全可以只返回一个Null,并不影响效果,但是覆盖ContentProvider的getType方法对于用new Intent(String action, Uri uri)方法启动activity是很重要的,如果它返回的MIME type和activity在中定义的data的MIME type不一致,将造成activity无法启动。这就涉及到Intent和Intent-filter的内容了,以后有机会再说,这里不再详解。

  从官方文档了解到,getType返回的字符串,如果URI针对的是单条数据,则返回的字符串以vnd.android.cursor.item/开头;如果是多条数据,则以vnd.adroid.cursor.dir/开头。

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容