http://www.jianshu.com/p/c3ce81b638bd

一、基础回顾。

  • 简介
    ContentProvider(数据提供者)是在应用程序间共享数据的一种接口机制,虽然我们可以采用文件存储方式、sharedpreferences方式在程序间进行共享数据,但ContentProvider提供了更为高级的数据共享方法,应用程序可以指定需要共享的数据,而其他应用程序则可以在不知数据来源、路径的情况下,对共享数据进行查询、添加、删除和更新等操作,当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。

    ContentProvider

  • 特点
    1、为存储和获取数据提供了统一的接口。
    2、可以在不同的应用程序之间共享数据。
    3、使用数据库表的形式来组织数据进行封装。数据库使用:Android实习生 —— 数据存储与共享
    4、为应用间的数据交互提供了一个安全的环境。它准许你把自己的应用数据根据需求开放给其他应用进行增、删、改、查,而不用担心直接开放数据库权限而带来的安全问题。
    【总的来说使用ContentProvider对外共享数据的好处是统一了数据的安全访问方式。】

  • 使用场景
    Android已经为常见的一些数据提供了系统默认的ContentProvider,比如去获取通讯录信息、获取图片、视频信息。我们可以在其他应用程通过提供的ContentProvider获取这些数据。

二、相关概念

1、Uri

通用资源标志符(Universal Resource Identifier, 简称"URI")。

Uri代表了要操作的数据,它为系统的每一个资源给其一个名字,比方说通话记录。每一个ContentProvider都拥有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。

Uri

URI一般主要由三部分组成:
Authority:授权信息,用以区别不同的ContentProvider,外部调用者可以根据这个标识来找到它。为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称。

  1. <provider android:name=".MyProvider"
  2. android:authorities="com.xxx.MyApp.myprovider" />

Path:表名,用以区分ContentProvider中不同的数据表;
Id:Id号,用以区别表中的不同数据记录;如果没有ID,就表示返回全部; "content://com.xxx.MyApp.myprovider/tablename/#" #表示数据id。

【举例】
1、要操作person表中id为10的记录,可以构建这样的路径:/person/10
2、要操作person表中id为10的记录的name字段, person/10/name
3、要操作person表中的所有记录,可以构建这样的路径:/person
4、如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:Uri uri = Uri.parse("content://com.xxx.MyApp.myprovider/person")

【每个ContentProvider都有一个公共的URI,这个URI用于表示这个ContentProvider所提供的数据。Android所提供的ContentProvider都存放在android.provider包当中】


2、工具类:UriMatcher

因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher和ContentUris 。掌握它们的使用,会便于我们的开发工作。

使用方法如下:

第一步,初始化:

  1. //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
  2. UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

第二步,注册需要的Uri:

  1. //如果match()方法匹配content://com.xxx.MyApp.myprovider/person路径,返回匹配码为1
  2. matcher.addURI("com.xxx.MyApp.myprovider", "person", );
  3. //如果match()方法匹配content://com.xxx.MyApp.myprovider/person/230路径,返回匹配码为2
  4. matcher.addURI("com.xxx.MyApp.myprovider", "person/#", ); //#号为通配符

第三步,与已经注册的Uri进行匹配:

  1. Uri uri = Uri.parse("content://com.xxx.MyApp.myprovider/people");
  2. int match = matcher.match(uri);
  3. switch (match)
  4. {
  5. case :
  6. break;
  7. case :
  8. break;
  9. default:
  10. break;
  11. }

match方法匹配后会返回一个匹配码Code,即在使用注册方法addURI时传入的第三个参数。

3、工具类ContentUris:

ContentUris类用于操作Uri路径后面的ID部分,它有两个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:

  1. Uri uri = Uri.parse("content://com.xxx.MyApp.myprovider/person")
  2. Uri resultUri = ContentUris.withAppendedId(uri, );
  3. //生成后的Uri为:content://com.xxx.MyApp.myprovider/person/10

parseId(uri)方法用于从路径中获取ID部分:

  1. Uri uri = Uri.parse("content://com.xxx.MyApp.myprovider/person/10")
  2. long personid = ContentUris.parseId(uri);//获取的结果为:10

三、使用ContentProvider共享数据(Demo,下载链接见附录)。

