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應用程式可以使用檔案或SqlLite資料庫來儲存資料。Content Provider提供了一種多應用間資料共用的方式,比如:連絡人資訊可以被多個應用程式訪問。Content Provider是個實現了一組用於提供其他應用程式存取資料的標準方法的類。
應用程式可以在Content Provider中執行如下操作:
查詢資料
修改資料
添加資料
刪除資料
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/MainActivity.java
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
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
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
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
<?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>