1.什么是内容提供者?

首先我们必须要明白的是ContentProvider(内容提供者)是android中的四大组件之一,但是在一般的开发中,可能使用比较少。ContentProvider为不同的软件之间数据共享,提供统一的接口。而且ContentProvider是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。至于如何从URI中识别出外界需要的是哪个“数据库”这就是Android底层需要做的事情了,也就是说,如果我们想让其他的应用使用我们自己程序内的数据,就可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用我们自己应用中的文件、数据库内存储的信息。当然,自己开发的应用需要给其他应用共享信息的需求可能比较少见,但是在Android系统中,很多数据如:联系人信息、短信信息、图片库、音频库等,这些信息在开发中还是经常用到的,这些信息谷歌工程师已经帮我们封装好了,我们可以使用谷歌给我的Uri去直接访问这些数据。所以对于ContentProvider我们还是需要认真的学习的,在遇到获取联系人信息,图片库,音视频库等需求的时候,才能更好的实现功能。

2.为什么会有内容提供者?

当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用sharedpreferences共享数据,需要使用sharedpreferences API读写数据。而使用ContentProvider共享数据的好处是统一了数据访问方式,这也是为什么会有内容提供者的原因。

3.怎么使用内容提供者?

在理解了什么是内容提供者,为什么会有内容提供者之后,我想在大家脑海中的浮现的一个问题就是怎么使用内容提供者,这也是今天我们要讨论的重点内容。在前面的博客我也说到学习这种东西的最好方法是看谷歌给出的官方文档,那么好我们先来翻译一段谷歌给出的介绍(注:这是本地的文档,我采用的是脱机工作地址(file:///D:/adt-bundle-windows-x86_64_20140101/sdk/docs/reference/android/content/ContentProvider.html))。

翻译:

内容提供者是android应用程序的基本构建块之一,它们封装数据并将封装的数据通过单一的ContentResolver接口提供给应用程序。当你需要在多个应用之间共享数据的时候就需要用到内容提供者。例如,手机中的联系人数据会被多个应用所用到所以必须要用内容提供者存储起来。如果你不需要在多个应用之间共享数据,你可以使用一个数据库,直接通过SQLite数据库。 当通过content resolver发送一个请求时,系统会检查给定的URI并把请求传给有注册授权的Contentprovider。 UriMatcher类有助于解析uri。

需要实现的主要方法是:

public boolean onCreate() 在创建ContentProvider时调用

public Cursor query(Uri, String[], String, String[], String) 用于查询指定Uri的ContentProvider,返回一个Cursor

public Uri insert(Uri, ContentValues) 用于添加数据到指定Uri的ContentProvider中,(外部应用向ContentProvider中添加数据)

public int update(Uri, ContentValues, String, String[]) 用于更新指定Uri的ContentProvider中的数据

public int delete(Uri, String, String[]) 用于从指定Uri的ContentProvider中删除数据

public String getType(Uri) 用于返回指定的Uri中的数据的MIME类型

数据访问的方法(如:insert(Uri, ContentValues) and update(Uri, ContentValues, String, String[]))可能被多个线程同时调用,此时必须是线程安全的。其他方法(如: onCreate())只能被应用的主线程调用,它应当避免冗长的操作。ContentResolver(内容解析者)请求被自动转发到合适的内容提供者实例,所以子类不需要担心跨进程调用的细节。

唉,每次最头疼的就是看全英文的文档,发现自己的英语水平太差,最近也在空闲时间学习学习英语,但是对于英语从来就没感兴趣过,希望自己可以坚持一段时间吧,其实在开发过程中遇到那些比较难解决的问题,在国外的有名的网站中基本都是可以查找到的,但是对于我这样一个英语水平差的人来说,说多了其实都是眼泪。。。,我先哭一会。。。

关于怎样使用ContentProvider后面有实例帮助大家理解。

4.Uri详解

在上面的翻译中如果你认真看的话你会发现在谷歌的官方文档中提到了ContentResolver(内容解析者),外界可以通过ContentResolver接口来访问ContentProvider(内容提供者)中的数据。但是在详细了解ContentResolver之前有一项工作是必须要做的,那就是先理解Uri,在谷歌文档中也有介绍,接下来我们就来详细的学习下Uri这个类

Uri 通用资源标志符(Universal Resource Identifier)Uri代表要操作的数据,Android中可用的每种资源 - 图像、视频片段等都可以用Uri来表示。Uri的结构由以下几个部分组成

scheme、authority、path、query和fragment组成。其中authority又分为host和port。它的格式根据划分的详细程度可以分为三种

如下:

[scheme:][scheme-specific-part][#fragment]

[scheme:][//authority][path][?query][#fragment]

[scheme:][//host:port][path][?query][#fragment]——最详细的划分形式

看到这里肯定有人糊里糊涂的,接着我们就来举一个例子来帮助大家详细的理解Uri这个类的结构

假如有这么一个Uri:http://www.baidu.com:8080/yourpath/fileName.html?id=15&name=du#dmk

你能将上述Uri进行提取吗?接着我们就比着标准的格式[scheme:][//host:port][path][?query][#fragment]来将这个Uri各个部分提取出来

scheme:根据标准格式可以看出这里的scheme就是Uri前面//前面的部分这里也就是http:。

fragment:dmk这个也是比较容易找到的,在#后面

query:id=15&name=du#dmk。从标准格式可以看到在"#"之前"?"之后的部分是query,在这里当然就是id=15&name=du#dmk了。

authority:从格式二中可以看到authority是在//后的部分,它的终点就是在path之前所以这里的authority就是www.baidu.com:8080

path:path就是?之前,主机之后的部分那就是yourpath/fileName.html

host和port:因为主机可以分为host和port所以这里的host和port分别为:www.baidu.com和8080

这里要提醒大家注意的是:在Uri中并不是上述所有的字段都必须有的除了scheme、authority是必须要有的,其它的几个path、query、fragment,它们每一个可以选择性的要或不要,但顺序不能变,比方说在上述Uri中没有path那它的格式就为:http://www.baidu.com:8080/?id=15&name=du#dmk。

在理解了Uri的格式之后,有的人可能会说Uri的各个字段能否用代码获取?答案是肯定的

这里我们同样以http://www.baidu.com:8080/yourpath/fileName.html?id=15&name=du#dmk为例

  • getScheme() :获取Uri中的scheme字符串部分,在这里是http
  • getSchemeSpecificPart():获取Uri中的scheme-specific-part:部分,这里是:http://www.baidu.com:8080/yourpath/fileName.html?
  • getFragment():获取Uri中的fragment部分,即dmk
  • getAuthority():获取Uri中Authority部分,即www.baidu.com:8080
  • getPath():获取Uri中path部分,即/yourpath/fileName.html
  • getQuery():获取Uri中的query部分,即id=15&name=du
  • getHost():获取Authority中的Host字符串,即www.baidu.com
  • getPost():获取Authority中的Port字符串,即8080

到这里关于Uri的介绍就完了(这里的关于Uri的介绍的内容主要来自:Uri详解之——Uri结构与代码提取 和 Java魔法堂:URI、URL(含URL Protocol Handler)

5.ContentResolver讲解

在了解了Uri之后就可以来学习学习ContentResolver了,前面我们说到ContentProvider共享数据是通过定义一个对外开放的统一的接口来实现的。然而,应用程序并不直接调用这些方法,而是使用一个 ContentResolver 对象,调用它的方法作为替代。ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。ContentResolver cr = getContentResolver();在上面我们提到ContentProvider可以向其他应用程序提供数据,与之对应的ContentResolver则负责获取ContentProvider提供的数据,修改、添加、删除更新数据等;

ContentResolver 类也提供了与ContentProvider类相对应的四个方法:

public Uri insert(Uri uri, ContentValues values)    该方法用于往ContentProvider添加数据。

public int delete(Uri uri, String selection, String[] selectionArgs)   该方法用于从ContentProvider删除数据。

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)   该方法用于更新ContentProvider中的数据。

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)   该方法用于从ContentProvider中获取数据。

这些方法的第一个参数为Uri,代表要操作的是哪个ContentProvider和对其中的什么数据进行操作,假设给定的是 Uri.parse(“content://com.qstingda.provider.personprovider/contact/15”),那么将会对主机名为com.qstingda.provider.personprovider的ContentProvider进行操作,path为contact/15的数据,看到这如果你之前没有接触过ContentProvider肯定一头雾水,没有关系,这很正常,等我们把理论知识讲完后会有实例,相信你看过实例后就会很明白了。

6.UriMatch

UriMatcher 类主要用于匹配Uri.这里的匹配是发生在ContentProvider中的,假如我们向ContentProvider中插入一条数据,不可能为所欲为的想怎么干就怎么干,在ContentProvider肯定要做一个判断,只有在符合条件下才会去执行你想要执行的操作,这里的判断就是用UriMatch进行匹配,假如是系统的ContentProvider如联系人、图库、视频库等,这些系统都提供了Uri我们可以根据系统提供的Uri来操作相应的数据。其实UriMatch的用法非常简单,查阅谷歌官方文档你会发现有这么几个方法

①publicUriMatcher(int code) 它的作用就是创建一个UriMatch对象

②public voidaddURI(String authority,String path, int code)

它的作用是在ContentProvider添加一个用于匹配的Uri,当匹配成功时返回code。Uri可以是精确的字符串,Uri中带有*表示可匹配任意text,#表示只能匹配数字。

③public int match(Uri uri) 这里的Uri就是传过来的要进行验证,匹配的Uri假如传过来的是:content://com.example.test/student/#,则content://com.example.test/student/10可以匹配成功,这里的10可以使任意的数字。

7.ContentObserver用法

ContentObserver——内容观察者,从其名字我们可以看出它的作用就是观察,观察什么?观察指定的Uri引起的数据库的变化,然后通知主线程,根据需求做我们想要做的处理。这样说大家可能理解的不是特别透彻,这样跟大家说它可以实现类似于Adapter的notifyDataSetChanged()这个方法的作用,比方说当观察到ContentProvider的数据变化时会自动调用谷歌工程师给我们提供的好的方法,可以在此方法中通知主线程数据改变等等。那么问题来了,应该怎样实现这样的功能呢?首先要做的就是注册这个观察者,这里的注册是在需要监测ContentProvider的应用中进行注册并不是在ContentProvider中而在ContentProvider中要做的就是当数据变化时进行通知,这里的通知的方法谷歌已经帮我们写好,直接调用就行了,查看谷歌文档你会发现在ContentResolver中有这样的介绍:

public final void registerContentObserver (Uri uri, boolean notifyForDescendents, ContentObserver observer)

注册一个观察者实例,当指定的Uri发生改变时,这个实例会回调实例对象做相应处理。

参数:uri:需要观察的Uri

notifyForDescendents:如果为true表示以这个Uri为开头的所有Uri都会被匹配到,

如果为false表示精确匹配,即只会匹配这个给定的Uri。

举个例子,假如有这么几个Uri:

①content://com.example.studentProvider/student

②content://com.example.studentProvider/student/#

③content://com.example.studentProvider/student/10

④content://com.example.studentProvider/student/teacher

假如观察的Uri为content://com.example.studentProvider/student,当notifyForDescendents为true时则以这个Uri开头的Uri的数据变化时都会被捕捉到,在这里也就是①②③④的Uri的数据的变化都能被捕捉到,当notifyForDescendents为false时则只有①中Uri变化时才能被捕捉到。

看到registerContentObserver 这个方法,根据语言基础我想大家能够想到ContentResolver中的另一个方法

public final voidunregisterContentObserver(ContentObserverobserver)它的作用就是取消对注册的那个Uri的观察,这里传进去的就是在registerContentObserver中传递进去的ContentObserver对象。到这关于注册和解除注册的ContentObserver可能大家都比较清楚了,那么问题来了,怎么去写一个ContentObserver呢?其实它的实现很简单,直接创建一个类继承ContentObserver需要注意的是这里必须要实现它的构造方法

public ContentObserver(Handlerhandler)

这里传进去的是一个Handler对象,这个Handler对象的作用一般要依赖于ContentObserver的另一个方法即

public void onChange(boolean selfChange)

这个方法的作用就是当指定的Uri的数据发生变化时会回调该方法,此时可以借助构造方法中的Handler对象将这个变化的消息发送给主线程,当主线程接收到这个消息之后就可以按照我们的需求来完成相应的操作,比如上面提到的类似于Adapter的notifyDataSetChanged()的作用,下面的案例也是完成了这个功能,准备工作完成之后来看一个案例,相信这个案例会让你对以上知识了解的更加深入。

8.案例(自定义ContentProvider)

在真正的开发中我们很少去自定义一个ContentProvider因为ContentProvider是为了更好的去共享数据,我们在开发中很少会遇到这种情况,而遇到更多的则是访问系统的ContentProvider,系统的ContentProvider谷歌工程师已经帮我们写好了,我们直接使用就可以了,这里为了让大家能够理解ContentProvider更加彻底,我们自定义一个ContentProvider然后在其它应用中来访问自定义的ContentProvider的数据这个案例的运行效果如下:

这里的插入数据,是在一个项目中向另一个项目中的ContentProvider中插入一条数据,其他的操作也是,接下来就来看看怎么实现上述的效果。

在上面我们提到在自定义ContentProvider时需要继承ContentProvider并实现3中所述的那几个方法(系统会自动帮你将要复写的方法罗列出来),那么我们自定义的PeopleContentProvider的代码如下

[java] view plain copy

  1. package com.example.contentproviderpractice;
  2. import android.content.ContentProvider;
  3. import android.content.ContentUris;
  4. import android.content.ContentValues;
  5. import android.content.UriMatcher;
  6. import android.database.Cursor;
  7. import android.database.sqlite.SQLiteDatabase;
  8. import android.net.Uri;
  9. public class PeopleContentProvider extends ContentProvider {
  10. //这里的AUTHORITY就是我们在AndroidManifest.xml中配置的authorities,这里的authorities可以随便写
  11. private static final String AUTHORITY = "com.example.studentProvider";
  12. //匹配成功后的匹配码
  13. private static final int MATCH_ALL_CODE = 100;
  14. private static final int MATCH_ONE_CODE = 101;
  15. private static UriMatcher uriMatcher;
  16. private SQLiteDatabase db;
  17. private DBOpenHelper openHelper;
  18. private Cursor cursor = null;
  19. //数据改变后指定通知的Uri
  20. private static final Uri NOTIFY_URI = Uri.parse("content://" + AUTHORITY + "/student");
  21. //在静态代码块中添加要匹配的 Uri
  22. static {
  23. //匹配不成功返回NO_MATCH(-1)
  24. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  25. /**
  26. * uriMatcher.addURI(authority, path, code); 其中
  27. * authority:主机名(用于唯一标示一个ContentProvider,这个需要和清单文件中的authorities属性相同)
  28. * path:路径路径(可以用来表示我们要操作的数据,路径的构建应根据业务而定)
  29. * code:返回值(用于匹配uri的时候,作为匹配成功的返回值)
  30. */
  31. uriMatcher.addURI(AUTHORITY, "student", MATCH_ALL_CODE);// 匹配记录集合
  32. uriMatcher.addURI(AUTHORITY, "student/#", MATCH_ONE_CODE);// 匹配单条记录
  33. }
  34. @Override
  35. public boolean onCreate() {
  36. openHelper = new DBOpenHelper(getContext());
  37. db = openHelper.getWritableDatabase();
  38. return false;
  39. }
  40. @Override
  41. public int delete(Uri uri, String selection, String[] selectionArgs) {
  42. switch (uriMatcher.match(uri)) {
  43. /**
  44. * 这里如果匹配是uriMatcher.addURI(AUTHORITY, "student",
  45. * MATCH_SUCCESS_CODE);中的Uri,则我们可以在这里对这个ContentProvider中的数据库
  46. * 进行删除等操作。这里如果匹配成功,我们将删除所有的数据
  47. */
  48. case MATCH_ALL_CODE:
  49. int count=db.delete("personData", nullnull);
  50. if(count>0){
  51. notifyDataChanged();
  52. return count;
  53. }
  54. break;
  55. /**
  56. * 这里如果匹配是uriMatcher.addURI(AUTHORITY,
  57. * "student/#",MATCH_ONE_CODE);中的Uri,则说明我们要操作单条记录
  58. */
  59. case MATCH_ONE_CODE:
  60. // 这里可以做删除单条数据的操作。
  61. break;
  62. default:
  63. throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
  64. }
  65. return 0;
  66. }
  67. @Override
  68. public String getType(Uri uri) {
  69. return null;
  70. }
  71. /**
  72. * 插入 使用UriMatch的实例中的match方法对传过来的 Uri进行匹配。 这里通过ContentResolver传过来一个Uri,
  73. * 用这个传过来的Uri跟在ContentProvider中静态代码块中uriMatcher.addURI加入的Uri进行匹配
  74. * 根据匹配的是否成功会返回相应的值,在上述静态代码块中调用uriMatcher.addURI(AUTHORITY,
  75. * "student",MATCH_CODE)这里的MATCH_CODE
  76. * 就是匹配成功的返回值,也就是说假如返回了MATCH_CODE就表示这个Uri匹配成功了
  77. * ,我们就可以按照我们的需求就行操作了,这里uriMatcher.addURI(AUTHORITY,
  78. * "person/data",MATCH_CODE)加入的Uri为:
  79. * content://com.example.studentProvider/student
  80. * ,如果传过来的Uri跟这个Uri能够匹配成功,就会按照我们设定的步骤去执行相应的操作
  81. */
  82. @Override
  83. public Uri insert(Uri uri, ContentValues values) {
  84. int match=uriMatcher.match(uri);
  85. if(match!=MATCH_ALL_CODE){
  86. throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
  87. }
  88. long rawId = db.insert("personData", null, values);
  89. Uri insertUri = ContentUris.withAppendedId(uri, rawId);
  90. if(rawId>0){
  91. notifyDataChanged();
  92. return insertUri;
  93. }
  94. return null;
  95. }
  96. /**
  97. * 查询 如果uri为
  98. * content://com.example.studentProvider/student则能匹配成功,然后我们可以按照需求执行匹配成功的操作
  99. */
  100. @Override
  101. public Cursor query(Uri uri, String[] projection, String selection,
  102. String[] selectionArgs, String sortOrder) {
  103. switch (uriMatcher.match(uri)) {
  104. /**
  105. * 如果匹配成功,就根据条件查询数据并将查询出的cursor返回
  106. */
  107. case MATCH_ALL_CODE:
  108. cursor = db.query("personData", nullnullnullnullnullnull);
  109. break;
  110. case MATCH_ONE_CODE:
  111. // 根据条件查询一条数据。。。。
  112. break;
  113. default:
  114. throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
  115. }
  116. return cursor;
  117. }
  118. @Override
  119. public int update(Uri uri, ContentValues values, String selection,
  120. String[] selectionArgs) {
  121. switch (uriMatcher.match(uri)) {
  122. case MATCH_ONE_CODE:
  123. long age = ContentUris.parseId(uri);
  124. selection = "age = ?";
  125. selectionArgs = new String[] { String.valueOf(age) };
  126. int count = db.update("personData", values, selection,selectionArgs);
  127. if(count>0){
  128. notifyDataChanged();
  129. }
  130. break;
  131. case MATCH_ALL_CODE:
  132. // 如果有需求的话,可以对整个表进行操作
  133. break;
  134. default:
  135. throw new IllegalArgumentException("Unkwon Uri:" + uri.toString());
  136. }
  137. return 0;
  138. }
  139. //通知指定URI数据已改变
  140. private void notifyDataChanged() {
  141. getContext().getContentResolver().notifyChange(NOTIFY_URI, null);
  142. }
  143. }

可以看到在onCreate()方法中创建了一个数据库,关于数据库的操作大家可以看此博客http://blog.csdn.net/dmk877/article/details/44876805。这里就不多做介绍了。注意这个案例牵扯到两个项目,一个是包含我们自定义的ContentProvider,另一个项目是访问这个包含ContentProvider项目中的数据。

写好PeopleContentProvider之后千万不要忘了在清单文件中注册

[java] view plain copy

  1. <provider
  2. android:name="com.example.contentproviderpractice.PeopleContentProvider"
  3. android:authorities="com.example.student"
  4. android:exported="true" >
  5. </provider>

这里的authorities就是它是唯一标识内容提供者的,为内容提供者指定一个唯一的标识,这样别的应用才可以唯一获取此provider,exported的值为[flase|true]当为true时:当前提供者可以被其它应用使用。任何应用可以使用Provider通过URI 来获得它,也可以通过相应的权限来使用Provider。当为false时:当前提供者不能被其它应用使用,默认为true。注册好之后运行到手机上,此时其它的应用就可以通过ContentResolver来访问这个PeopleContentProvider了,具体怎么操作呢?我们再新建一个项目在MainActivity的代码如下:

[java] view plain copy

  1. package com.example.otherapplication;
  2. import java.util.ArrayList;
  3. import com.example.otherapplication.adapter.MyAdapter;
  4. import com.example.otherapplication.bean.Student;
  5. import com.example.otherapplication.observer.PersonOberserver;
  6. import android.net.Uri;
  7. import android.os.Bundle;
  8. import android.os.Handler;
  9. import android.app.Activity;
  10. import android.content.ContentResolver;
  11. import android.content.ContentUris;
  12. import android.content.ContentValues;
  13. import android.database.Cursor;
  14. import android.view.View;
  15. import android.view.View.OnClickListener;
  16. import android.widget.Button;
  17. import android.widget.ListView;
  18. public class MainActivity extends Activity implements OnClickListener {
  19. private ContentResolver contentResolver;
  20. private ListView lvShowInfo;
  21. private MyAdapter adapter;
  22. private Button btnInit;
  23. private Button btnInsert;
  24. private Button btnDelete;
  25. private Button btnUpdate;
  26. private Button btnQuery;
  27. private Cursor cursor;
  28. private static final String AUTHORITY = "com.example.studentProvider";
  29. private static final Uri STUDENT_ALL_URI = Uri.parse("content://" + AUTHORITY + "/student");
  30. protected static final String TAG = "MainActivity";
  31. private Handler handler=new Handler(){
  32. public void handleMessage(android.os.Message msg) {
  33. //在此我们可以针对数据改变后做一些操作,比方说Adapter.notifyDataSetChanged()等,根据业务需求来定。。
  34. cursor = contentResolver.query(STUDENT_ALL_URI, nullnullnull,null);
  35. adapter.changeCursor(cursor);
  36. };
  37. };
  38. @Override
  39. protected void onCreate(Bundle savedInstanceState) {
  40. super.onCreate(savedInstanceState);
  41. setContentView(R.layout.activity_main);
  42. lvShowInfo=(ListView) findViewById(R.id.lv_show_info);
  43. initData();
  44. }
  45. private void initData() {
  46. btnInit=(Button) findViewById(R.id.btn_init);
  47. btnInsert=(Button) findViewById(R.id.btn_insert);
  48. btnDelete=(Button) findViewById(R.id.btn_delete);
  49. btnUpdate=(Button) findViewById(R.id.btn_update);
  50. btnQuery=(Button) findViewById(R.id.btn_query);
  51. btnInit.setOnClickListener(this);
  52. btnInsert.setOnClickListener(this);
  53. btnDelete.setOnClickListener(this);
  54. btnUpdate.setOnClickListener(this);
  55. btnQuery.setOnClickListener(this);
  56. contentResolver = getContentResolver();
  57. //注册内容观察者
  58. contentResolver.registerContentObserver(STUDENT_ALL_URI,true,new PersonOberserver(handler));
  59. adapter=new MyAdapter(MainActivity.this,cursor);
  60. lvShowInfo.setAdapter(adapter);
  61. }
  62. @Override
  63. public void onClick(View v) {
  64. switch (v.getId()) {
  65. //初始化
  66. case R.id.btn_init:
  67. ArrayList<Student> students = new ArrayList<Student>();
  68. Student student1 = new Student("苍老师",25,"一个会教学的好老师");
  69. Student student2 = new Student("柳岩",26,"大方");
  70. Student student3 = new Student("杨幂",27,"漂亮");
  71. Student student4 = new Student("张馨予",28,"不知道怎么评价");
  72. Student student5 = new Student("范冰冰",29,"。。。");
  73. students.add(student1);
  74. students.add(student2);
  75. students.add(student3);
  76. students.add(student4);
  77. students.add(student5);
  78. for (Student Student : students) {
  79. ContentValues values = new ContentValues();
  80. values.put("name", Student.getName());
  81. values.put("age", Student.getAge());
  82. values.put("introduce", Student.getIntroduce());
  83. contentResolver.insert(STUDENT_ALL_URI, values);
  84. }
  85. break;
  86. //增
  87. case R.id.btn_insert:
  88. Student student = new Student("小明", 26, "帅气男人");
  89. //实例化一个ContentValues对象
  90. ContentValues insertContentValues = new ContentValues();
  91. insertContentValues.put("name",student.getName());
  92. insertContentValues.put("age",student.getAge());
  93. insertContentValues.put("introduce",student.getIntroduce());
  94. //这里的uri和ContentValues对象经过一系列处理之后会传到ContentProvider中的insert方法中,
  95. //在我们自定义的ContentProvider中进行匹配操作
  96. contentResolver.insert(STUDENT_ALL_URI,insertContentValues);
  97. break;
  98. //删
  99. case R.id.btn_delete:
  100. //删除所有条目
  101. contentResolver.delete(STUDENT_ALL_URI, nullnull);
  102. //删除_id为1的记录
  103. Uri delUri = ContentUris.withAppendedId(STUDENT_ALL_URI,1);
  104. contentResolver.delete(delUri, nullnull);
  105. break;
  106. //改
  107. case R.id.btn_update:
  108. ContentValues contentValues = new ContentValues();
  109. contentValues.put("introduce","性感");
  110. //更新数据,将age=26的条目的introduce更新为"性感",原来age=26的introduce为"大方".
  111. //生成的Uri为:content://com.example.studentProvider/student/26
  112. Uri updateUri = ContentUris.withAppendedId(STUDENT_ALL_URI,26);
  113. contentResolver.update(updateUri,contentValues, nullnull);
  114. break;
  115. //查
  116. case R.id.btn_query:
  117. //通过ContentResolver获得一个调用ContentProvider对象
  118. Cursor cursor = contentResolver.query(STUDENT_ALL_URI, nullnullnull,null);
  119. //CursorAdapter的用法,参考此博客:http://blog.csdn.net/dmk877/article/details/44983491
  120. adapter=new MyAdapter(MainActivity.this,cursor);
  121. lvShowInfo.setAdapter(adapter);
  122. cursor = contentResolver.query(STUDENT_ALL_URI, nullnullnull,null);
  123. adapter.changeCursor(cursor);
  124. break;
  125. }
  126. }
  127. }

可以看出若想操作我们想操作的ContentProvider,必须要知道内容提供者的Uri,再正确得到Uri之后,就可以通过ContentResolver对象来操作ContentProvider中的数据了,假如你需要插入数据只需要调用contentResolver.insert(uri, contentValues);把正确的uri和ContentValues键值对传过去就行了。执行这句话系统就会根据我们提供的uri找到对应的ContentProvider,因为我们的uri中包含了authority(主机等各种信息),得到对应的ContentProvider后将调用ContentResolver的与之对应的增删改查方法,并将参数通过ContentResolver的增删改查方法传递到ContentProvider中。在上面用到了CursorAdapter关于CursorAdapter的用法可以参考此博客http://blog.csdn.net/dmk877/article/details/44983491

PersonObserver的代码如下

[java] view plain copy

  1. package com.example.otherapplication.observer;
  2. import android.database.ContentObserver;
  3. import android.os.Handler;
  4. import android.os.Message;
  5. public class PersonOberserver extends ContentObserver {
  6. private Handler handler;
  7. public PersonOberserver(Handler handler) {
  8. super(handler);
  9. this.handler=handler;
  10. }
  11. @Override
  12. public void onChange(boolean selfChange) {
  13. super.onChange(selfChange);
  14. //向handler发送消息,更新查询记录
  15. Message msg = new Message();
  16. handler.sendMessage(msg);
  17. }
  18. }

可以看到,在构造方法中接收了Handler然后当监听到指定的Uri的数据变化时就会通过Handler消息机制发送一条消息,然后的操作就由我们自行完成了。

到这里我们来理一理整个操作的运行流程:首先有两个项目,一个是有ContentProvider的,在这个ContentProvider中初始化了一个数据库,我们的目的就是在另一个项目中来操作这个项目中ContentProvider中的数据,例如插入一条数据,查询等。对于怎么在另一个项目中操作ContentProvider中的数据,是通过ContentResolver(内容解析者)对象来操作的,假如我们要进行insert操作,那么需要调用ContentResolver的insert(uri, ContentValues);将Uri和ContentValues对象经过一系列操作传递到ContentProvider的中,然后在ContentProvider会对这个Uri进行匹配,如果匹配成功则按照我们的需求去执行相应的操作,如:插入数据、查询数据等。如果想进一步理解ContentProvider和ContentResolver之间的关系http://blog.csdn.net/u010961631/article/details/14227421(对这个过程从源码进行了解析,不建议初学者阅读)。下面我们来画一张图再来说一下这个过程

从图中可以看出在OtherApplication中注册了ContentObserver之后,当Application1中的数据库发生了变化时,只需要在ContentProvider中调用ContentResolver的notifyChange(Uri,ContentObserver observer),由于在OtherApplication中注册了ContentObserver(注册时用的Uri和ContentProvider中发生变化的Uri一样)因此在ContentObserver中会收到这个变化信息,它就可以将这个消息通过Handler发送给OtherApplication。

--------------------------------------------------------------------------

一.读写加联系人

addcontact

向系统添加联系人

package com.glsite.addcontact;import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;/*** @author Admin* @version $Rev$* @des ${TODO}* @updateAuthor $Author$* @updateDes ${TODO}*/
public class BaseActivity extends AppCompatActivity {//**************** Android M Permission (Android 6.0权限控制代码封装)private int permissionRequestCode = 88;private PermissionCallback permissionRunnable;public interface PermissionCallback {void hasPermission();void noPermission();}/*** Android M运行时权限请求封装** @param permissionDes 权限描述* @param runnable      请求权限回调* @param permissions   请求的权限(数组类型),直接从Manifest中读取相应的值,比如Manifest.permission.WRITE_CONTACTS*/public void performCodeWithPermission(@NonNull String permissionDes, PermissionCallback runnable, @NonNull String... permissions) {if (permissions == null || permissions.length == 0)return;//        this.permissionrequestCode = requestCode;this.permissionRunnable = runnable;if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.M) || checkPermissionGranted(permissions)) {if (permissionRunnable != null) {permissionRunnable.hasPermission();permissionRunnable = null;}} else {//permission has not been granted.requestPermission(permissionDes, permissionRequestCode, permissions);}}private boolean checkPermissionGranted(String[] permissions) {boolean flag = true;for (String p : permissions) {if (ActivityCompat.checkSelfPermission(this, p) != PackageManager.PERMISSION_GRANTED) {flag = false;break;}}return flag;}private void requestPermission(String permissionDes, final int requestCode, final String[] permissions) {if (shouldShowRequestPermissionRationale(permissions)) {/*1. 第一次请求权限时,用户拒绝了,下一次:shouldShowRequestPermissionRationale()  返回 true,应该显示一些为什么需要这个权限的说明2.第二次请求权限时,用户拒绝了,并选择了“不在提醒”的选项时:shouldShowRequestPermissionRationale()  返回 false3. 设备的策略禁止当前应用获取这个权限的授权:shouldShowRequestPermissionRationale()  返回 false*/// Provide an additional rationale to the user if the permission was not granted// and the user would benefit from additional context for the use of the permission.// For example, if the request has been denied previously.//            Snackbar.make(getWindow().getDecorView(), requestName,//                    Snackbar.LENGTH_INDEFINITE)//                    .setAction(R.string.common_ok, new View.OnClickListener() {//                        @Override//                        public void onClick(View view) {//                            ActivityCompat.requestPermissions(BaseAppCompatActivity.this,//                                    permissions,//                                    requestCode);//                        }//                    })//                    .show();//如果用户之前拒绝过此权限,再提示一次准备授权相关权限new AlertDialog.Builder(this).setTitle("提示").setMessage(permissionDes).setPositiveButton("授权", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {ActivityCompat.requestPermissions(BaseActivity.this, permissions, requestCode);}}).show();} else {// Contact permissions have not been granted yet. Request them directly.ActivityCompat.requestPermissions(BaseActivity.this, permissions, requestCode);}}private boolean shouldShowRequestPermissionRationale(String[] permissions) {boolean flag = false;for (String p : permissions) {if (ActivityCompat.shouldShowRequestPermissionRationale(this, p)) {flag = true;break;}}return flag;}/*** Callback received when a permissions request has been completed.*/@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,@NonNull int[] grantResults) {if (requestCode == permissionRequestCode) {if (verifyPermissions(grantResults)) {if (permissionRunnable != null) {permissionRunnable.hasPermission();permissionRunnable = null;}} else {Toast.makeText(this, "暂无权限执行相关操作!", Toast.LENGTH_SHORT).show();if (permissionRunnable != null) {permissionRunnable.noPermission();permissionRunnable = null;}}} else {super.onRequestPermissionsResult(requestCode, permissions, grantResults);}}public boolean verifyPermissions(int[] grantResults) {// At least one result must be checked.if (grantResults.length < 1) {return false;}// Verify that each required permission has been granted, otherwise return false.for (int result : grantResults) {if (result != PackageManager.PERMISSION_GRANTED) {return false;}}return true;}//********************** END Android M Permission ****************************************
}
package com.glsite.addcontact;import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;public class MainActivity extends BaseActivity {private EditText mEtName;private EditText mEtEmail;private EditText mEtPhone;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);performCodeWithPermission("插入联系人信息", new PermissionCallback() {@Overridepublic void hasPermission() {Toast.makeText(MainActivity.this, "已经授权", Toast.LENGTH_SHORT).show();}@Overridepublic void noPermission() {Toast.makeText(MainActivity.this, "没有授权", Toast.LENGTH_SHORT).show();}}, Manifest.permission.WRITE_CONTACTS, Manifest.permission.READ_CONTACTS);mEtName = findViewById(R.id.et_name);mEtEmail = findViewById(R.id.et_email);mEtPhone = findViewById(R.id.et_phone);}/*** 添加联系人信息** @param view*/public void addContact(View view) {// 1.判断是否为空String name = mEtName.getText().toString().trim();String email = mEtEmail.getText().toString().trim();String phone = mEtPhone.getText().toString().trim();if (TextUtils.isEmpty(name)) {Toast.makeText(this, "姓名不能为空", Toast.LENGTH_SHORT).show();return;}// 2.在raw_contact表里面添加联系人idContentResolver resolver = getContentResolver();Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");Uri dataUri = Uri.parse("content://com.android.contacts/data");Cursor cursor = resolver.query(uri, null, null, null, null, null);int new_id = cursor.getCount() + 1;ContentValues values = new ContentValues();values.put("contact_id", new_id);resolver.insert(uri, values);// 3.在data表里面添加联系人数据ContentValues namevalues = new ContentValues();namevalues.put("raw_contact_id", new_id);namevalues.put("data1", name);namevalues.put("mimetype", "vnd.android.cursor.item/name");resolver.insert(dataUri, namevalues);ContentValues phonevalues = new ContentValues();phonevalues.put("raw_contact_id", new_id);phonevalues.put("data1", phone);phonevalues.put("mimetype", "vnd.android.cursor.item/phone_v2");resolver.insert(dataUri, phonevalues);ContentValues emailvalues = new ContentValues();emailvalues.put("raw_contact_id", new_id);emailvalues.put("data1", email);emailvalues.put("mimetype", "vnd.android.cursor.item/email_v2");resolver.insert(dataUri, emailvalues);Toast.makeText(this, "添加信息成功", Toast.LENGTH_SHORT).show();}
}

 添加联系人与读取联系人权限

<uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>

================================================

二.读写通话记录

CallLog

package com.glsite.calllog;import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;public class MainActivity extends BaseActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);performCodeWithPermission("读写拨打记录", new PermissionCallback() {@Overridepublic void hasPermission() {Toast.makeText(MainActivity.this, "已经授权", Toast.LENGTH_SHORT).show();}@Overridepublic void noPermission() {Toast.makeText(MainActivity.this, "没有授权", Toast.LENGTH_SHORT).show();}}, Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG);}public void readCallLog(View view) {ContentResolver resolver = getContentResolver();Uri uri = Uri.parse("content://call_log/calls");Cursor cursor = resolver.query(uri, new String[]{"number", "date", "type"}, null, null, null);while (cursor.moveToNext()) {String number = cursor.getString(0);String date = cursor.getString(1);String type = cursor.getString(2);System.out.println("number:" + number + "--date:" + date + "--type:" + type);}cursor.close();}public void insertCallLog(View view) {ContentResolver resolver = getContentResolver();Uri uri = Uri.parse("content://call_log/calls");ContentValues values = new ContentValues();values.put("number", "12345678912");values.put("date", System.currentTimeMillis());values.put("type", "1");//未接通是3 ,拨出去接通是1,未接通是2values.put("duration", "60");resolver.insert(uri, values);}
}
    <uses-permission android:name="android.permission.READ_CALL_LOG"/><uses-permission android:name="android.permission.WRITE_CALL_LOG"/>

=====================================

三.观察者监听短信

Observer

package com.glsite.observer;import android.Manifest;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;public class MainActivity extends BaseActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);performCodeWithPermission("读取短信", new PermissionCallback() {@Overridepublic void hasPermission() {}@Overridepublic void noPermission() {}}, Manifest.permission.READ_SMS);ContentResolver resolver = getContentResolver();Uri uri = Uri.parse("content://sms/");resolver.registerContentObserver(uri, true, new MyObserver(new Handler()));}private class MyObserver extends ContentObserver {public MyObserver(Handler handler) {super(handler);}@Overridepublic void onChange(boolean selfChange) {super.onChange(selfChange);System.out.println("监听到数据库的内容变化了");Uri uri = Uri.parse("content://sms/");Cursor cursor = getContentResolver().query(uri, new String[]{"address", "body", "date", "type"},null, null, "date desc");cursor.moveToFirst();String address = cursor.getString(0);String body = cursor.getString(1);String date = cursor.getString(2);String type = cursor.getString(3);System.out.println("address:" + address + " --body:" + body + " --date:" + date + " --type" + type);cursor.close();}}
}
 <uses-permission android:name="android.permission.READ_SMS"/>

