ContentProvider简介

ContentProvider(数据提供者)是在应用程序间共享数据的一种接口机制 ContentProvider提供了更为高级的数据共享方法,应用程序可以指定需要共享的数据,而其他应用程序则可以在不知数据来源、路径的情况下,对共享数据进行查询、添加、删除和更新等操作 许多Android系统的内置数据也通过ContentProvider提供给用户使用,例如通讯录、音视频文件和图像文件等 在创建ContentProvider时,需要首先使用数据库、文件系统或网络实现底层存储功能,然后在继承ContentProvider的类中实现基本数据操作的接口函数,包括添加、删除、查找和更新等功能 调用者不能够直接调用ContentProvider的接口函数,而需要使用ContentResolver对象,通过URI间接调用ContentProvider。

使用ContentProvider可以在不同的应用程序之间共享数据。 它为存储和获取数据提供了统一的接口。ContentProvide对数据进行封装,不用关心数据存储的细节。

ContentProvider不管底层数据的实际存储方式,对外统一使用表的形式来组织数据 。

URI的简介

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

 URI的格式

A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"  B:URI 的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称; "content://hx.android.text.myprovider" C:路径,通俗的讲就是你要操作的数据库中表的名字;"content://hx.android.text.myprovider/tablename" D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id

路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下: • 要操作contact表中id为10的记录,可以构建这样的路径:/contact/10 • 要操作contact表中id为10的记录的name字段, contact/10/name • 要操作contact表中的所有记录,可以构建这样的路径:/contact 要操作的数据不一定来自数据库,也可以是文件等他存储方式,如下: 要操作xml文件中contact节点下的name节点,可以构建这样的路径:/contact/name 如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下: Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")

UriMatcher

因为Uri代表了要操作的数据,所以我们很经常需要解析Uri,并从Uri中获取数据。Android系统提供UriMatcher ,用于匹配Uri,用法如下:    1.首先把你需要匹配Uri路径全部给注册上,如:    uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);    uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2); 2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码. //如果match()匹配路径,返回匹配码为1  content://com.changcheng.sqlite.provider.contactprovider/contact //如果match()匹配路径,返回匹配码为2 content://com.changcheng.sqlite.provider.contactprovider/contact/23

因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。掌握它们的使用,会便于我们的开发工作。 UriMatcher类用于匹配Uri,它的用法如下: 首先第一步把你需要匹配Uri路径全部给注册上,如下: //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码 UriMatcher  sMatcher = new UriMatcher(UriMatcher.NO_MATCH); //如果match()方法匹配content://cn.itcast.provider.personprovider/person路径,返回匹配码为1 sMatcher.addURI(“cn.itcast.provider.personprovider”, “person”, 1);//添加需要匹配uri,如果匹配就会返回匹配码 //如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路径,返回匹配码为2 sMatcher.addURI(“cn.itcast.provider.personprovider”, “person/#”, 2);//#号为通配符 switch (sMatcher.match(Uri.parse("content://cn.itcast.provider.personprovider/person/10"))) {    case 1     break;    case 2     break;    default://不匹配     break; } 注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://cn.itcast.provider.personprovider/person路径,返回的匹配码为1

 ContentUris类

ContentUris类用于获取Uri路径后面的ID部分,它有两个比较实用的方法: withAppendedId(uri, id)用于为路径加上ID部分: Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person") Uri resultUri = ContentUris.withAppendedId(uri, 10); //生成后的Uri为:content://cn.itcast.provider.personprovider/person/10

parseId(uri)方法用于从路径中获取ID部分: Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person/10") long personid = ContentUris.parseId(uri);//获取的结果为:10

ContentProvider的编程方法

程序开发人员通过继承ContentProvider类可以创建一个新的数据提供者,过程可以分为三步 1.继承ContentProvider,并重载六个函数 public boolean onCreate() 该方法在ContentProvider创建后就会被调用, Android开机后, 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中获取数据。 public String getType(Uri uri) 该方法用于返回当前Url所代表数据的MIME类型。如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,例如:要得到所有person记录的Uri为content://cn.itcast.provider.personprovider/person,那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,例如:得到id为10的person记录,Uri为content://cn.itcast.provider.personprovider/person/10,那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。

2.声明CONTENT_URI,实现UriMatcher 3.注册ContentProvider  AndroidManifest.xml使用<provider>对该ContentProvider进行配置,为了能让其他应用找到该ContentProvider , ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名:

<application android:icon="@drawable/icon" android:label="@string/app_name">  <provider android:name = ".PeopleProvider"    android:authorities = "edu.hrbeu.peopleprovider"/> </application>

