ContentProvider
ContentProvider作為四大組件之一,作用是IPC(跨進程通信),底層實現(xiàn)是Binder。
Android提供了ContentProvider,一個程序可以通過實現(xiàn)一個Content provider的抽象接口將自己的數(shù)據(jù)完全暴露出去,而且Content providers是以類似數(shù)據(jù)庫中表的方式將數(shù)據(jù)暴露战得。
ContentProvider中的URI
URI:是網(wǎng)絡資源的定義
Authority:授權(quán)信息,用以區(qū)別不同的ContentProvider将饺;
Path:表名贡避,用以區(qū)分ContentProvider中不同的數(shù)據(jù)表;
Id:Id號予弧,用以區(qū)別表中的不同數(shù)據(jù)刮吧;
自定義ContentProvider必須要實現(xiàn)6個方法:
public class MyContentProvider extends ContentProvider{
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
ContentResolver
Content providers是以類似數(shù)據(jù)庫中表的方式將數(shù)據(jù)暴露出去,那么ContentResolver也將采用類似數(shù)據(jù)庫的操作來從Content providers中獲取數(shù)據(jù)掖蛤。
這里常使用到query操作:
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
uri :查詢地址 (Uri)
projection :查詢的數(shù)據(jù)字段名稱 (String[] )
selection :查詢的條件 (String)
selectionArgs :查詢條件的參數(shù) (String[])
sortOrder :排序 (String)
ContentProvider杀捻、ContentResolver、ContentObserver 之間的關系
ContentProvider——內(nèi)容提供者蚓庭, 在android中的作用是對外共享數(shù)據(jù)致讥,也就是說你可以通過ContentProvider把應用中的數(shù)據(jù)共享給其他應用訪問,其他應用可以通過ContentProvider 對你應用中的數(shù)據(jù)進行添刪改查器赞。
ContentResolver——內(nèi)容解析者垢袱, 其作用是按照一定規(guī)則訪問內(nèi)容提供者的數(shù)據(jù)(其實就是調(diào)用內(nèi)容提供者自定義的接口來操作它的數(shù)據(jù))。
ContentObserver——內(nèi)容觀察者港柜,目的是觀察(捕捉)特定Uri引起的數(shù)據(jù)庫的變化请契,繼而做一些相應的處理,它類似于數(shù)據(jù)庫技術中的觸發(fā)器(Trigger)夏醉,當ContentObserver所觀察的Uri發(fā)生變化時爽锥,便會觸發(fā)它。
使用案例:獲取手機聯(lián)系人和短信
1.首先需要添加權(quán)限
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
2.需要詢問權(quán)限并同意
requestPermissions( arrayOf(Manifest.permission.READ_CONTACTS, Manifest.permission.READ_SMS), REQUEST_CODE)
查詢聯(lián)系人和短信的管理類:
object ExtendsContentProvider {
@SuppressLint("Range")
fun readMessages(context: Context) {
//短信表的路徑
var uri = Uri.parse("content://sms")
//需要查詢的字段
var projection = arrayOf("address", "date", "body")
var cursor = context.contentResolver.query(uri, projection, null, null, null)
cursor?.let {
while (it.moveToNext()) {
Log.i(
"minfo", it.getString(it.getColumnIndex("address"))
+ it.getString(it.getColumnIndex("date"))
+ it.getString(it.getColumnIndex("body"))
)
}
it.close()
}
}
@SuppressLint("Range")
fun readContacts(context: Context) {
//ContactsContract.CommonDataKinds.Phone 聯(lián)系人表
var cursor: Cursor? = context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, null, null, null)
cursor?.let {
while (it.moveToNext()) {
//讀取通訊錄的姓名
var name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
//讀取通訊錄的號碼
var number = cursor.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
Log.i("minfo", "$name--$number")
}
}
}
/**
* 模糊查詢聯(lián)系人
*/
@SuppressLint("Range")
fun getContactsFuzzyQueryByKey(context: Context, key: String) {
//ContactsContract.CommonDataKinds.Phone 聯(lián)系人表
val projection = arrayOf(
ContactsContract.PhoneLookup.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
)
val selection = StringBuilder()
selection.append(ContactsContract.Contacts.DISPLAY_NAME)
selection.append(" LIKE '%$key%' ")
var cursor: Cursor? = context.contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection, selection.toString(), null, null)
cursor?.let {
while (it.moveToNext()) {
//讀取通訊錄的姓名
var name = it.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
//讀取通訊錄的號碼
var number = cursor.getString(it.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
Log.i("minfo", "$name--$number")
}
it.close()
}
}
/**
* 模糊查詢短信
*/
@SuppressLint("Range")
fun getSmsFuzzyQueryByKey(context: Context, key: String) {
//短信表的路徑
var uri = Uri.parse("content://sms")
//需要查詢的字段
var projection = arrayOf(Telephony.Sms.ADDRESS, Telephony.Sms.BODY)
val selection = java.lang.StringBuilder()
selection.append(Telephony.Sms.BODY)
selection.append(" LIKE '%$key%' ")
var cursor = context.contentResolver.query(uri, projection, selection.toString(), null, null)
cursor?.let {
while (it.moveToNext()) {
var number = it.getString(it.getColumnIndex(Telephony.Sms.ADDRESS))
var content = it.getString(it.getColumnIndex(Telephony.Sms.BODY))
Log.i("minfo", "$content--$number")
}
it.close()
}
}
}
contentProvider的初始化流程
Application的創(chuàng)建和執(zhí)行onCreate畔柔,來看看這段代碼
android.app.ActivityThread
......
Application app;
......
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
2.創(chuàng)建Application(內(nèi)部也是反射)
app = data.info.makeApplication(data.restrictedBackupMode, null);
......
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
本文關注點:初始化ContentProvider
installContentProviders(app, data.providers);
}
}
try {
3.初始化Application
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}finally {
......
}
}
......
}
可以看到氯夷,執(zhí)行初始化ContentProvider的時機在makeApplication和callApplicationOnCreate直接,那最起碼可以得出一個結(jié)論:ContentProvider初始化的時機在Application的onCreate之前靶擦。
想要弄明白ContentProvider初始化做了什么腮考,那就需要去installContentProviders方法看一看了。
android.app.ActivityThread
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
遍歷ProviderInfo列表
for (ProviderInfo cpi : providers) {
if (DEBUG_PROVIDER) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
}
各自啟動
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
啟動后放入數(shù)組中
results.add(cph);
}
}
try {
將數(shù)組傳給AMS
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
installContentProviders完成了通過installProvider方法完成ContentProvider的啟動玄捕,并且將啟動了的ContentProvider放在了數(shù)組中踩蔚,傳遞給了AMS,AMS內(nèi)部會將他們存起來桩盲,這樣外部調(diào)用者就可以直接從AMS中獲取ContentProvider了。