相信大家對于 Android 頂頂大名的四大組件也不陌生了。今天我們來談一談 ContentProvider 的用法。什么東西都是基本最重要啊,可以人是一個健忘的動物,所以需要做下來筆記,常看常新。愿我們成為真實的自己。
ContentProvider概要
從系統提供的Provider訪問數據
- 內容URI的組成
- ContentResolve類
創建自己的Provider
- UriMater類
- 自定義一個Provider的步驟
ContentProvider 也有存儲數據的功能,但是與安卓自帶的數據庫和 SharedPreferences 以及文件存儲方法不同的是,后者保存下的數據只能被該應用程序使用,而前者可以讓不同應用程序之間進行數據共享,它還可以選擇只對哪一部分數據進行共享,從而保證程序中的隱私數據不會有泄漏風險。所以組件ContentProvider主要負責存儲和共享數據。
ContentProvider有兩種形式:可以使用現有的內容提供者來讀取和操作相應程序中的數據,也可以創建自己的內容提供者給這個程序的數據提供外部訪問接口。
2.從系統提供的Provider訪問數據
既然ContentProvider有對外共享數據的功能,換句話說,其他應用程序可以通過ContentProvider對應用中的數據進行增刪改查,之前學習SQLite數據存儲的時候就提到過可以實現增刪改查的各種輔助性方法,實際上ContentProvider是對SQLiteOpenHelper的進一步封裝,不過不再用單純的表名指明被操作的表,畢竟現在是其他程序訪問它,而是用有一定格式規范的內容URI來代替。下面先來學習URI的組成。
(1)URI的組成
以之前學習數據庫的demo為例,它的包名是com.example.myapplication,如果其他程序想訪問該程序student.db中的student表,那么需要的內容URI如圖所示:
可以看出內容 URI 可以非常清楚地表達出我們想要訪問哪個程序中哪張表里的數據,但還沒完,還需要將它解析成 Uri 對象才可以作為參數傳入。通過調用 Uri.parse()方法,就可以將內容 URI 字符串解析成 Uri 對象了,代碼如下:
Uri 這個類就是專門做這種鏈接轉換的。其實這種鏈接方式和 http 很像。content:是不能變化的,后面就是包名.provider ,也是一定的,最后就是表名。
(2)ContentResolve類
現在有了酷似“表名”的Uri,類似的,在ContentResolver類中提供的一系列用于對數據進行增刪改查操作的方法也酷似SQLiteDatabase的那些輔助性方法:insert()方法用于添加數據,update()方法用于更新數據,delete()方法用于刪除數據,query()方法用于查詢數據。它們不僅方法名一樣,連提供的參數都非常相似,見下圖,紅色部分是區別:
所以其他程序若想要訪問ContentProvider中共享的數據的方法是:
第一:通過 Context 中的 getContentResolver() 方法實例化一個 ContentResolver 對象。
第二:調用該對象的增刪改查方法去操作 ContentProvider 中的數據。
下面我們來看看查詢聯系人數據的基本代碼:
別忘記在注冊文件中聲明權限
不但如此,Android 6.0 之后有了運行時權限,所以我們還要加上判斷才可以。
這一部分是通用代碼,大家可以在使用的過程中把這部分代碼封裝到 BaseActivity 中去。這樣可以增加代碼復用。
這是需要動態申請權限的圖標,不過沒關系,大家記不得可以去查找一下即可。
3.創建自己的Provider
(1)UriMater類
UriMater 類有匹配內容 URI 的功能,在這里常用它的兩個方法:一個是 addURI() 方法來傳入 URI,它接收三個參數(權限,路徑,一個自定義代碼);另一個是 match() 方法用來匹配 URI,接收一個 Uri 對象,返回值是某個能夠匹配這個 Uri 對象所對應的自定義代碼,利用這個自定義代碼,就可以判斷出調用方期望訪問的是哪張表中的數據了。
(2)自定義一個Provider的步驟
步驟一:新建一個類去繼承ContentProvider。
步驟二:重寫ContentProvider的六個抽象方法,方法及含義如圖:
public class MyProvider extends ContentProvider {
// 在ContentProvider 創建后調用
@Override
public boolean onCreate() {
return false;
}
//該方法用于提供外部應用從 ContentProvider 中獲取數據
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override //用于返回當前 Uri 代表的 MIME 類型
public String getType(@NonNull Uri uri) {
return null;
}
//該方法用于提供外部應用從 ContentProvider 中插入數據
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
//該方法用于提供外部應用從 ContentProvider 中刪除數據
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
//該方法用于提供外部應用從 ContentProvider 中修改數據
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
步驟三:在配置文件中進行注冊,并注明屬性:
android:authorities即Provider的權限,形式是包名.provider
android:name即Provider的全名,形式是包名.類名
android:exported="true"指明該Provider可被其它程序訪問。
(3)例子:為student.db創建MyProvider
接下里還是給上篇的數據庫demo創建一個自定義提供器MyProvider,然后在別的應用程序中通過MyProvider去操作student.db中的數據。
開始自定義提供器!一開始定義了四個常量,分別表示訪問student表中的所有數據、訪問student表中的單條數據(student/#用于表示student表中任意一行記錄)、訪問course表中的所有數據和訪問course表中的單條數據。然后在靜態代碼塊里對UriMatcher進行了初始化操作,將期望匹配的幾種URI格式添加了進去。
接下來就是六個抽象方法的具體實現了,先看onCreate()方法,這里創建了一個MyHelper的實例,然后返回true表示內容提供器初始化成功,現在數據庫就已經完成了創建或升級操作。
接下來是 getType()方法,需要返回一個MIME字符串。一個內容URI所對應的MIME字符串主要由三部分組分,Android對這三個部分做了以下格式規定:必須以vnd開頭;如果內容URI以路徑結尾,則后接android.cursor.dir/,如果內容URI以id結尾,則后接android.cursor.item/;最后接上vnd.< authority>.< path>。所以四個內容URI對應的MIME字符串分別是:
在query()方法里先獲取到SQLiteDatabase的實例,然后根據傳入的Uri參數判斷出用戶想要訪問哪張表,再調用SQLiteDatabase的query()進行查詢并將Cursor對象返回就好了。注意當訪問的是單條數據時調用了Uri對象的getPathSegments()方法,它會將內容URI權限之后的部分以“/”符號進行分割,并把分割后的結果放入到一個字符串列表中,那這個列表的第0個位置存放的就是路徑,第1個位置存放的就是id了。得到了id之后,再通過selection和selectionArgs參數進行約束,就實現了查詢單條數據的功能。
補充部分:事務有關:
事務是恢復和并發控制的基本單位。
事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為ACID特性。
原子性(atomicity)。一個事務是一個不可分割的工作單位,事務中包括的諸操作要么都做,要么都不做。
一致性(consistency)。事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation)。一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對并發的其他事務是隔離的,并發執行的各個事務之間不能互相干擾。
持久性(durability)。持續性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。
/**
* SQLite的事務管理
*/
public void transation() {
SQLiteDatabase db = helper.getWritableDatabase();
// 事務開始
db.beginTransaction();
//事務的處理
try {
db.execSQL("update person set age = age-1 where name = 'xiaohong'");
db.execSQL("update person set age = age+1 where name = 'xiaolan'");
//事務處理成功
db.setTransactionSuccessful();
} finally {
//結束事務
db.endTransaction();
}
}
當使用 sqlite 數據庫批量插入數據的時候使用事務就可以提升效率。
在使用 sqlite 數據庫的時候有那些可以優化的地方。
一:顯示使用事務
Android中,無論是使用SQLiteDatabase的insert,delete等方法還是execSQL都開啟了事務,來確保每一次操作都具有原子性,使得結果要么是操作之后的正確結果,要么是操作之前的結果。所以我們可以顯示的支持事務,這樣事務的操作打開次數會明顯變少。
二:建立索引
創建索引的基本語法:
CREATE INDEX index_name ON table_name;
三:及時關閉Cursor
四:耗時異步化
數據庫的操作,屬于本地IO,通常比較耗時,如果處理不好,很容易導致ANR,因此建議將這些耗時操作放入異步線程中處理。
今天狀態不夠好,前途漫漫不知路在何方。只能一步一步向前走。
愿我們成為真實的自己。