首页 > 代码库 > Android学习笔记十九.使用ContentProvider实现数据共享(一)

Android学习笔记十九.使用ContentProvider实现数据共享(一)

一、Android如何实现数据共享? 
    为了在应用程序之间交换数据,Android提供了ContentProvider,ContentProvider是不同应用程序之间进行数据交换的标准API,当一个应用程序需要把自己的数据暴露给其他程序使用时,该应用程序就可通过提供ContentProvider来实现,其他的应用程序就可以通过ContentResolver来操作ContentProvider暴露的数据。一旦某个应用程序通过ContentProvider暴露了自己的数据操作接口,那么不管该应用程序是否启动,其他应用程序都可以通过该接口来操作该应用程序的内部数据,包括增加数据、删除数据、修改数据、查询数据等。
总结:A应用通过ContentProvider暴露自己的数据,B应用通过ContentResolver来操作ContentProvider暴露的数据。
        A应用以某种Uri的形式对外提供数据,B应用使用ContentResolver根据A应用提供的Uri获得A应用的authority 属性去访问操作指定的数据。
二、ContentProvider与
ContentResolver简介

1.功能
(1)ContentProvider是不同应用程序之间进行数据交换的标准API,ContentProvider以某种Uri的形式对外提供数据,运行其他应用访问或修改数据,即其他应用程序使用ContentResolver根据Uri去访问操作指定数据, UriMatcher类用于帮助解
析URI。在实际应用中,自定义的ContentProvider类除了需要继承ContentProvider之外,还需要同时实现以下方法(均为抽象方法):
    - abstract boolean onCreate()
    - abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
     -abstract Uri insert(Uri uri, ContentValues values)
    - abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
    - abstract int delete(Uri uri, String selection, String[] selectionArgs)
    - abstract String getType(Uri uri) 
(2)Contentprovider相当于一个"网站",它的作用是暴露可供提供操作的数据。其他应用程序则通过ContentResolver来操作ContentProvider所暴露的数据,即ContentResolver相当于HttpClient。
    一般来说,ContentProvider是单实例模式,当多个应用程序通过ContentResolver来操作ContentProvider提供的数据时,ContentResolver调用的数据操作将会委托给同一个ContentProvider处理

升华笔记1:ContentResolver是如何实现访问、修改Uri对应的ContentProvider中的数据?
    从ContentResolver、ContentProvider和Uri的关系来看,Uri是ContentResolver和ContentProvider进行数据交换的标识。
    ContentResolver对指定Uri执行CRUD等数据操作,但Uri并不是真正的数据中心,因此这些CRUD操作会委托给该Uri对应的ContentProvider来实现。举个例子:假如B应用通过ContentResolver执行CUUD操作,这些CRUD操作都需要指定Uri参数,Android系统就根据该Uri找到对应的ContentProvider(该ContentProvider属于A应用),ContentProvider复杂实现CRUD方法,完成对底层数据的增、删、改、查等操作,即实现了B应用访问A应用数据的目的。
技术分享
注释:这里的CRUD操作,指的是当B应用调用ContentResolver的insert()方法是,实际上相当于调用了该Uri对应的ContentProvider(属于A应用)的insert()方法。

2.API
ContentProvider
(1)继承关系
public abstract class
java.lang.Object
     ? android.content.ContentProvider
(2)构造方法
ContentProvider()
Construct a ContentProvider instance.
(3)常用方法
 abstract boolean onCreate():该方法在ContentProvider创建后会被调用,当其他应用程序第一次访问ContentProvider时,
该 ContentProvider会被创建出来,并立即回调该onCreate()方法;
abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):根据Uri查询出select条件所匹配的全部记录,其中projection就是一个列名列表,表明只选择指定的数据列;
abstract Uri insert(Uri uri, ContentValues values):根据该Uri插入values对应的数据
abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):根据Uri修改selection条件所匹配的全部记录
abstract int delete(Uri uri, String selection, String[] selectionArgs):根据Uri删除select条件所匹配的全部记录;
abstract String getType(Uri uri) :该方法用于返回当前Uri所代表的数据的MIME类型。如果该Uri对应数据可能包括多条记录,那么MIME类型字符串应用以vnd.android.cursor.dir/开头。如果该Uri对应的数据只包含一条记录,那么返回MIME类型字符串应该以vnd.android.cursor.item/开头。

ContentResolver
(1)继承关系
public abstract class
java.lang.Object
   ? android.content.ContentProvider
(2)构造方法(创建ContentResolver对象)
ContentResolver(Context context)
此外,Content提供了getContentResolver()方法来获取一个ContentResolver对象。也就是说,Activity、Service等组件都可通过getContentResolver()方法获取ContentResolver对象,这样我们再调用ContenResolver的如下方法就可以操作暴露的数据了。
                                                                    ContentResolver contentResolver=getContentResolver();
