Content Provider 属于Android应用程序的组件之一,作为应用程序之间唯一的共享数据的途径,Content Provider 主要的功能就是存储并检索数据以及向其他应用程序提供访问数据的借口。
Android 系统为一些常见的数据类型(如音乐、视频、图像、手机通信录联系人信息等)内置了一系列的 Content Provider, 这些都位于android.provider包下。持有特定的许可,可以在自己开发的应用程序中访问这些Content Provider。
让自己的数据和其他应用程序共享有两种方式:创建自己的Content Provier(即继承自ContentProvider的子类) 或者是将自己的数据添加到已有的Content Provider中去,后者需要保证现有的Content Provider和自己的数据类型相同且具有该 Content Provider的写入权限。对于Content Provider,最重要的就是数据模型(data model) 和 URI。
1.数据模型
Content Provider 将其存储的数据以数据表的形式提供给访问者,在数据表中每一行为一条记录,每一列为具有特定类型和意义的数据。每一条数据记录都包括一个 "_ID" 数值字段,改字段唯一标识一条数据。
2.URI
URI,每一个Content Provider 都对外提供一个能够唯一标识自己数据集(data set)的公开URI, 如果一个Content Provider管理多个数据集,其将会为每个数据集分配一个独立的URI。所有的Content Provider 的URI 都以"content://" 开头,其中"content:"是用来标识数据是由Content Provider管理的 schema。
在几乎所有的Content Provider 的操作中都会用到URI,因此一般来讲,如果是自己开发的Content Provider,最好将URI定义为常量,这样在简化开发的同时也提高了代码的可维护性。
首先来介绍如何访问Content Provider中的数据,访问 Content Provider中的数据主要通过ContentResolver对象,ContentResolver类提供了成员方法可以用来对Content Provider 中的数据进行查询、插入、修改和删除等操作。 以查询为例,查询一个 Content Provider 需要掌握如下的信息。
唯一标识Content Provider 的URI
需要访问的数据字段名称。
该数据字段的数据类型
提示: 如果需要访问特定的某条数据记录,只需该记录的ID 即可。
查询Content Provider的方法有两个:ContentResolver的query() 和 Activity 对象的 managedQuery(),二者接收的参数均相同,返回的都是Cursor 对象,唯一不同的是 使用managedQuery 方法可以让Activity 来管理 Cursor 的生命周期。
被管理的Cursor 会在 Activity进入暂停状态的时候调用自己的 deactivate 方法自行卸载,而在Activity回到运行状态时会调用自己的requery 方法重新查询生成的Cursor对象。如果一个未被管理的Cursor对象想被Activity管理,可以调用Activity的 startManagingCursor方法来实现。
代码
代码
代码
代码
代码
Android 系统为一些常见的数据类型(如音乐、视频、图像、手机通信录联系人信息等)内置了一系列的 Content Provider, 这些都位于android.provider包下。持有特定的许可,可以在自己开发的应用程序中访问这些Content Provider。
让自己的数据和其他应用程序共享有两种方式:创建自己的Content Provier(即继承自ContentProvider的子类) 或者是将自己的数据添加到已有的Content Provider中去,后者需要保证现有的Content Provider和自己的数据类型相同且具有该 Content Provider的写入权限。对于Content Provider,最重要的就是数据模型(data model) 和 URI。
1.数据模型
Content Provider 将其存储的数据以数据表的形式提供给访问者,在数据表中每一行为一条记录,每一列为具有特定类型和意义的数据。每一条数据记录都包括一个 "_ID" 数值字段,改字段唯一标识一条数据。
2.URI
URI,每一个Content Provider 都对外提供一个能够唯一标识自己数据集(data set)的公开URI, 如果一个Content Provider管理多个数据集,其将会为每个数据集分配一个独立的URI。所有的Content Provider 的URI 都以"content://" 开头,其中"content:"是用来标识数据是由Content Provider管理的 schema。
在几乎所有的Content Provider 的操作中都会用到URI,因此一般来讲,如果是自己开发的Content Provider,最好将URI定义为常量,这样在简化开发的同时也提高了代码的可维护性。
首先来介绍如何访问Content Provider中的数据,访问 Content Provider中的数据主要通过ContentResolver对象,ContentResolver类提供了成员方法可以用来对Content Provider 中的数据进行查询、插入、修改和删除等操作。 以查询为例,查询一个 Content Provider 需要掌握如下的信息。
唯一标识Content Provider 的URI
需要访问的数据字段名称。
该数据字段的数据类型
提示: 如果需要访问特定的某条数据记录,只需该记录的ID 即可。
查询Content Provider的方法有两个:ContentResolver的query() 和 Activity 对象的 managedQuery(),二者接收的参数均相同,返回的都是Cursor 对象,唯一不同的是 使用managedQuery 方法可以让Activity 来管理 Cursor 的生命周期。
被管理的Cursor 会在 Activity进入暂停状态的时候调用自己的 deactivate 方法自行卸载,而在Activity回到运行状态时会调用自己的requery 方法重新查询生成的Cursor对象。如果一个未被管理的Cursor对象想被Activity管理,可以调用Activity的 startManagingCursor方法来实现。
Android应用程序可以使用文件或SqlLite数据库来存储数据。Content Provider提供了一种多应用间数据共享的方式,比如:联系人信息可以被多个应用程序访问。Content Provider是个实现了一组用于提供其他应用程序存取数据的标准方法的类。
应用程序可以在Content Provider中执行如下操作:
查询数据
修改数据
添加数据
删除数据
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/MainActivity.java
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
package com.amaker.ch10.app; import android.app.Activity; import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.util.Log; import com.amaker.ch10.app.Employees.Employee; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); // 添加 insert(); // 查询 query(); // 更新 update(); // 查询 query(); // 删除 del(); // 查询 query(); } // 删除方法 private void del(){ // 删除ID为1的记录 Uri uri = ContentUris.withAppendedId(Employee.CONTENT_URI, 1 ); // 获得ContentResolver,并删除 getContentResolver().delete(uri, null , null ); } // 更新 private void update(){ // 更新ID为1的记录 Uri uri = ContentUris.withAppendedId(Employee.CONTENT_URI, 1 ); ContentValues values = new ContentValues(); // 添加员工信息 values.put(Employee.NAME, " hz.guo " ); values.put(Employee.GENDER, " male " ); values.put(Employee.AGE, 31 ); // 获得ContentResolver,并更新 getContentResolver().update(uri, values, null , null ); } // 查询 private void query(){ // 查询列数组 String[] PROJECTION = new String[] { Employee._ID, // 0 Employee.NAME, // 1 Employee.GENDER, // 2 Employee.AGE // 3 }; // 查询所有备忘录信息 Cursor c = managedQuery(Employee.CONTENT_URI, PROJECTION, null , null , Employee.DEFAULT_SORT_ORDER); // 判断游标是否为空 if (c.moveToFirst()) { // 遍历游标 for ( int i = 0 ; i < c.getCount(); i ++ ) { c.moveToPosition(i); // 获得姓名 String name = c.getString( 1 ); String gender = c.getString( 2 ); int age = c.getInt( 3 ); // 输出日志 Log.i( " emp " , name + " : " + gender + " : " + age); } } } // 插入 private void insert(){ // 声明Uri Uri uri = Employee.CONTENT_URI; // 实例化ContentValues ContentValues values = new ContentValues(); // 添加员工信息 values.put(Employee.NAME, " amaker " ); values.put(Employee.GENDER, " male " ); values.put(Employee.AGE, 30 ); // 获得ContentResolver,并插入 getContentResolver().insert(uri, values); } }
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/Employees.java
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
package com.amaker.ch10.app; import android.net.Uri; import android.provider.BaseColumns; /** * 通讯录常量类 */ public final class Employees { // 授权常量 public static final String AUTHORITY = " com.amaker.provider.Employees " ; private Employees() {} // 内部类 public static final class Employee implements BaseColumns { // 构造方法 private Employee() {} // 访问Uri public static final Uri CONTENT_URI = Uri.parse( " content:// " + AUTHORITY + " /employee " ); // 内容类型 public static final String CONTENT_TYPE = " vnd.android.cursor.dir/vnd.amaker.employees " ; public static final String CONTENT_ITEM_TYPE = " vnd.android.cursor.item/vnd.amaker.employees " ; // 默认排序常量 public static final String DEFAULT_SORT_ORDER = " name DESC " ; // 按姓名排序 // 表字段常量 public static final String NAME = " name " ; // 姓名 public static final String GENDER = " gender " ; // 性别 public static final String AGE = " age " ; // 年龄 } }
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/EmployeeProvider.java
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
package com.amaker.ch10.app; import java.util.HashMap; 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.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.text.TextUtils; import com.amaker.ch10.app.Employees.Employee; public class EmployeeProvider extends ContentProvider{ // 数据库帮助类 private DBHelper dbHelper; // Uri工具类 private static final UriMatcher sUriMatcher; // 查询、更新条件 private static final int EMPLOYEE = 1 ; private static final int EMPLOYEE_ID = 2 ; // 查询列集合 private static HashMap < String, String > empProjectionMap; static { // Uri匹配工具类 sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); sUriMatcher.addURI(Employees.AUTHORITY, " employee " , EMPLOYEE); sUriMatcher.addURI(Employees.AUTHORITY, " employee/# " , EMPLOYEE_ID); // 实例化查询列集合 empProjectionMap = new HashMap < String, String > (); // 添加查询列 empProjectionMap.put(Employee._ID, Employee._ID); empProjectionMap.put(Employee.NAME, Employee.NAME); empProjectionMap.put(Employee.GENDER, Employee.GENDER); empProjectionMap.put(Employee.AGE, Employee.AGE); } // 创建是调用 public boolean onCreate() { // 实例化数据库帮助类 dbHelper = new DBHelper(getContext()); return true ; } // 添加方法 public Uri insert(Uri uri, ContentValues values) { // 获得数据库实例 SQLiteDatabase db = dbHelper.getWritableDatabase(); // 插入数据,返回行ID long rowId = db.insert(DBHelper.EMPLOYEES_TABLE_NAME, Employee.NAME, values); // 如果插入成功返回uri if (rowId > 0 ) { Uri empUri = ContentUris.withAppendedId(Employee.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(empUri, null ); return empUri; } return null ; } // 删除方法 public int delete(Uri uri, String selection, String[] selectionArgs) { // 获得数据库实例 SQLiteDatabase db = dbHelper.getWritableDatabase(); // 获得数据库实例 int count; switch (sUriMatcher.match(uri)) { // 根据指定条件删除 case EMPLOYEE: count = db.delete(DBHelper.EMPLOYEES_TABLE_NAME, selection, selectionArgs); break ; // 根据指定条件和ID删除 case EMPLOYEE_ID: String noteId = uri.getPathSegments().get( 1 ); count = db.delete(DBHelper.EMPLOYEES_TABLE_NAME, Employee._ID + " = " + noteId + ( ! TextUtils.isEmpty(selection) ? " AND ( " + selection + ' ) ' : "" ), selectionArgs); break ; default : throw new IllegalArgumentException( " 错误的 URI " + uri); } getContext().getContentResolver().notifyChange(uri, null ); return count; } // 获得类型 public String getType(Uri uri) { return null ; } // 查询方法 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); switch (sUriMatcher.match(uri)) { // 查询所有 case EMPLOYEE: qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME); qb.setProjectionMap(empProjectionMap); break ; // 根据ID查询 case EMPLOYEE_ID: qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME); qb.setProjectionMap(empProjectionMap); qb.appendWhere(Employee._ID + " = " + uri.getPathSegments().get( 1 )); break ; default : throw new IllegalArgumentException( " Uri错误! " + uri); } // 使用默认排序 String orderBy; if (TextUtils.isEmpty(sortOrder)) { orderBy = Employee.DEFAULT_SORT_ORDER; } else { orderBy = sortOrder; } // 获得数据库实例 SQLiteDatabase db = dbHelper.getReadableDatabase(); // 返回游标集合 Cursor c = qb.query(db, projection, selection, selectionArgs, null , null , orderBy); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } // 更新方法 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // 获得数据库实例 SQLiteDatabase db = dbHelper.getWritableDatabase(); int count; switch (sUriMatcher.match(uri)) { // 根据指定条件更新 case EMPLOYEE: count = db.update(DBHelper.EMPLOYEES_TABLE_NAME, values, selection, selectionArgs); break ; // 根据指定条件和ID更新 case EMPLOYEE_ID: String noteId = uri.getPathSegments().get( 1 ); count = db.update(DBHelper.EMPLOYEES_TABLE_NAME, values, Employee._ID + " = " + noteId + ( ! TextUtils.isEmpty(selection) ? " AND ( " + selection + ' ) ' : "" ), selectionArgs); break ; default : throw new IllegalArgumentException( " 错误的 URI " + uri); } getContext().getContentResolver().notifyChange(uri, null ); return count; } }
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/DBHelper.java
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
package com.amaker.ch10.app; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import com.amaker.ch10.app.Employees.Employee; /** * * 数据库工具类 */ public class DBHelper extends SQLiteOpenHelper{ // 数据库名称常量 private static final String DATABASE_NAME = " Employees.db " ; // 数据库版本常量 private static final int DATABASE_VERSION = 1 ; // 表名称常量 public static final String EMPLOYEES_TABLE_NAME = " employee " ; // 构造方法 public DBHelper(Context context) { // 创建数据库 super (context, DATABASE_NAME, null , DATABASE_VERSION); } // 创建时调用 public void onCreate(SQLiteDatabase db) { db.execSQL( " CREATE TABLE " + EMPLOYEES_TABLE_NAME + " ( " + Employee._ID + " INTEGER PRIMARY KEY, " + Employee.NAME + " TEXT, " + Employee.GENDER + " TEXT, " + Employee.AGE + " INTEGER " + " ); " ); } // 版本更新时调用 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 删除表 db.execSQL( " DROP TABLE IF EXISTS employee " ); onCreate(db); } }
/Chapter10_ContentProvider_01_Test02/AndroidManifest.xml
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
<? xml version="1.0" encoding="utf-8" ?> < manifest xmlns:android ="http://schemas.android.com/apk/res/android" package ="com.amaker.ch10.app" android:versionCode ="1" android:versionName ="1.0" > < application android:icon ="@drawable/icon" android:label ="@string/app_name" > < provider android:name ="EmployeeProvider" android:authorities ="com.amaker.provider.Employees" /> < 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 > </ application > < uses-sdk android:minSdkVersion ="3" /> </ manifest >