===========================================

四.获取之前已经收到的短信

SmsProvider

package com.glsite.smsprovider;import android.Manifest;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;public class MainActivity extends BaseActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);performCodeWithPermission("读取短信", new PermissionCallback() {@Overridepublic void hasPermission() {}@Overridepublic void noPermission() {}}, Manifest.permission.READ_SMS);}public void readSms(View view) {ContentResolver resolver = getContentResolver();Uri uri = Uri.parse("content://sms/");Cursor cursor = resolver.query(uri, new String[]{"address", "date", "type", "body"},null, null, null);while (cursor.moveToNext()) {String address = cursor.getString(0);String date = cursor.getString(1);String type = cursor.getString(2);String body = cursor.getString(3);System.out.println("address:" + address + " --date:" + date + " --type:" + type + " --body:" + body);}cursor.close();}
}
<uses-permission android:name="android.permission.READ_SMS"/>

内容提供者ContentProvider, 读写加联系人,读写通话记录,监听收到的短信,获取之前已经收到的短信相关推荐

  1. Android开发14——监听内容提供者ContentProvider的数据变化

    一.提出需求 有A,B,C三个应用,B中的数据需要被共享,所以B中定义了内容提供者ContentProvider:A应用修改了B应用的数据,插入了一条数据.有这样一个需求,此时C应用需要得到数据被修改 ...

  2. Android通讯录管理(获取联系人、通话记录、短信消息)(三)

    Android通讯录管理(获取联系人.通话记录.短信消息)(三) 这是通讯录管理的最后一篇,前面两篇已经把获取联系人和通话记录解决了,短息消息就相对来说要稍微复杂那么一点.我们先来看看效果图: 源码下 ...

  3. Android通讯录管理(获取联系人、通话记录、短信消息)

    前言:前阵子主要是记录了如何对联系人的一些操作,比如搜索,全选.反选和删除等在实际开发中可能需要实现的功能,本篇博客是小巫从一个别人开源的一个项目抽取出来的部分内容,把它给简化出来,可以让需要的朋友清 ...

  4. android通讯录管理(获取联系人,通话记录,短信消息),Android通讯录管理(获取联系人、通话记录、短信消息)(二)...

    Android通讯录管理(获取联系人.通话记录.短信消息)(二) 前言:上一篇博客介绍的是获取联系人的实现,本篇博客将介绍通话记录的实现. 界面布局: /Contact_Demo/res/layout ...

  5. Android通讯录管理(获取联系人、通话记录、短信消息)(二)

    Android通讯录管理(获取联系人.通话记录.短信消息)(二) 前言:上一篇博客介绍的是获取联系人的实现,本篇博客将介绍通话记录的实现. 同样的,你可以到这里下载源码:http://download ...

  6. 基于腾讯 x5 开源库,提高 webView 开发效率,大概要节约你百分之六十的时间成本。该案例支持处理 js 的交互逻辑且无耦合、同时暴露进度条加载进度、可以监听异常 error 状态、支持视频播放

    YCWebView 项目地址:yangchong211/YCWebView 简介: 基于腾讯 x5 开源库,提高 webView 开发效率,大概要节约你百分之六十的时间成本.该案例支持处理 js 的交 ...

  7. [react] 组件卸载前,加在DOM元素的监听事件和定时器要不要手动清除?为什么?

    [react] 组件卸载前,加在DOM元素的监听事件和定时器要不要手动清除?为什么? 定时器要在 componentWillUnmount 手动清除,直接绑定在JSX里的事件监听器不用,使用ref绑定 ...

  8. android 通话状态监听(自定义接听挂断按钮与通话界面,根据公司的业务逻辑可以实现自己的来电秀功能)...

    前言: 因为公司需求,要自定义一款来电秀的app当做周边产品来配合主营的app业务. 之前因为赶项目,没时间整理这块,现在项目告一段落了,现在回头看看感觉这个功能还是挺有意思的,比较有针对性.电话呼入 ...

  9. Android学习---通过内容提供者(ContentProvider)操作另外一个应用私有数据库的内容...

    一.什么是ContentProvider? ContentProvider直译过来就是内容提供者,主要作用就是A应用提供接口给B应用调用数据,和之前介绍的sharedPreference和直接开放文件 ...