1、首先建立一个Provider所用到变量类,方便Demo调用

  1. public class ContentData {
  2.  
  3. //provider唯一标示信息
  4. protected static final String CONTENT_AUTHORITY = "com.xxx.MyApp.myprovider";
  5.  
  6. //基础Uri
  7. protected static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
  8.  
  9. //操作表的名称
  10. protected static final String PATH_TEST = "people";
  11.  
  12. //表中记录信息
  13. public static final class TestEntry implements BaseColumns {
  14.  
  15. // 完整Uri
  16. public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(PATH_TEST).build();
  17.  
  18. protected static Uri buildUri(long id) {
  19. return ContentUris.withAppendedId(CONTENT_URI, id);
  20. }
  21.  
  22. protected static final String TABLE_NAME = "people";
  23.  
  24. public static final String COLUMN_NAME = "name";
  25.  
  26. public static final String COLUMN_SEX = "sex";
  27.  
  28. public static final String COLUMN_AGE = "age";
  29. }
  30. }
  31. Provider最终还要操作数据库,这里我们写数据库操作类代码。数据库使用:Android实习生 —— 数据存储共享(近期更新)
  32.  
  33. public class DBOpenHelper extends SQLiteOpenHelper {
  34.  
  35. //数据库版本
  36. private static final int DATABASE_VERSION = ;
  37. //数据库名称
  38. private static final String DATABASE_NAME = "people.db";
  39.  
  40. //构造方法
  41. public DBOpenHelper(Context context) {
  42. super(context, DATABASE_NAME,null, DATABASE_VERSION);
  43. }
  44.  
  45. //通过sql语句建表并插入数据
  46. @Override
  47. public void onCreate(SQLiteDatabase db) {
  48. System.out.println("create table");
  49.  
  50. final String SQL_CREATE_CONTACT_TABLE = "CREATE TABLE " + ContentData.TestEntry.TABLE_NAME + "( "
  51. + "_id integer primary key autoincrement,"
  52. + ContentData.TestEntry.COLUMN_NAME + " TEXT NOT NULL,"
  53. + ContentData.TestEntry.COLUMN_SEX + " TEXT NOT NULL,"
  54. + ContentData.TestEntry.COLUMN_AGE + " INTEGER NOT NULL );";
  55. db.execSQL(SQL_CREATE_CONTACT_TABLE);
  56.  
  57. db.execSQL("insert into people(name,sex,age)values('张三','女',18)");
  58. db.execSQL("insert into people(name,sex,age)values('张四','男',20)");
  59. db.execSQL("insert into people(name,sex,age)values('张五','女',19)");
  60. db.execSQL("insert into people(name,sex,age)values('张六','男',22)");
  61. }
  62.  
  63. //数据库升级的时候会调用的代码
  64. @Override
  65. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  66. db.execSQL("DROP TABLE IF EXISTS " + ContentData.TestEntry.TABLE_NAME);
  67. onCreate(db);
  68.  
  69. }
  70.  
  71. }

3、首先我们创建一个自己的MyProvider继承ContentProvider。默认该Provider需要实现如下六个方法

  1. public class MyProvider extends ContentProvider {
  2.  
  3. private DBOpenHelper dbOpenHelper;//声明数据库操作类
  4.  
  5. private final static int TEST = ;//匹配码
  6.  
  7. //使用UriMatcher解析Uri,如果被匹配到,返回匹配码100
  8. static UriMatcher buildUriMatcher() {
  9. final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
  10. final String authority = ContentData.CONTENT_AUTHORITY;
  11.  
  12. matcher.addURI(authority, ContentData.PATH_TEST, TEST);
  13.  
  14. return matcher;
  15. }
  16. @Override
  17. //该方法在ContentProvider被其它应用第一次访问它时才会被创建。
  18. //同时我们操作数据库建表
  19. public boolean onCreate() {
  20. dbOpenHelper = new DBOpenHelper(getContext());
  21. return true;
  22. }
  23.  
  24. @Override
  25. //该方法用于供外部应用往ContentProvider添加数据。
  26. public Uri insert(Uri uri, ContentValues contentValues) {
  27. //获得可写数据库
  28. final SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
  29. Uri returnUri;
  30. long _id;
  31. switch ( buildUriMatcher().match(uri)) {
  32. case TEST:
  33. //插入数据
  34. _id = db.insert(ContentData.TestEntry.TABLE_NAME, null, values);
  35. if ( _id > )
  36. returnUri = ContentData.TestEntry.buildUri(_id);
  37. else
  38. throw new android.database.SQLException("Failed to insert row into " + uri);
  39. break;
  40. default:
  41. throw new android.database.SQLException("Unknown uri: " + uri);
  42. }
  43. return returnUri;
  44. }
  45.  
  46. @Override
  47. //该方法用于供外部应用从ContentProvider删除数据。
  48. public int delete(Uri uri, String s, String[] strings) {
  49. return ;
  50. }
  51.  
  52. @Override
  53. //该方法用于供外部应用从ContentProvider中获取数据。
  54. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
  55. //获得可读数据库
  56. final SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
  57. //查到的结果是游标类型。
  58. Cursor cursor = null;
  59. switch ( buildUriMatcher().match(uri)) {
  60. case TEST:
  61. cursor = db.query(ContentData.TestEntry.TABLE_NAME, projection, selection, selectionArgs, sortOrder, null, null);
  62. break;
  63. }
  64.  
  65. return cursor;
  66. }
  67.  
  68. @Override
  69. //该方法用于供外部应用从ContentProvider更新数据。
  70. public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
  71. return ;
  72. }
  73.  
  74. @Override
  75. public String getType(Uri uri) {
  76. return null;
  77. }
  78. //该方法用于返回当前Uri所代表数据的MIME类型。
  79.  
  80. //如果操作的数据属于集合类型,那么MIME类型字符串应以vnd.android.cursor.dir/开头,
  81. //例如:要得到所有person记录的Uri为content://com.xxx.MyApp.myprovider/person,
  82. //那么返回的MIME类型字符串应该为:"vnd.android.cursor.dir/person"。
  83.  
  84. //如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
  85. //例如:得到id为10的person记录,Uri为content://com.xxx.MyApp.myprovider/person/10,
  86. //那么返回的MIME类型字符串为:"vnd.android.cursor.item/person"。
  87.  
  88. }

