Android四大组件之Content Provider

 Android四大组件之Content Provider

一、概念
Content Provider 作为Android应用程序四大组件之一,为存储和查询数据提供统一的接口,实现程序间数据的共享。Android系统内一些常见的数据如音乐、视频、图像等都内置了一系列的Content Provider。
应用程序间共享数据有两种方式:

一是创建子类继承于Content Provider,重写该类用于数据存储和查询的方法。

二是直接使用已经存在的Content Provider,如联系人等。
在Content Provider中数据的是以表的形式存储,在数据表中每一行为一条记录,每一列为类型和数据。每一条记录都包括一个唯一的名叫_ID的数值字段,这个字段唯一标识一条数据记录,在创建表的时候用 INTEGER PRIMARY KEY AUTOINCREMENT来标识此字段。
二、相关类介绍

类URI:
每一个Content Provider为其管理的多个数据集,分配一个URI,这个URI对外提供了一个能够唯一标识自己数据集的字符串。这样别的应用程序就可以通过这个URI来访问这个数据集。Android中所有的Content Provider的URI都有固定格式:content://****开头,一般可分为4个部分:

 标准前缀:用来标识一个Content Provider,固定Content://

      URI标识:定义了是哪个Content Provider提供这些数据。一般是定义该ContentProvider的包类的名字。和AndroidManifest.xml中定义的authorities属性相同。

      路径:标识URI下的某一个Item。

记录的ID:如果URI中包含表示某个记录的ID,则返货该id对应的数据。否则表示返回全部。

注意:”content://com.android.people.provider/contacts/#” 这里#表示匹配任意数字”content://com.android.people.provider/contacts/*”表示匹配任意文本

 

UriMatcher:
Uri标识了要操作的数据,而UriMatcher即使Android提供给我们用于操作Uri这个数据的工具类。
常用方法:
public void addURI(String authority, String path, int code) 往UriMatcher对象里面添加Uri。
public int match(Uri uri) 与UriMatcher对象的Uri进行匹配,如果成功返回上面传入的code值,否则返回-1.

类ContentUris:
类似于UriMatcher,也是一个操作Uri数据的工具类,用于在Uri后面追加一个ID或解析出传入的Uri对象的ID值。
常用方法:
public static Uri withAppendId(Uri contentUri, long id)  为前面contentUri加上ID部分
public static long parseId(Uri contentUri) 从contentUri中获取ID部分

 

类ContentProvider:
常用方法:

[java][/java] view plaincopyprint?

  1. public abstract boolean onCreate();
  2. public abstract Uri insert(Uri uri, ContentValues values)
  3. public abstract int delete(Uri uri, String selection, String[] selectionArsg);
  4. public abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs);
  5. public abstract Cursor query(Uri uri, String[] peojection, String selection, String[] selectionArgs, String sortOrder)
  6. public abstract String getType(Uri uri)

这些都是抽象方法需要子类去实现。类 ContentValues:

android.content.ContentValues 这个用于存储ContentResolver能处理的数据的集合。

构造函数:

ContentValues()         // 创建一个空的ContentValues对象,初始化默认大小

ContentValues(int size)

ContentValues(ContentValues from)

常用方法:

[java][/java] view plaincopyprint?

  1. void clear()
  2. boolean containKey(String key)
  3. Object get(String key)
  4. void put(String key, Type value)     // Type: Byte Integer Float Short byte[] String Double Long Boolean
  5. int size()
  6. Type getAsTypeArray(String key)      // Type: Object Boolean Byte byte[] Double Float Integer Long Short String<span style=”font-size:14px;”>
  7. </span>

类android.content.ContentResolver:
一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据以类似数据库中表的方式完全暴露出去,那么外界其他应用程序怎么从数据库中获取数据呢,这就需要ContentResolver了,通过URI表示外界访问需要的数据库。           这个类为我们定义了一系列的方法包括:插入、删除、修改、查询等,与ContentProvider基本类似,主要根据传入的参数Uri找到对应的Content Provider,调用其相应的方法。

构造函数:

public ContentResolver(Context context)

一般在代码中我们直接通过: MainActivity.this.getContentResolver()获得当前应用程序的一个ContentResolver实例。