最新文章

  1. Android教你打造独一无二的刷新加载框架
  2. Fedora 17 install VMWare tool
  3. 使用 Task 简化异步编程
  4. bzoj1024 [SCOI2009]生日快乐 结论+dfs
  5. do_something方法解析
  6. python自动化办公入门书籍-Python如此神奇,让繁琐工作自动化 (文中含Python基础)...
  7. atitit.php 流行框架 前三甲为:Laravel、Phalcon、Symfony2 attilax 总结
  8. 流体力学matlab例题,流体力学简单计算MATLAB程式
  9. 解决ThinkPad早期笔记本Broadcom博通系列无线网卡Win10掉线、受限、速度慢问题(ThinkPad E530为例)
  10. lnmp 升级php后 502 bad gateway,lnmp 运行一段时间后出现nginx 502 Bad Gateway的解决方法...
  11. 南向接口 YANG 文件定义规范
  12. 安装并测试Gitweb
  13. 华为交换机/华三交换机 查看光口模块信息
  14. 有限元中四面体的一些积分公式
  15. Pyecharts基本图:日历图
  16. 空间申请(malloc)与释放(free)——C语言
  17. 年会活跃微信群小游戏有哪些?塔防小游戏经典玩法讲解
  18. HBase(6):HBase优化
  19. STM8L低功耗设置,深坑。。。
  20. 安装配置Maven构建工具

热门文章

  1. javaweb JAVA JSP学校宿舍公寓管理系统(JSP宿舍管理系统)java寝室管理网站源码
  2. 撤底理解es6中的箭头函数
  3. C++ Type traits
  4. H3C F100-C-A3 网络防火墙 进行恢复出厂设置操作
  5. 故障模块名称: NetdiskExt64.dll的解决之法
  6. ASIC加速技术在航空航天领域的应用:提高飞行器速度和稳定性
  7. Python爬虫入门【3】:美空网数据爬取
  8. tcp_tw_recycle参数引发的故障
  9. SqlServer删除表,不记录日志
  10. 卸载office2016kms密钥重新获取office安装密钥窗口