前言:
總結這篇文章之前我們先來回顧一下Android Sqlite資料庫,參考文章:http://www.cnblogs.com/whoislcj/p/5506294.html,Android程式內部資料儲存如果使用Sqlite資料庫,那麼Android 如何實現程式間資料共享?Android 提供了一種機制可以實現程式間的資料共享,它就是Android 四大元件之一ContentProvider,Android為儲存和獲取資料提供統一的介面,用於實現程式間資料共享,不要將其理解為資料庫。
為什麼說是熟悉又陌生呢?因為我們經常使用到,Android內建的許多資料都是採用ContentProvider,比如圖片,視訊,音訊,手機聯絡人等,至於陌生那是因為我很少自己去實現一個ContentProvider,今天我們重點是來實現一個自定義ContentProvider。
ContentProvider類簡介:
1.) 我們一般要繼承ContentProvider,那麼要實現那些函式呢?
- ContentProvider() 建構函式
- onCreate() 建立資料時呼叫的回撥函式
- insert() 插入資料
- delete() 刪除資料
- update() 更新資料
- query() 查詢資料
- getType() 得到資料型別
2.)URI簡介:
ContentProvider通過URI來訪問資料執行增刪改查的操作,一個完整的URI有 content://自定義ContentProvider/xxx資料庫名稱
我們先宣告一個作用域:
//訪問URI作用域 public static final String CONTENT_URI="com.whoislcj.testsqlite.personprovider";
對應URI舉例說明一下:
- content://com.whoislcj.testsqlite.personprovider/person 返回person所以資料
- content://com.whoislcj.testsqlite.personprovider/person/10 返回id為10的person資料
3.)UriMatcher簡介
主要用於匹配Uri,為什麼要匹配Uri呢?通過上面的Uri舉例可以看出操作域不一樣,對於執行一個delete、update、query來說我們要獲取引數引數執行不能對應操作。
使用:
//定義一個UriMatcher類物件,用來匹配Uri的。 private static final UriMatcher uriMatcher; //集合操作 public static final int INCOMING_COLLECTION = 1; //單個ID操作 public static final int INCOMING_SIGNAL = 2; static { //常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼 uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); //如果match()方法匹配com.whoislcj.testsqlite.personprovider/person路徑,返回匹配碼為1 uriMatcher.addURI(CONTENT_URI, "person", INCOMING_COLLECTION);//新增需要匹配uri,如果匹配就會返回匹配碼 //如果match()方法匹配content://com.ljq.provider.personprovider/person/230路徑,返回匹配碼為2 uriMatcher.addURI(CONTENT_URI, "person/#", INCOMING_SIGNAL);//#號為萬用字元 }
4.)ContentUris簡介
ContentUris是對URI的操作類,比如獲取URI路徑裡的引數,或者給URI拼接一個引數
舉例說明:
- long id = ContentUris.parseId(uri);//從uri中獲取id
- Uri rowUri = ContentUris.withAppendedId(uri, rowId);//uri追加id 生成該條資料完整的URI地址
5.)ContentResolver簡介
ContentResolver主要用於為外部程式提供增刪改查的操作函式,也可以註冊觀察者來監聽資料的變化。
6.)自定義ContentProvider具體實現:
public class PersonProvider extends ContentProvider { // DatabaseHelper操作控制程式碼 private DBHelper dbHelper; //訪問URI public static final String CONTENT_URI="com.whoislcj.testsqlite.personprovider"; // 資料集的MIME型別字串則應該以vnd.android.cursor.dir/開頭 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person"; // 單一資料的MIME型別字串應該以vnd.android.cursor.item/開頭 public static final String CONTENT_TYPE_ITME = "vnd.android.cursor.item/person"; //定義一個UriMatcher類物件,用來匹配Uri的。 private static final UriMatcher uriMatcher; //集合操作 public static final int INCOMING_COLLECTION = 1; //單個ID操作 public static final int INCOMING_SIGNAL = 2; static { //常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼 uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); //如果match()方法匹配com.whoislcj.testsqlite.personprovider/person路徑,返回匹配碼為1 uriMatcher.addURI(CONTENT_URI, "person", INCOMING_COLLECTION);//新增需要匹配uri,如果匹配就會返回匹配碼 //如果match()方法匹配content://com.ljq.provider.personprovider/person/230路徑,返回匹配碼為2 uriMatcher.addURI(CONTENT_URI, "person/#", INCOMING_SIGNAL);//#號為萬用字元 } public PersonProvider() { } /** * 回撥函式,在ContentProvider建立的時候,就會執行 * 作用獲取操作使用者的控制程式碼 */ @Override public boolean onCreate() { //這裡會呼叫 DBHelper的建構函式建立一個資料庫; dbHelper = new DBHelper(getContext()); return true; } /** * 執行插入資料函式 * * @param uri * @param values * @return */ @Override public Uri insert(Uri uri, ContentValues values) { //獲取一個可寫的資料庫 SQLiteDatabase db = dbHelper.getWritableDatabase(); //呼叫資料庫的插入操作 也可以自己構造sql語句 執行 db.execSQL();相對比較麻煩 long rowId = db.insert(DBHelper.TABLE_NAME, "", values); //判斷是否插入成功 if (rowId > 0) { Uri rowUri = ContentUris.withAppendedId(uri, rowId);//uri追加id 生成該條資料完整的URI地址 getContext().getContentResolver().notifyChange(uri, null); return rowUri; } throw new SQLException("Failed to insert row" + uri); } /** * 刪除資料操作 * * @param uri * @param selection * @param selectionArgs * @return */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { //獲取一個可寫的資料庫 SQLiteDatabase db = dbHelper.getWritableDatabase(); int count = 0; switch (uriMatcher.match(uri)) { case INCOMING_COLLECTION: //執行刪除操作 count = db.delete(DBHelper.TABLE_NAME, selection, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); break; case INCOMING_SIGNAL: long id = ContentUris.parseId(uri);//從uri中獲取id String where = "id=" + id; // 刪除指定id的記錄 where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : ""; // 把其它條件附加上 count = db.delete(DBHelper.TABLE_NAME, where, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); break; default: throw new SQLException("Failed to delete row " + uri); } //關閉資料庫 db.close(); return count; } /** * 更新資料操作 * * @param uri * @param values * @param selection * @param selectionArgs * @return */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { //獲取一個可寫的資料庫 SQLiteDatabase db = dbHelper.getWritableDatabase(); int count = 0; switch (uriMatcher.match(uri)) { case INCOMING_COLLECTION: //執行更新資料 count = db.update(DBHelper.TABLE_NAME, values, selection, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); break; case INCOMING_SIGNAL: long id = ContentUris.parseId(uri);//從uri中獲取id String where = "id=" + id; // 刪除指定id的記錄 where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它條件附加上 //執行更新資料 count = db.update(DBHelper.TABLE_NAME, values, where, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); break; default: throw new SQLException("Failed to update row " + uri); } //關閉資料庫 db.close(); return count; } /** * 查詢操作 * * @param uri * @param projection * @param selection * @param selectionArgs * @param sortOrder * @return */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { //獲取一個可讀的資料庫 SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = null; switch (uriMatcher.match(uri)) { case INCOMING_COLLECTION: //執行查詢 cursor = db.query(DBHelper.TABLE_NAME, projection, selection, selectionArgs, null, null, sortOrder); break; case INCOMING_SIGNAL: long id = ContentUris.parseId(uri);//從uri中獲取id String where = "id=" + id; // 刪除指定id的記錄 where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : "";// 把其它條件附加上 cursor = db.query(DBHelper.TABLE_NAME, projection, where, selectionArgs, null, null, sortOrder); break; default: throw new SQLException("Failed to query " + uri); } return cursor; } /** * 該方法用於返回當前Url所代表資料的MIME型別。 * * @param uri * @return */ @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case INCOMING_COLLECTION: return CONTENT_TYPE; case INCOMING_SIGNAL: return CONTENT_TYPE_ITME; default: throw new IllegalArgumentException("Unknown URI " + uri); } } }
7.)外部如何訪問
ContentResolver resolver = getContentResolver(); Uri uri = Uri.parse("content://com.whoislcj.testsqlite.personprovider/person"); //新增一條記錄 ContentValues values = new ContentValues(); values.put("name", "whoislcj"); resolver.insert(uri, values); //更新一條資料 ContentValues updateValues = new ContentValues(); updateValues.put("name", "lcj"); //組合 resolver.update(uri, updateValues, "id=?", new String[]{"2"}); //單個 Uri updateIdUri = ContentUris.withAppendedId(uri, 5); resolver.update(updateIdUri, updateValues, null, null); //刪除person表指定資料 Uri deleteIdUri = ContentUris.withAppendedId(uri, 5); resolver.delete(deleteIdUri, null, null); //獲取person表指定資料 Uri tempUri = ContentUris.withAppendedId(uri, 5); Cursor cursor = resolver.query(tempUri, null, null, null, "id asc"); while (cursor.moveToNext()) { Log.e("testContentProvider", "signal id=" + cursor.getInt(0) + ",name=" + cursor.getString(1)); } cursor.close(); //獲取person表中所有記錄 cursor = resolver.query(uri, null, null, null, "id asc"); while (cursor.moveToNext()) { Log.e("testContentProvider", "id=" + cursor.getInt(0) + ",name=" + cursor.getString(1)); } cursor.close();
8.)如何監聽資料變化
需要註冊一個自定義的觀察者,當時如下
// 為uri的資料改變註冊監聽器 getContentResolver().registerContentObserver( Uri.parse("content://com.whoislcj.testsqlite.personprovider/person"), true, new Observer(new Handler())); // 提供方自定義的ContentOberver監聽器 private final class Observer extends ContentObserver { public Observer(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange, Uri uri) { // 查詢傳送郵箱中的短息(處於正在傳送狀態的簡訊放在傳送箱) Log.e("MainActivity", "onChange--->uri :" + uri.toString()); } }
同樣資料操作位置也需要執行如下程式碼
getContext().getContentResolver().notifyChange(uri, null);
9.)訪問許可權控制
宣告讀寫自定義許可權
<permission android:name="com.whoislcj.testsqlite.personprovider.read" /> <permission android:name="com.whoislcj.testsqlite.personprovider.write" /> <uses-permission android:name="com.whoislcj.testsqlite.personprovider.read" /> <uses-permission android:name="com.whoislcj.testsqlite.personprovider.write" />
ContentProvider註冊宣告:
<provider android:name=".PersonProvider" android:authorities="com.whoislcj.testsqlite.personprovider" android:enabled="true" android:exported="true" android:readPermission="com.whoislcj.testsqlite.personprovider.read" android:writePermission="com.whoislcj.testsqlite.personprovider.write"> </provider>
10.)關於getTpye
ContentProvider裡面一個getType ()函式很多人不知道 這個幹嘛的,接下來介紹一下,
// 資料集的MIME型別字串則應該以vnd.android.cursor.dir/開頭 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person"; // 單一資料的MIME型別字串應該以vnd.android.cursor.item/開頭 public static final String CONTENT_TYPE_ITME = "vnd.android.cursor.item/person"; /** * 該方法用於返回當前Url所代表資料的MIME型別。 * * @param uri * @return */ @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case INCOMING_COLLECTION: return CONTENT_TYPE; case INCOMING_SIGNAL: return CONTENT_TYPE_ITME; default: throw new IllegalArgumentException("Unknown URI " + uri); } }
假設我們在專案搞了一個聯絡人列表Activity,我們需要外面來訪問這個Activity,首先看下這個Activity的註冊宣告:
<activity android:name=".TestActivity" android:icon="@mipmap/ic_launcher"> <intent-filter> <action android:name="com.whoislcj.testsqlite.personprovider" /> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="vnd.android.cursor.dir/person" /> </intent-filter> </activity>
看到上面的mimeType:vnd.android.cursor.dir/person
外部如何啟動呢:
Uri uri = Uri.parse("content://com.whoislcj.testsqlite.personprovider/person"); Intent intent = new Intent(); intent.setAction("com.whoislcj.testsqlite.personprovider"); intent.setData(uri); startActivity(intent);
這樣以來系統會去呼叫你定義的ContentProvider中的getType,去匹配出相應的Activity來實現跳轉。