这里我们需要考虑一个问题,就是如果多个程序同时通过ContentResolver共享访问一个ContentProvider,会不会不同步,尤其是数据写入的时候这就需要在AndroidManifest.xml中定义ContentProvider的时候加上:<provider>元素的multiprocess属性。同时Android在ContentResolver中为我们提供了

notifyChange()接口,在数据发生改变时通知其他的ContentObserver。

[java][/java] view plaincopyprint?

  1. final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
  2. final void unregisterContentObserver(ContentObserver observer)
  3. void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork)
  4. void notifyChange(Uri uri, ContentObserver observer)

三、使用已经存在的ContentProvider
使用已经存在的ContentProvider包括两种情况:一是从已经存在的ContentProvider里面读取数据,如获得
手机里面联系人的信息。二是将自己的数据加入到ContentProvider里面,让其他程序能共享次数据,这就需要获得对这个
ContentProvider的写权限了。

Android中电话簿就是通过ContentProvider实现数据共享的,系统中有很多已经存在的共享URI,我们可以使用ContentResolver通过Uri来操作不同的标的数据。如Contacts.People.CONTENT_URI
在Android中为我们提供了两种方法来查询Content Provider:一是使用ContentResolver的query()方法,二是使用Activity对象的manageQuery()方法,他们的参数都相同,而且都返回Cursor对象。但是使用manageQuery()方法返回的Cursor对象的生命周期自动被Activity来管理,被管理的Cursor对象在Activity进入暂停状态的时候调用自己的deactivate()方法卸载,在Activity回到运行状态的时候调用自己的requery()方法重新查询生成的Cursor。而使用ContentResolver的query()方法返回的Cursor对象需要手动加入Activity来管理,这是通过Activity的startManagingCursor()方法来实现的。
 

 

四、创建自己的ContentProvider

(1) 创建一个继承于ContentProvider的类MyContentProvider
(2) 定义一个public static final Uri 类型变量CONTENT_URI
如:public static final Uri CONTENT_URI = Uri.parse(“content://com.android.MyContentProvider”)
(3) 定义需要返回给客户端的数据列名,如果使用到数据库SQLite,必须定义一个_id,表示记录的唯一性。
(4) 创建数据存储系统,如文件系统或数据库SQLite系统。
(5) 如果存储字节型数据,如位图文件等,数据列其实是一个表示实际保存文件的URI字符串,用来读取对应的实际文件数据。 处理这种数据类型的Content Provider需要实现一个名为_data的字段,该字段列出了该文件在Android系统的实际路 径,客户端可以通过调用方法ContentResolver.

openOutputStream()来处理该URI指向的文件资源。
(6) 查询返回一个Cursor类型对象,所有执行写操作的方法如insert()、update()及delete()都将被监听,可以通过Content
Resolver().notifyChange()来通过监听器关于数据更新的消息。
(7) 在AndroidManifest.xml中使用<provider>标签来设置Content Provider信息,如:android:authorities、android:name
android:permission等。

Demo:
数据库类DatabaseHelper用来存储个人信息,名字(name)对应年龄(age)

[java][/java] view plaincopyprint?

  1. public class DatabaseHelper extends SQLiteOpenHelper {
  2.     private static final String DB_NAME = “personInfo.db”;
  3.     private static final String TB_NAME = “person”;
  4.     private static final int VERSION = 1;
  5.     private static final String creat_cmd = “create table IF NOT EXISTS ” + TB_NAME + ” (_id integer PRIMARY KEY autoincrement, name text, age integer)”;
  6.     private static final String upgrade_cmd = “alert table ” + TB_NAME + ” add sex varchar(8)”;
  7.     public DatabaseHelper(Context context) {
  8.         super(context, DB_NAME, null, VERSION);
  9.         // TODO Auto-generated constructor stub
  10.     }
  11.     @Override
  12.     public void onCreate(SQLiteDatabase db) {
  13.         // TODO Auto-generated method stub
  14.         db.execSQL(creat_cmd);
  15.     }
  16.     @Override
  17.     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  18.         // TODO Auto-generated method stub
  19.         db.execSQL(upgrade_cmd);
  20.     }
  21. }

 

创建自己的ContentProvider