【注意】以上代码并没有实现删除,更改功能

4、注册Provider

  1. //应用内访问
  2. <provider
  3. android:authorities="com.xxx.MyApp.myprovider"
  4. android:name=".MyProvider" />

在注册的时候需要注意几个属性:

  1. android:exported 设置此provider是否可以被其他应用使用。
  2. android:readPermission provider的读权限的标识
  3. android:writePermission provider的写权限标识
  4. android:permission provider读写权限标识

如何让其他应用也可以访问此应用中的数据呢,我们需要这么注册

  1. <provider
  2. android:authorities="com.xxx.MyApp.myprovider"
  3. android:name=".MyProvider"
  4. android:readPermission="com.xxx.READ"
  5. android:exported="true">
  6. </provider>

并且要在注册文件中声明一个permission

  1. <permission android:name="com.bb.READ" android:protectionLevel="normal"/>

【通过以上步骤,一个ContentProvider就造好了。】

四、使用ContentResolver调用ContentProvider去操作数据库数据。

  • 为什么我们不直接访问Provider,而是又在上面加了一层ContentResolver来进行对其的操作。**

    大家要知道一台手机中可不是只有一个Provider内容,它可能安装了很多含有Provider的应用,比如联系人应用,日历应用,字典应用等等。有如此多的Provider,如果你开发一款应用要使用其中多个,如果让你去了解每个ContentProvider的不同实现,岂不是要头都大了。所以Android为我们提供了ContentResolver来统一管理与不同ContentProvider间的操作。

    ContentResolver
  • 实现过程

    实现数据操作的过程

    1、当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法通过URI进行数据操作。

    1. ContentResolver resolver = getContentResolver();

    2、ContentResolver 类提供了与ContentProvider类相同签名的四个方法:

    1. public Uri insert(Uri uri, ContentValues values):
    2. //该方法用于往ContentProvider添加数据。
    3.  
    4. public int delete(Uri uri, String selection, String[] selectionArgs):
    5. //该方法用于从ContentProvider删除数据。
    6.  
    7. public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):
    8. //该方法用于从ContentProvider中获取数据。
    9.  
    10. public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):
    11. //该方法用于更新ContentProvider中的数据。
    12. 使用ContentResolver对我们刚才造的ContentProvider中的数据进行添加、查询操作:
    13.  
    14. public class MainActivity extends AppCompatActivity {
    15. //声明Uri常量
    16. private static final Uri CONTENT_URI = Uri.parse("content://com.xxx.MyApp.myprovider/people");
    17. TextView tv_id, tv_name, tv_sex, tv_age;
    18.  
    19. @Override
    20. protected void onCreate(Bundle savedInstanceState) {
    21. super.onCreate(savedInstanceState);
    22. setContentView(R.layout.activity_main);
    23. tv_id = (TextView) findViewById(R.id.tv_id);
    24. tv_name = (TextView) findViewById(R.id.tv_name);
    25. tv_sex = (TextView) findViewById(R.id.tv_sex);
    26. tv_age = (TextView) findViewById(R.id.tv_age);
    27. }
    28.  
    29. //query()为在xml组建中定义的OnClick
    30. public void query(View view) {
    31. //通过getContentResolver().query调用ContentProvider实现对数据库的查询
    32. tv_id.setText("");
    33. tv_name.setText("");
    34. tv_sex.setText("");
    35. tv_age.setText("");
    36. Cursor cursor = getContentResolver().query(CONTENT_URI, new String[]{"_id", "name", "sex", "age"
    37. }, null, null, null);
    38. if (cursor != null) {
    39. while (cursor.moveToNext()) {
    40. tv_id.append("\n" + cursor.getString(cursor.getColumnIndex("_id")));
    41. tv_name.append("\n" + cursor.getString(cursor.getColumnIndex("name")));
    42. tv_sex.append("\n" + cursor.getString(cursor.getColumnIndex("sex")));
    43. tv_age.append("\n" + cursor.getString(cursor.getColumnIndex("age")));
    44. }
    45. cursor.close();
    46. }
    47. }
    48.  
    49. public void add(View view) {
    50. //通过getContentResolver().insert调用ContentProvider实现对数据库的增加
    51. ContentValues values = new ContentValues();
    52. values.put("name", "新来的");
    53. values.put("sex", "男");
    54. values.put("age", "");
    55. getContentResolver().insert(CONTENT_URI, values);
    56. query(findViewById(R.id.btn_query));
    57. }
    58. }

    【XML布局请下载Demo】
    3、在注册文件中记得增加权限读取权限

    1. <uses-permission android:name="com.xxx.READ"/>
    2. <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    4、最终效果

    点击查询

    点击增加