注意:一旦应用继承了ContentProvider类,后面我们就会把这个应用称为ContentProvider(内容提供者)。

ContentResolver的编程方法

使用ContentProvider是通过Android组件都具有的ContentResolver对象,通过URI进行数据操作 程序开发人员只需要知道URI和数据集的数据格式,则可以进行数据操作,解决不同应用程序之间的数据共享问题 每个Android组件都具有一个ContentResolver对象,获取ContentResolver对象的方法是调用getContentResolver()函数

使用ContentResolver操作ContentProvider中的数据

当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 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://cn.itcast.providers.personprovider/person/10”),那么将会对主机名为cn.itcast.providers.personprovider的ContentProvider进行操作,操作的数据为person表中id为10的记录。

使用ContentResolver对ContentProvider中的数据进行添加、删除、修改和查询操作: ContentResolver resolver =  getContentResolver(); Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person"); //添加一条记录 ContentValues values = new ContentValues(); values.put("name", "itcast"); values.put("age", 25); resolver.insert(uri, values);   //获取person表中所有记录 Cursor cursor = resolver.query(uri, null, null, null, "personid desc"); while(cursor.moveToNext()){  Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1)); } //把id为1的记录的name字段值更改新为liming ContentValues updateValues = new ContentValues(); updateValues.put("name", "liming"); Uri updateIdUri = ContentUris.withAppendedId(uri, 2); resolver.update(updateIdUri, updateValues, null, null); //删除id为2的记录 Uri deleteIdUri = ContentUris.withAppendedId(uri, 2); resolver.delete(deleteIdUri, null, null);

当ContentProvider中的数据发生变化时可以向其用户发出通知