(3)常用方法
  
final int delete(Uri url, String where, String[] selectionArgs):删除Uri对应的ContentProvider中where提交匹配的数据;
final String getType(Uri url):获取给定Content URL的MIME类型;
final Uri insert(Uri url, ContentValues values):向Uri对应的ContentProvider中插入values对应的数据;
Parameters:
url The URL of the table to insert into.
values The initial values for the newly inserted row. The key is the column name for the field. Passing an empty ContentValues will create an empty row.
Returns:
the URL of the newly created row.
final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):查询Uri对应的ContentProvider中where提交匹配的数据;
Parameters:
uri The URI, using the content:// scheme, for the content to retrieve.
projection A list of which columns to return. Passing null will return all columns, which is inefficient.
selection A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing null will return all rows for the given URI.
selectionArgs You may include ?s in selection, which will be replaced by the values from selectionArgs, in the order that they appear in the selection. The values will be bound as Strings.
sortOrder How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null will use the default sort order, which may be unordered.
Returns:
A Cursor object, which is positioned before the first entry, or null
final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer):注册一个观察类,当暴露的URI对应的ContentProvider中的数据发生变化时调用该方法
final int update(Uri uri, ContentValues values, String where, String[] selectionArgs)更新Uri对应的ContentProvider中where提交匹配的数据;

ContentValues
    This class is used to store a set of values that the ContentResolver can process.
(1)继承关系
java.lang.Object 
   ? android.content.ContentValues
(2)构造函数
ContentValues()
Creates an empty set of values using the default initial size
ContentValues(int size)
Creates an empty set of values using the given initial size
ContentValues(ContentValues from)
Creates a set of values copied from the given set
(3)常用方法
void put(String key, Byte value) :添加键值对值到set集合
Set<Entry<String, Object>> valueSet() :返回set集合中所有的键对象和值对象
int size() :返回set集合中值的数量

Cursor接口
(1)功能概述:该接口主要用于随机读写数据库查询返回set集合形式的结果。Cursor的实现不需要同步,所有当我们使用Cursor接口时,多线程使用Cursor时只需要注意自身同步即可。
(2)常用方法
abstract void close() :关闭Cursor,释放所有资源
abstract void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) :获取需要的列文本并将其存储到提供的buffer数组中
abstract int getPosition() :返回set集合中cursor游标当前的行位置
abstract boolean isNull(int columnIndex) :当指定的列为null时返回为true
abstract boolean move(int offset) :把当前游标位置向前或向后移动offset数量
abstract boolean moveToNext() :将游标移动到下一行

三、ContentProvider与
ContentResolver
开发思路
1.ContentProvider开发思路(A应用提供ContentResolver接口)
(1)定义一个ContentProvider子类。该子类需要继承Android提供的ContentProvider基类,并且需要实现query()、insert()、update()和delete()(抽象方法)等方法;
(2)配置ContentProvider。在该ContentProvider供其他应用调用之前,需要向Android系统注册ContentProvider,即在A应用的工程文件AndroidManifest.xml文件中注册这ContentProvider(类似于注册Activity),且注册ContentProvider时需要为它绑定一个Uri;
  <provider android:name=".FirsttProvider"                                                        //指定该ContentProvider的实现类的类名
                android:authorities="com.example.Android.ContentProvider_1"        //指定该ContentProvider对应的Uri(相当于为该ContentProvider分配一个域名)
                android:exported="true"/>                                                                //指定该ContentProvider是否允许其他应用调用("true"为允许)

升华笔记2:
注释1:ContentProvider子类实现的query()、insert()、update()和delete()方法,并不是给A应用本身调用,而是提供给其他应用来调用。
注释2:由于可以有多个应用程序通过ContentProvider暴露自己数据,android:authorities的作用在于方便其他应用程序访问、删除指定应用程序数据。
技术分享
注释3:传递参数-ContentResolver调用方法时的参数,将会传递给Uri对应的ContentProvider的query()、insert()、update()和delete()方法;
            返回值   -ContentResolver调用方法时的返回值,即为Uri对应的ContentProvider的query()、insert()、update()和delete()方法的返回值;