[java][/java] view plaincopyprint?

  1. // 创建一个MyProvider继承于ContentProvider
  2. public class MyProvider extends ContentProvider {
  3.     private DatabaseHelper dbHelper;    // 数据库类
  4.     private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
  5.     private static final int ALL_PERSON = 1;
  6.     private static final int PERSON = 2;
  7.     private static final String TAG = “MyProvider”;
  8.     private static final String TABLE_NAME = “person”;
  9.     // 定义自己的URI
  10.     private static final String AUTHORITY = “com.myAndroid.myProvider”;
  11.     public static final Uri CONTENT_URI = Uri.parse(“content://”+AUTHORITY+”/”+TABLE_NAME);
  12.     static {
  13.         URI_MATCHER.addURI(AUTHORITY, “person”, ALL_PERSON);
  14.         URI_MATCHER.addURI(AUTHORITY, “person/#”, PERSON);
  15.     }
  16.     // ContentProvider的入口,初始化
  17.     @Override
  18.     public boolean onCreate() {
  19.         // TODO Auto-generated method stub
  20.         Log.i(TAG, “—-onCreate—-“);
  21.         dbHelper = new DatabaseHelper(this.getContext());
  22.         return false;
  23.     }
  24.     @Override
  25.     public int delete(Uri arg0, String arg1, String[] arg2) {
  26.         // TODO Auto-generated method stub
  27.         Log.i(TAG, “—-delete—-“);
  28.         SQLiteDatabase db = dbHelper.getWritableDatabase();
  29.         int count = 0;
  30.         switch (URI_MATCHER.match(arg0)) {
  31.         case ALL_PERSON:
  32.             count = db.delete(TABLE_NAME, arg1, arg2);
  33.         case PERSON:
  34.             long id = ContentUris.parseId(arg0);
  35.             String where = “_id=” + id;
  36.             if(arg1 != null && !””.equals(arg1)) {
  37.                 where += ” and ” + arg1;
  38.             }
  39.             count = db.delete(TABLE_NAME, where, arg2);
  40.         default:
  41.             throw new IllegalArgumentException(“Unknown Uri:” + arg0.toString());
  42.         }
  43.         getContext().getContentResolver().notifyChange(arg0, null); // 通知注册客户端数据发生改变
  44.         return count;
  45.     }
  46.     @Override
  47.     public String getType(Uri arg0) {
  48.         // TODO Auto-generated method stub
  49.         Log.i(TAG, “—-getType—-“);
  50.         switch (URI_MATCHER.match(arg0)) {
  51.         case ALL_PERSON:
  52.                 return “com.android.cursor.dir/person”;
  53.         case PERSON:
  54.                 return “com.android.cursor.item/person”;
  55.         default:
  56.                 throw new IllegalArgumentException(“Unknow Uri” + arg0.toString());
  57.         }
  58.     }
  59.     @Override
  60.     public Uri insert(Uri uri, ContentValues arg1) {
  61.         // TODO Auto-generated method stub
  62.         Log.i(TAG, “—-insert—-“);
  63.         SQLiteDatabase db = dbHelper.getWritableDatabase();
  64.         Uri insertUri = null;
  65.         switch (URI_MATCHER.match(uri)) {
  66.         case ALL_PERSON:
  67.             long rowId = db.insert(TABLE_NAME, “name”, arg1);
  68.             Log.d(TAG, “insert:”+arg1.toString()+” Id:”+rowId);
  69.             insertUri = ContentUris.withAppendedId(uri, rowId);
  70. //          this.getContext().getContentResolver().notifyChange(insertUri, null);
  71.             break;
  72.         default:
  73.             throw new IllegalArgumentException(“Unknown Uri:” + uri.toString());
  74.         }
  75.         getContext().getContentResolver().notifyChange(insertUri, null);
  76.         return insertUri;
  77.     }
  78.     // 处理查询,返回Cursor
  79.     @Override
  80.     public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
  81.             String arg4) {
  82.         // TODO Auto-generated method stub
  83.         Log.i(TAG, “—-query—-“);
  84.         SQLiteDatabase db = dbHelper.getReadableDatabase();
  85.         Cursir cursor = null;
  86.         switch (URI_MATCHER.match(arg0)) {
  87.         case ALL_PERSON:                    // query all the person info
  88.             cursor =  db.query(TABLE_NAME, arg1, arg2, arg3, null, null, arg4);
  89.         case PERSON:                        // query only one person info from a given ID
  90.             long id = ContentUris.parseId(arg0);
  91.             String where = ” _id=” + id;
  92.             if( (!””.equals(arg2)) && (arg2 != null)) {
  93.                 where += ” and ” + arg2 ;
  94.             }
  95.             cursor =  db.query(TABLE_NAME, arg1, where, arg3, null, null, arg4);
  96.         default:
  97.             throw new IllegalArgumentException(“unknow uri” + arg0.toString());
  98.         }
  99.         if(cursor !=  null) {
  100.         // 注册该Uri对应的数据发生改变时,向客户端发送通知
  101.             cursor.setNotificationUri(getContext().getContentResolver(), uri);
  102.         }
  103.         return cursor;
  104.     }
  105.     @Override
  106.     public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
  107.         // TODO Auto-generated method stub
  108.         Log.i(TAG, “—-update—-“);
  109.         SQLiteDatabase db = dbHelper.getWritableDatabase();
  110.         int count = 0;
  111.         switch (URI_MATCHER.match(arg0)) {
  112.         case ALL_PERSON:
  113.             count = db.update(TABLE_NAME, arg1, arg2, arg3);
  114.             break;
  115.         case PERSON:
  116.             long id = ContentUris.parseId(arg0);
  117.             String where = “_id=” + id;
  118.             if( (arg2 != null) && (!””.equals(arg2))) {
  119.                 where += ” and ” + arg2;
  120.             }
  121.             count = db.update(TABLE_NAME, arg1, where, arg3);
  122.             break;
  123.         default:
  124.             throw new IllegalArgumentException(“Unknow Uri:”+arg0.toString());
  125.         }
  126.         getContext().getContentResolver().notifyChange(arg0, null);
  127.         return count;
  128.     }
  129. }

