距離上一次寫部落格已經半年多了,這半年發生了很多事情,也有了很多感觸,最主要是改變了忙碌了工作,更加重視身體的健康,為此也把工作地點從深圳這個一線城市換到了珠海,工作相對沒有那麼累,身體感覺也好了很多。所以在工作完成之餘,也有了更多的時間來自我學習和提高,後續會用更多時間來寫更多實用的東西,幫助我們理解安卓系統的原理,幫助我們快速、穩定的開發。 這一篇我們接著之前的計劃,完成四大元件的最後一個ContentProvider的分析。ContentProvider是一個抽象類,用來提供訪問資料的統一格式的介面。ContentProvider的作用是多應用間共享資料,如果使用者需要直接使用則可以直接在裡面使用資料庫來儲存資料,也可以通用ContentResolver使用URI來儲存資料,使用URI使用者不需要知道內部怎麼儲存只需要知道如何使用該儲存方式。
ContentProvider
ContentProvider的描述及使用:
在之前我們分析過ContentProvider的啟動比Application的啟動早,所以使用時需要知道這種情況。在使用ContentResolver時是通過URI來訪問的,URI的結構:content://cn.codemx.myprovider/item/123,我們劃分一下:
[ 1 ][ 2 ][ 3 ][ 4 ]
[content://][cn.codemx.myprovider.settings][/item][/123]
複製程式碼
- 第一個元件:是一個協議名稱,它的值固定為:“content://”,是Content Provider元件的專用訪問協議
- 第二個元件:是一個Content Provider元件的android:authority屬性值。這個元件類似於URL中的域名,因此我們要保證它是全域性唯一的,一般使用它所描述的ContentProvider元件的包名來命名。
- 第三個元件:是一個資源相對路徑,用來描述要訪問的資源型別。如果一個ContentProvider只有一種資源,那麼忽略這個元件,否則通過它來指定要訪問的資源的型別。
- 第四個元件:是一個資源ID,用來描述具體的資源。
舉個例子:
<provider
android:name="com.android.launcher3.LauncherProvider"
android:authorities="cn.codemx.myprovider.settings"
android:exported="true"
android:readPermission="cn.codemx.myprovider.permission.READ_SETTINGS"
android:writePermission="cn.codemx.myprovider.permission.WRITE_SETTINGS"/>
複製程式碼
我有個LauncherProvider繼承ContentProvider,在AndroidManifest.xml中要像上面一樣宣告,其中authorities是位置認證,在使用時是這樣使用的:
public static final String TABLE_NAME = "favorites";
/**
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI = Uri.parse("content://" +
ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
複製程式碼
其中ProviderConfig.AUTHORITY為:
public class ProviderConfig {
public static final String AUTHORITY = "cn.codemx.myprovider.settings".intern();
}
複製程式碼
這個就是上面AndroidManifest.xml中宣告的那個authorities屬性值。上面獲取了Uri就可以通過Uri來獲取或者儲存資料了。
ContentProvider啟動過程:
- Activity通過MyContentProvider.CONTENT_URI來訪問MyContentProvider元件,以便可以獲得它的內容
- Activity元件所執行在的應用程式程式發現它裡面不存在一個用來訪問MyContentProvider元件的代理物件,於是通過MyContentProvider.CONTENT_URI來請求AMS返回一個用來訪問MyContentProvider元件的代理物件
- AMS發現MyContentProvider還沒有起來,於是先建立一個新的應用程式,然後在這個新建立的應用程式中啟動MyContentProvider元件
- MyContentProvider元件啟動之後,就會將自己釋出到AMS中,以便AMS可以將它的一個代理物件返回Activity元件。
ContentProvider方法:
onCreate() // 執行初始化工作;
insert(Uri, ContentValues) // 插入新資料;
delete(Uri, String, String[]) // 刪除已有資料;
update(Uri, ContentValues, String, String[]) // 更新資料;
query(Uri, String[], String, String[], String) // 查詢資料;
getType(Uri) // 獲取資料MIME型別。
複製程式碼
ContentProvider不像Activity一樣有生命週期,只有一個onCreate方法用來建立。然後就是提供了增、刪、改、查用來運算元據的介面,通過Uri來管理資料。
ContentResolver的獲取:
上面我們提到ContentProvider主要是提供介面,並沒有具體實現,如果需要具體實現,一種是通過運算元據庫,一種是通過URI操作ContentResolver,資料庫的這裡不再介紹,只介紹一下ContentResolver。
通常我們獲取ContentResolver的程式碼如下:
ContentResolver cr = context.getContentResolver(); //獲取ContentResolver
複製程式碼
前面Android系統原始碼分析--Context章節我們分析過Context的子類及繼承關係,知道,Context的所有實現都是在ContextImp中,所以context.getContentResolver()方法的實現也是一樣。
ContextImpl.getContentResolver():
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
複製程式碼
mContentResolver是定義在ContextImpl中型別為ApplicationContentResolver的變數:
private final ApplicationContentResolver mContentResolver;
複製程式碼
這裡我們可以知道我們獲取到的ContentResolver實際上是ApplicationContentResolver,所以具體操作應該都是在ApplicationContentResolver中實現的。我們先看看這個mContentResolver是在哪裡初始化的。
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,
Display display, Configuration overrideConfiguration, int createDisplayWithId) {
...
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}
複製程式碼
上面程式碼可以知道這個是在ContextImpl建構函式中初始化的。而ContentResolver中的所有操作都在ApplicationContentResolver實現。
ContentResolver.query
先看時序圖:
1.ContentResolver.query
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
// 返回的是ContentProvider的中的mTransport(Transport)
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
...
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
// 返回的是ContentProvider的中的mTransport(Transport)
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
...
// Wrap the cursor object into CursorWrapperInner object.
final IContentProvider provider = (stableProvider != null) ? stableProvider
: acquireProvider(uri);
final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
...
return null;
} finally {
// 釋放資源
...
}
}
複製程式碼
首先是判斷Uri是否是空,如果空丟擲異常,如果不是獲取unstableProvider,從名字看是不穩定的Provider,往下看還有個stableProvider(穩定的Provider),其實從程式碼看沒有區別,只是第一次獲取是不穩定的,出現異常就再次獲取就是穩定的。所以我們只看一個即可。從註釋可以知道unstableProvider其實是ContentProvider中的Transport物件mTransport,這個我們先提出來,後面通過程式碼看是不是對的。
2.ContentResolver.acquireUnstableProvider
public final IContentProvider acquireUnstableProvider(Uri uri) {
// uri以"content"開頭
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
// 獲取Manifest檔案中的authorities屬性(例如:cn.codemx.myprovider.settings)
String auth = uri.getAuthority();
if (auth != null) {
// 具體實現ApplicationContentResolver中
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
複製程式碼
這裡主要是驗證協議名是不是content,然後驗證屬性是不是存在,如果存在則開始獲取實現IContentProvider介面的物件。
3.ApplicationContentResolver.acquireUnstableProvider
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
複製程式碼
這裡呼叫的是ActivityThread.acquireProvider方法。
4.ActivityThread.acquireProvider
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// 返回的是ContentProvider的中的mTransport(Transport)
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {// 如果不為空,說明訪問過,如果是第一次訪問,為空
return provider;
}
...
IActivityManager.ContentProviderHolder holder = null;
try {
// 核心部分
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
// Install provider will increment the reference count for us, and break
// any ties in the race.
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
複製程式碼
先根據auth獲取是否存在實現IContentProvider的類,如果存在直接返回,不存在則獲取IActivityManager.ContentProviderHolder物件,然後獲取實現IContentProvider介面的類。我們先看acquireExistingProvider方法。
5.ActivityThread.acquireExistingProvider
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
final ProviderKey key = new ProviderKey(auth, userId);
// mProviderMap儲存當前應用程式程式訪問過的ContentProvider元件代理物件
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {// 如果不存在說明還沒有訪問過
return null;
}
// 返回的是ContentProvider的中的mTransport(Transport)
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
// 檢查執行緒是否還存在
if (!jBinder.isBinderAlive()) {
...
return null;
}
// Only increment the ref count if we have one. If we don't then the
// provider is not reference counted and never needs to be released.
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
// 增加引用計數
incProviderRefLocked(prc, stable);
}
return provider;
}
}
複製程式碼
首先是去快取中獲取,是否存在封裝了ContentProvider物件的ProviderClientRecord物件,如果不存在則說明第一次啟動,還沒新增到快取中,如果存在,那麼說明已經啟動過,則會有需要的實現IContentProvider介面的物件。再看第4步中如果返回物件不為空則直接返回,如果是空則要先獲取IActivityManager.ContentProviderHolder。
6.ActivityManagerProxy.getContentProvider
public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name, int userId, boolean stable) throws RemoteException {
...
// 通過Binder代理物件mRemote傳送一個型別為GET_CONTENT_PROVIDER_TRANSACTION的程式間通訊訊息
mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
...
return cph;
}
複製程式碼
這裡我們在前面幾章涉及到了很多次,就不再說了,主要是從這裡呼叫AMS(ActivityManagerService)中的對應方法getContentProvider
7.AMS.getContentProvider
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
...
return getContentProviderImpl(caller, name, null, stable, userId);
}
複製程式碼
這裡比較簡單,呼叫AMS.getContentProviderImpl方法。
8.AMS.getContentProviderImpl
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized (this) {
// 描述程式資訊的物件
ProcessRecord r = null;
if (caller != null) {// 如果呼叫者的執行緒存在,根據caller獲取對應的程式
r = getRecordForAppLocked(caller);
// 如果找不到caller對應的程式丟擲異常
...
}
...
// 根據名稱和userId獲取ContentProviderRecord物件,用來檢測是不是ContentProvider已經發布了
cpr = mProviderMap.getProviderByName(name, userId);
// If that didn't work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
if (cpr == null && userId != UserHandle.USER_SYSTEM) {// 如果不存在並且不是系統的userId
// 檢測mProviderMap中是否存在系統的ContentProviderRecord物件,如果已經啟動則存在
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_SYSTEM);
if (cpr != null) {
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
...
} else {
cpr = null;
cpi = null;
}
}
}
// 檢查ContentProvider是否正在執行(第一次還沒有執行)
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
if (providerRunning) {// ContentProvider已經執行
...
if (r != null && cpr.canRunHere(r)) {// 程式存在
ContentProviderHolder holder = cpr.newHolder(null);
holder.provider = null;
return holder;
}
...
// provider例項已經存在,因此我們直接返回
conn = incProviderCountLocked(r, cpr, token, stable);
// 如何stable和unstable的總引用計數為1,那麼更新LruProcess列表
if (conn != null && (conn.stableCount + conn.unstableCount) == 1) {
if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) {
...
updateLruProcessLocked(cpr.proc, false, null);
}
}
final int verifiedAdj = cpr.proc.verifiedAdj;
// 更新provider程式的adj
boolean success = updateOomAdjLocked(cpr.proc);
...
if (!success) {// 如果更新不成功,那麼減少引用計數並殺死provider程式
...
ppDiedLocked(cpr.proc);// 殺死程式
...
// 已經殺死了,所以要恢復初始狀態
providerRunning = false;
conn = null;
} else {// 更新成功了
cpr.proc.verifiedAdj = cpr.proc.setAdj;
}
...
}
if (!providerRunning) {// 還沒有執行,第一次啟動
try {
// 根據ContentProvider的AUTHORITY來獲取對應的配置資訊(ProviderInfo)
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
} catch (RemoteException ex) {
}
// 沒有找到對應的ProviderInfo,因此返回空
if (cpi == null) {
return null;
}
// Provider是否為單例
boolean singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
...
// 通過ComponentName獲取providerMap中的cpr
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
// 先去快取中找
cpr = mProviderMap.getProviderByClass(comp, userId);
// 是否是第一次啟動
final boolean firstClass = cpr == null;
if (firstClass) {// 第一次啟動
final long ident = Binder.clearCallingIdentity();
try {
// 獲取應用資訊
ApplicationInfo ai =
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
...
// 建立一個cpr(ContentProviderRecord)
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
...
} finally {
...
}
}
...
if (r != null && cpr.canRunHere(r)) {// 程式存在且正在執行
return cpr.newHolder(null);
}
...
final int N = mLaunchingProviders.size();
int i;
for (i = 0; i < N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// ContentProvider還沒有被啟動
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
...
// 獲取執行ContentProvider的程式
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
// 如果程式已經存在並且沒有被殺死
if (proc != null && proc.thread != null && !proc.killed) {
if (!proc.pubProviders.containsKey(cpi.name)) {
// 將描述ContentProvider的物件(ContentProviderRecord)以包名為鍵放到程式進行快取
proc.pubProviders.put(cpi.name, cpr);
try {
// 安裝ContentProvider
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {// 程式不存在
// 啟動程式
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
if (proc == null) {// 程式啟動失敗
return null;
}
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
} finally {
...
}
}
if (firstClass) {// 第一次啟動要儲存ContentProvider
mProviderMap.putProviderByClass(comp, cpr);
}
// 儲存
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
}
...
return cpr != null ? cpr.newHolder(conn) : null;
}
複製程式碼
這裡程式碼比較多,但是思路比較清晰,首先是找對應的程式是否存在,因為我們正在啟動ContentProvider,所以正常程式時存在的,然後根據程式找描述ContentProvider的描述物件ContentProviderRecord,如果第一次啟動,這個描述物件是不存在的,如果不是第一次那麼則存在該物件,其中providerRunning引數用來判斷ContentProvider是否已經執行的,如果執行了直接返回,如果沒有執行則建立ContentProvider的描述物件ContentProviderRecord,然後判斷程式是否存在,存在則呼叫ApplicationThread.scheduleInstallProvider方法安裝ContentProvider,不存在則直接啟動程式。安裝完成後快取這些建立的ContentProvider相關的物件,方便下次使用直接從快取獲取。
9.ApplicationThread.scheduleInstallProvider
public void scheduleInstallProvider(ProviderInfo provider) {
sendMessage(H.INSTALL_PROVIDER, provider);
}
複製程式碼
這裡是傳送訊息到ApplicationThread中的H(繼承Handler)中,然後呼叫ActivityThread.handleInstallProvider
10.ActivityThread.handleInstallProvider
public void handleInstallProvider(ProviderInfo info) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
installContentProviders(mInitialApplication, Lists.newArrayList(info));
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
複製程式碼
呼叫installContentProviders方法。
11.ActivityThread.installContentProviders
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers) {
...
// 載入ContentProvider,然後呼叫ContentProvider的onCreate方法
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
複製程式碼
這裡有兩步,第一步:安裝ContentProvider,然後呼叫ContentProvider.onCreate方法,第二步:呼叫AMP.publishContentProviders釋出ContentProvider。我們先看第一步。
12.ActivityThread.installProvider
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
...
try {
final java.lang.ClassLoader cl = c.getClassLoader();
// 根據名字通過類載入方式載入ContentProvider
localProvider = (ContentProvider) cl.
loadClass(info.name).newInstance();
// 獲取provider,也就是ContentProvider中的mTransport(Transport),這個provider賦值給了retHolder,所以前面在獲取的時候就是這個provider(Transport)
provider = localProvider.getIContentProvider();
...
// 新增ProviderInfo,然後呼叫ContentProvider的onCreate方法
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
...
return null;
}
} else {// localProvider == null
provider = holder.provider;
...
}
IActivityManager.ContentProviderHolder retHolder;
// 建立IActivityManager.ContentProviderHolder並且快取ContentProvider相關資訊
...
return retHolder;
}
複製程式碼
這裡通過反射獲取ContentProvider,然後呼叫ContentProvider的onCreate方法。建立完成後會快取到IActivityManager.ContentProviderHolder中,並且返回IActivityManager.ContentProviderHolder物件。在11步中我們提到兩步,一個就是安裝,一個是釋出,下面我看釋出的程式碼。
13.AMP.publishContentProviders
public void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) throws RemoteException {
...
mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0);
...
}
複製程式碼
通過這裡呼叫AMS.publishContentProviders方法。
14.AMS.publishContentProviders
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
synchronized (this) {
// 獲取程式描述物件
final ProcessRecord r = getRecordForAppLocked(caller);
...
final long origId = Binder.clearCallingIdentity();
final int N = providers.size();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
// 獲取每個ContentProvider的描述物件ContentProviderRecord放到mProviderMap中
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
// 快取
mProviderMap.putProviderByName(names[j], dst);
}
int launchingCount = mLaunchingProviders.size();
...
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
// 通知AMS provider已經"釋出"成功
dst.notifyAll();
}
...
}
}
...
}
}
複製程式碼
這裡主要是從ContentProvider列表中獲取對應的描述物件進行快取,然後通知AMS中的ContentProvider釋出完成。
15.ActivityThread.installProvider
這個方法我們上面12中分析了。只是一個是第一次啟動,一個已經存在快取了。
16.Transport.query
結合第1步和第12步我們知道IContentProvider unstableProvider返回的是ContentProvider中的mTransport,因此這裡呼叫的是Transport.query方法
public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
...
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
...
// 回撥目標ContentProvider所定義的query方法
Cursor cursor = ContentProvider.this.query(uri, projection, selection,
selectionArgs, sortOrder, CancellationSignal.fromTransport(
cancellationSignal));
if (cursor == null) {
return null;
}
// Return an empty cursor for all columns.
return new MatrixCursor(cursor.getColumnNames(), 0);
}
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.query(
uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
...
}
}
複製程式碼
最終呼叫了ContentProvider.this.query方法。到此我們對於ContentProvider.this.query方法分析完成了。其實還有其他方法(insert,delete,update)邏輯也是差不多的,分析完一個其他也就很好分析。
到此Android系統四大元件都分析完了,可能感覺還是不夠詳細,其實還是要自己多看,多看幾遍原始碼就熟悉了。後面開始分析View的繪製流程,因為這個相對來說非常複雜,如果一篇寫完會寫很長,看的話也很累,所以後面改用分章節去寫,沒章儘量分離開,儘量簡短清晰,一看就能明白。
參考
從原始碼角度看ContentProvider 理解ContentProvider原理
程式碼地址:
直接拉取匯入開發工具(Intellij idea或者Android studio)
由於coding與騰訊雲合作,改變很多,所以後續程式碼切換到Gitlab。
注:
首發地址:www.codemx.cn
Android開發群:192508518
微信公眾賬號:Code-MX
注:本文原創,轉載請註明出處,多謝。