【附录】

Android四大组件-Content Provider的更多相关文章

  1. Android应用程序组件Content Provider简要介绍和学习计划

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6946067 在Android系统中,Conte ...

  2. Android应用程序组件Content Provider的共享数据更新通知机制分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6985171 在Android系统中,应用程序组 ...

  3. Android应用程序组件Content Provider在应用程序之间共享数据的原理分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6967204 在Android系统中,不同的应用 ...

  4. Android应用程序组件Content Provider的启动过程源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6963418 通过前面的学习,我们知道在Andr ...

  5. Android应用程序组件Content Provider应用实例

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6950440 文简要介绍了Android应用程序 ...

  6. android四大组件(简单总结)

    activity 一个Activity通常就是一个单独的屏幕(窗口) Activity之间通过Intent进行通信 android应用中每一个Activity都必须要在AndroidManifest. ...

  7. Android成长日记-Android四大组件之Service组件的学习

    1.什么是Service? Service是Android四大组件中与Activity最相似的组件,它们都代表可执行的程序,Service与Activity的区别在于:Service一直在后台运行,它 ...

  8. Android四大组件小结

    Android四大组件分别为activity.service.content provider.broadcast receiver. 一.android四大组件详解 1.activity (1)一个 ...

  9. 【Android 界面效果16】关于android四大组件的总结

    Android四大组件:Activity.Service.Broadcast receiver.Content provider 在Android中,一个应用程序可以使用其它应用程序的组件,这是And ...

随机推荐

  1. zabbix 监控安装

    注意:此篇是在安装好lnmp环境后才能部署的操作,所以,做之前准备好lnmp环境,或者可以参考我做的lnmp环境,之后接着此篇开始安装 监控系统Zabbix-3.2.1的安装 zabbix-serve ...

  2. odoo开发笔记:前端显示强制换行

    未调整之前:客户信息显示不全 调整后实现效果: 补充CSS知识: 一.强制换行 word-break: break-all; 只对英文起作用,以字母作为换行依据. word-wrap: break-w ...

  3. (转)Python3之requests模块

    原文:https://www.cnblogs.com/wang-yc/p/5623711.html Python标准库中提供了:urllib等模块以供Http请求,但是,它的 API 太渣了.它是为另 ...

  4. RandomStringUtils工具类

    //产生5位长度的随机字符串,中文环境下是乱码 RandomStringUtils.random(5); //使用指定的字符生成5位长度的随机字符串 RandomStringUtils.random( ...

  5. Android_view的生命周期

    onFinishInflate() 当View中所有的子控件均被映射成xml后触发 onMeasure( int , int ) 确定所有子元素的大小 onLayout( boolean , int ...

  6. Linux系统修改防火墙配置

    防火墙配置文件位置 /etc/sysconfig/iptables 需要开放端口,请在里面添加一条内容即可: 1 -A RH-Firewall-1-INPUT -m state --state NEW ...

  7. Maven 打包遇到的问题

    [ERROR] No compiler is provided in this environment. Perhaps you are running on a JRE rather than a ...

  8. 异常处理:net.sf.cglib.beans.BulkBeanException

    今天下午由于各种开会,断断续续写得代码,单元测试的时候,老是报如题的错误,后来查阅资料,发现原来是从数据库查询的值如果为空,则对应实体类执行set方法会赋值null给对应属性值,但是我当时的几个值偏偏 ...

  9. SOA和微服务架构的区别?

    转自知乎:https://www.zhihu.com/question/37808426/answer/93335393 SOA和微服务架构的区别? 微服务架构强调的第一个重点就是业务系统需要彻底的组 ...

  10. zoj 2744 Palindromes(计算回文子串个数的优化策略)

    题目链接: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2744 题目描述: A regular palindrome i ...