客户端使用Content Provider:

[java][/java] view plaincopyprint?

  1. // 定义自己的URI
  2. private static final String TABLE_NAME = “person”;
  3. private static final String AUTHORITY = “com.myAndroid.myProvider”;
  4. public static final Uri CONTENT_URI = Uri.parse(“content://”+AUTHORITY+”/”+TABLE_NAME);
  5. // 插入
  6. ContentResolver contenResolver = MainActivity.this.getContentResolver();
  7. ContentValues values = new ContentValues();
  8. values.put(“name”, “hello”);
  9. values.put(“age”, 25);
  10. Uri resultUri = contentResolver.insert(CONTENT_URI, values);
  11. if(ContentUris.parseId(resultUri) > 0) { Log.i(TAG, “OK”); }
  12. // 查询
  13. String columns[] = new String[] (“_id”, “name”, “age”);
  14. Cursor cursor = contentResolver.query(CONTENT_URI, columns, null, null, “_id”);
  15. if(cursor.moveToFirst()) {
  16.     do {
  17.         Log.i(TAG, “_id:”+cursor.getInt(cursor.getColumnIndex(“_id”)));
  18.         Log.i(TAG, “name:”+cursor.getString(cursor.getColumnIndex(“name”)));
  19.         Log.i(TAG, “age:”+cursor.getInt(cursor.getColumnIndex(“age”)));
  20.     } while(cursor.moveToNext());
  21.     cursor.close();
  22. }
  23. // 删除 ID为1的记录
  24. contentResolver.update(Uri.parse(“content://”+AUTHORITY+”/”+TABLE_NAME+”/1”), null, null);
  25. // 修改ID为 1 的记录
  26. ContentValues values = new ContentValues();
  27. values.put(“name”, “world”);
  28. values.put(“age”,  32);
  29. contentResolver.update(Uri.parse(“content://”+AUTHORITY+”/”+TABLE_NAME+”/1”), values, null, null);

 

最后我们还需要在AndroidManifest.xml中定义我们的provider属性:

[html][/html] view plaincopyprint?

  1. <provider android:name=”.MyProvider” android:authorities=”com.myAndroid.myProvider” />

 

 

标签