如果ContentProvider的访问者需要得知ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调用getContentResolver().notifyChange(uri, null)来通知注册在此URI上的访问者,例子如下: public class PersonContentProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) {  db.insert("person", "personid", values);  getContext().getContentResolver().notifyChange(uri, null); } } 如果ContentProvider的访问者需要得到数据变化通知,必须使用ContentObserver对数据(数据采用uri描述)进行监听,当监听到数据变化通知时,系统就会调用ContentObserver的onChange()方法: getContentResolver().registerContentObserver(Uri.parse("content://cn.itcast.providers.personprovider/person"),           true, new PersonObserver(new Handler())); public class PersonObserver extends ContentObserver{  public PersonObserver(Handler handler) {   super(handler);   }  public void onChange(boolean selfChange) {      //此处可以进行相应的业务处理  } }

具体一个例子代码:

View Code
 import cn.itcast.service.DBOpenHelper;
 import android.content.ContentProvider;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 
 public class PersonProvider extends ContentProvider {
     private DBOpenHelper dbOpenHelper;
     //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
     private static final UriMatcher MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
     private static final int PERSONS = 1;
     private static final int PERSON = 2;
     static{
         //如果match()方法匹配content://cn.itcast.provider.personprovider/person路径,返回匹配码为1
         MATCHER.addURI("cn.itcast.providers.personprovider", "person", PERSONS);
         //如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路径,返回匹配码为2
         MATCHER.addURI("cn.itcast.providers.personprovider", "person/#", PERSON);
     }   
     //删除person表中的所有记录   /person
 //删除person表中指定id的记录 /person/10
     @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {
         SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
         int count = 0;
         switch (MATCHER.match(uri)) {
         case PERSONS:
             count = db.delete("person", selection, selectionArgs);
             return count;
            
         case PERSON:
             long id = ContentUris.parseId(uri);
             String where = "personid="+ id;
             if(selection!=null && !"".equals(selection)){
                 where = selection + " and " + where;
             }
             count = db.delete("person", where, selectionArgs);
             return count;
            
         default:
             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
         }
     }
 
     @Override
     public String getType(Uri uri) {//返回当前操作的数据的mimeType
         switch (MATCHER.match(uri)) {
         case PERSONS:    //多条数据       
             return "vnd.android.cursor.dir/person";
            
         case PERSON:    //单条数据       
             return "vnd.android.cursor.item/person";
            
         default:
             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
         }
     }
 
     @Override
     public Uri insert(Uri uri, ContentValues values) {// /person
         SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
         switch (MATCHER.match(uri)) {
         case PERSONS:
             long rowid = db.insert("person", "name", values);
             //生成后的Uri为:content://cn.itcast.provider.personprovider/person/10
             Uri insertUri = ContentUris.withAppendedId(uri, rowid);//得到代表新增记录的Uri
 //当ContentProvider中的数据发生变化时可以向其用户发出通知,第一个参数为uri,说明是person表的uri,不是单条记录的uri
             this.getContext().getContentResolver().notifyChange(uri, null);
             return insertUri;
 
         default://不匹配
             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
         }
     }
 
     @Override
     public boolean onCreate() {
         this.dbOpenHelper = new DBOpenHelper(this.getContext());
         return false;
     }
     //查询person表中的所有记录   /person
 //查询person表中指定id的记录 /person/10
     @Override
     public Cursor query(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
         SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
         switch (MATCHER.match(uri)) {
         case PERSONS:
             return db.query("person", projection, selection, selectionArgs, null, null, sortOrder);
            
         case PERSON:
             long id = ContentUris.parseId(uri);
             String where = "personid="+ id;
             if(selection!=null && !"".equals(selection)){
                 where = selection + " and " + where;
             }
             return db.query("person", projection, where, selectionArgs, null, null, sortOrder);
            
         default:
             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
         }
     }
    
     //更新person表中的所有记录   /person
 //更新person表中指定id的记录 /person/10
     @Override
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
         SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
         int count = 0;
         switch (MATCHER.match(uri)) {
         case PERSONS:
             count = db.update("person", values, selection, selectionArgs);
             return count;
            
         case PERSON:
             //parseId(uri)方法用于从路径中获取ID部分:获取的结果为:10
             long id = ContentUris.parseId(uri);
             String where = "personid="+ id;
             //如果外面传进来的条件不为空,而且不为空字符串
             if(selection!=null && !"".equals(selection)){
                 //外面的条件加自己的条件
                 where = selection + " and " + where;
             }
             count = db.update("person", values, where, selectionArgs);
             return count;
            
         default:
             throw new IllegalArgumentException("Unkwon Uri:"+ uri.toString());
         }
     }
 
 }

AndroidMainFest.xml代码

View Code
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="cn.itcast.db"
       android:versionCode="1"
       android:versionName="1.0">
     <application android:icon="@drawable/icon" android:label="@string/app_name">
          <uses-library android:name="android.test.runner" />
         <activity android:name=".MainActivity"
                   android:label="@string/app_name">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
         <provider android:name=".PersonProvider" android:authorities="cn.itcast.providers.personprovider"/>
     </application>
     <uses-sdk android:minSdkVersion="8" />
 <instrumentation android:name="android.test.InstrumentationTestRunner"
   android:targetPackage="cn.itcast.db" android:label="Tests for My App" />
 </manifest>

另一个应用程序调用ContentProvider

View Code
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
 public class AccessContentProviderTest extends AndroidTestCase {
     private static final String TAG = "AccessContentProviderTest";
    
     /**
      * 往内容提供者添加数据
      * @throws Throwable
 */
     public void testInsert() throws Throwable{
         ContentResolver contentResolver = this.getContext().getContentResolver();
         Uri insertUri = Uri.parse("content://cn.itcast.providers.personprovider/person");
         ContentValues values = new ContentValues();
         values.put("name", "zhangxiaoxiao");
         values.put("amount", 90);
         Uri uri = contentResolver.insert(insertUri, values);
         Log.i(TAG, uri.toString());
     }
 
     /**
      * 更新内容提供者中的数据
      * @throws Throwable
 */
     public void testUpdate() throws Throwable{
         ContentResolver contentResolver = this.getContext().getContentResolver();
         Uri updateUri = Uri.parse("content://cn.itcast.providers.personprovider/person/1");
         ContentValues values = new ContentValues();
         values.put("name", "lili");
         contentResolver.update(updateUri, values, null, null);
     }
    
     /**
      * 从内容提供者中删除数据
      * @throws Throwable
 */
     public void testDelete() throws Throwable{
         ContentResolver contentResolver = this.getContext().getContentResolver();
         Uri deleteUri = Uri.parse("content://cn.itcast.providers.personprovider/person/1");
         contentResolver.delete(deleteUri, null, null);
     }
    
     /**
      * 获取内容提供者中的数据
      * @throws Throwable
 */
     public void testFind() throws Throwable{
         ContentResolver contentResolver = this.getContext().getContentResolver();
         Uri selectUri = Uri.parse("content://cn.itcast.providers.personprovider/person");
         Cursor cursor = contentResolver.query(selectUri, null, null, null, "personid desc");
         while(cursor.moveToNext()){
             int id = cursor.getInt(cursor.getColumnIndex("personid"));
             String name = cursor.getString(cursor.getColumnIndex("name"));
             int amount = cursor.getInt(cursor.getColumnIndex("amount"));
             Log.i(TAG, "id="+ id + ",name="+ name+ ",amount="+ amount);
         }
     }
 }

原文地址:https://www.cnblogs.com/fx2008/p/3137985.html