2.ContentResolver开发思路(B应用访问、修改A应用的数据)
(1)获取ContentResolver实例对象。
(2)通过实例对象调用ContentResolver的query()、insert()、update()和delete()方法。即指定Uri对应ContentProvider的query()、insert()、update()和delete()方法将要完成的功能。
四、实战源码
1.开发ContentProvider
(1)FirstProvider.java
package com.example.android_contentprovider_1;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
public class FirstProvider extends ContentProvider {
 //1.第一次创建该ContentProvider时调用该方法
 @Override
 public boolean onCreate() {
  System.out.println("====onCreate方法被调用=====");
  return false;
 }
 //2.实现查询方法,该方法应该返回查询得到的Cursor
 @Override
 public Cursor query(Uri uri, String[] projection, String where,
   String[] selectionArgs, String sortOrder) {
  System.out.println("====query方法被调用=====");
  System.out.println("where参数为:"+where);
  return null;
 }
 //3.该方法的返回值代表了该ContentProvider所提供数据的MIME类型
 @Override
 public String getType(Uri uri) {
  return null;
 }
 //4.实现插入的方法,该方法应该返回新插入的记录的Uri
 @Override
 public Uri insert(Uri uri, ContentValues values) {
  System.out.println(uri+"====insert被调用=====");
  System.out.println("values参数为:"+values);
  return null;
 }
 //5.实现删除方法,该方法应该返回被删除的记录条数
 @Override
 public int delete(Uri uri, String where, String[] selectionArgs) {
  System.out.println(uri+"====delete方法被调用=====");
  System.out.println("where参数为:"+where);
  return 0;
 }
 //6.实现更新方法,该方法应该返回被更新的记录条数
 @Override
 public int update(Uri uri, ContentValues values, String selection,
   String[] selectionArgs) {
  System.out.println(uri+"====update方法被调用=====");
  System.out.println("values参数为:"+values);
  return 0;
 }
}
(2)AndroidManifest.xml(部分)
<application 
android:icon="@drawable/ic_launcher"> 
<!--注册一个ContentProvider --> 
<provider 
android:name=".FirstProvider" 
android:authorities="com.example.android_contentprovider_1" 
android:exported="true"/> 
</application> 
</manifest>

2.使用ContentResolver调用方法
FirstResolver.java
package com.example.android_contentresolver_2;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class FirstResolver extends Activity {
 ContentResolver contentResolver;
 Uri uri=Uri.parse("content://com.example.android_contentprovider_1");	//解析一个uri用以获取对应的ContentProvider
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
 //1.获取系统的ContentResolver对象
  contentResolver=getContentResolver();
 }
 //2.调用ContentResolver的query()方法
 public void query(View source)
 {
  Cursor c=contentResolver.query(uri, null, "query_where", null, null);//实际返回的是该Uri对应的ContentProvider的query()的返回值
  Toast.makeText(this, "远程ContentProvider返回的Cursor为"+c, Toast.LENGTH_SHORT).show();
 }
 //3.调用ContentResolver的insert()方法
 public void insert(View source)
 {
  ContentValues values=new ContentValues();
  values.put("name", "jiangdongguo");
  Uri newUri=contentResolver.insert(uri, values);	//将键值对插入到uri对应的ContentProvider共享数据中
  Toast.makeText(this, "远程ContentProvider新插入记录的Uri为:"+newUri, Toast.LENGTH_SHORT).show();
 }
 //4.调用ContentResolver()的update()方法
 public void update(View source)
 {
  ContentValues values=new ContentValues();
  values.put("name", "zhongxian");
  int count=contentResolver.update(uri, values, "update_where", null);
  Toast.makeText(this, "远程ContentProvider更新记录数为:"+count, Toast.LENGTH_SHORT).show();
 }
 //5.调用ContentResoler的delete()方法
 public void delete(View source)
 {
  int count=contentResolver.delete(uri, "delete_where", null);//实际返回的是该ContentProvider的delete()的返回值
  Toast.makeText(this, "远程ContentProvider删除记录数为:"+count, Toast.LENGTH_SHORT).show();
 }
 
}

注释:布局界面为4个按钮,每个按钮需要定义android:onClick="delete"属性。
(1)ContentProvider不同于Activity存在复杂的声明周期,ContentProvider只有一个onCreate()生命周期方法,当其他应用通过ContentResolver第一次访问该ContentProvider时,onCreate()方法将会被回调,onCreate方法只会被调用一次。
(2)其他应用通过ContentResolver调用query()、insert()、delete()、update()方法,实际上是调用ContentProvider提供的query()、insert()、delete()、update()方法来操作ContentProvider暴露的数据;
(3)开发ContentProvider时所实现的query()、insert()、delete()、update()方法的第一个参数为Uri参数,该参数由ContentResolver调用这些方法时传入。
 效果演示: 
技术分享
技术分享

参考:http://wear.techbrood.com/reference/android/content/ContentProvider.html

Android学习笔记十九.使用ContentProvider实现数据共享(一)