Android System Server大綱之ContentService和ContentProvider原理剖析
前言
ContentService是Android四大元件之一的ContentProvider密切相關的一個系統服務,且和AccountManagerService也有著緊密的聯絡。ContentService以功能來劃分,就兩大模組:
- 提供管理通知監聽ContentProvider資料變化的觀察者的服務
- 同步功能:同步日曆、Email等
第一個功能,對於很多開發者來說可能熟悉。而對於第二個,可能就會陌生一點,這個是Android提供的同步功能,很多APP開發者,也很難用到這個Android的同步功能。ContentService的同步功能和AccountManagerService往往緊密聯絡在一起。本文將不再闡述ContentService的同步功能,有興趣的讀者可以自行閱讀Android的參考文件瞭解該功能的使用方法
- ContentProvider的原理
- ContentProvider的觀察者服務
ContentProvider是Android的四大元件之一,是提供APP間安全共享資料的方式之一,APP可以通過ContentProvider從其它APP中獲取到想要的資料,例如讀取手機的聯絡人。也可以通過ContentProvider把APP的資料共享出去給其它APP使用。
以查詢ContentProvider的資料的流程為線索,一步一步分析這個過程,從而瞭解ContentProvider的過程和原理。
APP發起查詢
引用官方的一段話:如果您想要訪問內容提供程式中的資料,可以將應用的 Context 中的 ContentResolver 物件用作客戶端來與提供程式通訊。 ContentResolver 物件會與提供程式物件(即實現 ContentProvider 的類例項)通訊。 提供程式物件從客戶端接收資料請求,執行請求的操作並返回結果。
APP發起查詢,很簡單,如下:
ContentResolver cr = getContentResolver();
cr.query(Uri uri, ....);
- 首先通過getContentResolver()獲取到ContentResolver物件,ContentResolver物件好比內容分解器,能夠分解出來需要呼叫哪個內容提供程式即ContentProvider。ContentResolver分解的依據便是引數Uri。Uri是如何把ContentResolver和ContentProvider聯絡起來的呢?Uri可以看作是一個地址描述,隨意舉一個Uri可描述的地址例子:
content://user_dictionary
可以和平時上網的網址做一個類比,如訪問百度:https://www.baidu.com
Uri和網址結構有很大相似性,https表示的是網路技術中的傳輸協議,那麼content也可以當做ContentProvider中的一種協議,那麼content就代表當前需要通過ContentProvider進行資料通訊。www.baidu.com是百度的域名,表示需要訪問網站的位置,user_dictionary也就可以比作ContentProvider的域名,user_dictionary表示是那個ContentProvider。換句話說,ContentResolver接收到content://user_dictionary這個Uri時,就知道當前需要發起ContentProvider資料互動,通過user_dictionary可以尋找用user_dictionary表示的ContentProvider。
深入ContentResolver
準備工作
檢視ContentResolver的定義,如下:
public abstract class ContentResolver {
......
}
- 可見ContentResolver是一個抽象類,所以說getContentResolver()所獲取到例項本質是ContentResolver的子類,檢視getContentResolver()的具體實現:
private final ApplicationContentResolver mContentResolver;
public ContentResolver getContentResolver() {
return mContentResolver;
}
- 上面的程式碼定義在檔案frameworks/base/core/java/android/app/ContextImpl.java中
從上面的程式碼可知,getContentResolver()獲取到的實際是ApplicationContentResolver的例項mContentResolver。cr.query(Uri uri, ….)的實現如下:
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,......) {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
......
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
......
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
......
}
- 這個方法定義在檔案frameworks/base/core/java/android/content/ContentResolver.java中。
這裡用文字描述一下上面程式碼的過程,先宣告兩個相同型別的區域性變數unstableProvider、stableProvider,都是IContentProvider的例項,IContentProvider是AIDL的介面,所以IContentProvider所呼叫的方法是遠端呼叫。通過acquireUnstableProvider()方法給unstableProvider變數賦值,unstableProvider呼叫query()方法,如果發生DeadObjectException異常,呼叫acquireProvider()方法給stableProvider賦值,然後呼叫stableProvider的query()方法。
這裡有點奇怪,為什麼要宣告兩個一樣的IContentProvider變數,且呼叫相同的方法query()。從變數的命名上,也能找到一點線索,unstableProvider和stableProvider,前者是不穩定的provider,後者是穩定的provider。什麼樣表示穩定的,什麼樣表示不穩定的?從上面的程式碼來看,不穩定的provider,呼叫query()方法會丟擲DeadObjectException。下面先看看unstableProvider的賦值過程:
public final IContentProvider acquireExistingProvider(......) {
synchronized (mProviderMap) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
......
return provider;
}
}
- 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。
可見unstableProvider是從變數mProviderMap的例項中遍歷出來,也就是說,在呼叫者的程序內部,對provider會快取到mProviderMap這個變數中。而IContentProvider是一個跨程序的遠端呼叫。換句話說,呼叫者的程序內部所快取的unstableProvider例項所對應的遠端程序不能保證還正常執行著,如果所對應的遠端程序沒有執行,那麼就會丟擲DeadObjectException異常,並呼叫unstableProviderDied()方法把本地的快取清除。然後通過acquireProvider()重新生產一個新的stableProvider,且Android系統會啟動和stableProvider對應的遠端程序,這個過程下文會闡述。可見Android這個舉措是為了效能考慮,目的讓APP的執行僅可能的快。
獲取ContentProvider
回到上文中例項化變數unstableProvider和stableProvider的程式碼,unstableProvider或stableProvider例項化後,直接呼叫query()方法,然後直接就可以返回Cursor給呼叫者了,說明在這裡的query()這一步,已經拿到呼叫者需要的資料。所以理解unstableProvider和stableProvider的實質至關重要。那麼unstableProvider和stableProvider所對應的遠端程序的服務是哪一個呢?是不是就直接對應ContentProvider呢?
由於unstableProvider是一個呼叫者程序中快取的例項,所以需要對stableProvider下手,瞭解stableProvider的例項化過程即可,如下:
public final IContentProvider acquireProvider(......) {
......
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
......
return holder.provider;
}
- 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。
stableProvider被封裝到ContentProviderHolder的變數中provider中。通過呼叫ActivityManagerNative.getContentProvider()獲取ContentProviderHolder,ActivityManagerNative的所對應的遠端服務是system_process中的ActivityManagerService,接著看getContentProvider()這個方法:
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
......
return getContentProviderImpl(caller, name, null, stable, userId);
}
- 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
這裡直接呼叫了getContentProviderImpl()方法:
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
synchronized(this) {
......
boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed;
if (providerRunning) {
......
}
if (!providerRunning) {
......
// If the provider is not already being launched, then get it
// started.
if (i >= N) {
try {
// Content provider is now in use, its package can't be stopped.
......
if (proc != null && proc.thread != null && !proc.killed) {
......
} else {
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
......
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
......
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
}
// Wait for the provider to be published...
synchronized (cpr) {
while (cpr.provider == null) {
......
cpr.wait();
......
}
}
return cpr != null ? cpr.newHolder(conn) : null;
}
- 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
由於這個方法的程式碼篇幅非常大,這裡省略了很多程式碼,省略的程式碼主要就是一些快取、許可權等等相關的程式碼。這裡假設provider的程序沒有啟動,從最原始,最乾淨的時候分析ContentProvider的這個過程。聚焦上面的程式碼,首先分兩種情況,providerRunning和!providerRunning,providerRunning是基於!providerRunning為前提的,因此provider第一次啟動是執行!providerRunning這個程式碼路徑。假若provider沒有執行過,Android首先通過startProcessLocked()把provider所在的應用啟動起來,這個過程這裡暫時先放下,繼續往下看,在最後面,有一個while迴圈,迴圈條件是cpr.provider == null?這裡會一直等待cpr的provider變數被賦值,回到上文中的acquireProvider()方法的程式碼,acquireProvider()首先要獲取的是ContentProviderHolder的例項,上面的程式碼如果cpr != null,就返回一個ContentProviderHolder物件(cpr.newHolder(conn))。所以這裡的cpr.provider就是上文中acquireProvider()需要獲取的IContentProvider的例項物件。
公佈ContentProvider
因此,只要繼續分析清楚cpr.provider的賦值過程,就知道開始的時候ContentResolver.query()所呼叫的真正的遠端服務。回到上面的程式碼,startProcessLocked()啟動應用時,執行到如下程式碼(APP的啟動過程本文不再贅述):
public static void main(String[] args) {
......
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
}
- 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。
生成ActivityThread例項,也就是常說的主執行緒(UI執行緒了),然後呼叫attach()方法:
private void attach(boolean system) {
......
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
......
}
- 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。
又回到ActivityManagerService了,接著往下看:
public final void attachApplication(IApplicationThread thread) {
......
attachApplicationLocked(thread, callingPid);
......
}
- 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
這裡沒有需要看的程式碼,繼續往下跟蹤:
private final boolean attachApplicationLocked(IApplicationThread thread,.....) {
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,......);
}
- 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
IApplicationThread例項thread是遠端APP的遠端呼叫端,所以thread.bindApplication()實際又從system_process程序回到了APP所在的程序,注意第三個引數providers,providers是封裝了應用的所有的ContentProvider,詳情讀者可以閱讀generateApplicationProvidersLocked(app)方法,本文不再闡述。繼續往下看:
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,......) {
......
data.providers = providers;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
sendMessage(H.BIND_APPLICATION, data);
}
- 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。
傳送了一個H.BIND_APPLICATION的訊息,注意data.providers = providers。該訊息的處理是在:
private void handleBindApplication(AppBindData data) {
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
......
}
}
- 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。
呼叫了installContentProviders()方法,注意引數data.providers,如下:
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers) {
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();
}
}
- 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。
把應用的所有的ContentProvider資訊封裝到ContentProviderHolder中,這裡要回到上文中acquireProvider()方法,獲取IContentProvider的例項正好是ContentProviderHolder的中的變數provider,因此,這裡需要了解上面程式碼ContentProviderHolder的例項cph的例項化過程,看installProvider()方法:
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,.....) {
ContentProvider localProvider = null;
IContentProvider provider;
if (holder == null || holder.provider == null) {
......
try {
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
......
IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap) {
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
ComponentName cname = new ComponentName(info.packageName, info.name);
ProviderClientRecord pr = mLocalProvidersByName.get(cname);
if (pr != null) {
......
} else {
holder = new IActivityManager.ContentProviderHolder(info);
holder.provider = provider;
......
}
retHolder = pr.mHolder;
} else {
......
return retHolder;
}
- 這個方法定義在檔案frameworks/base/core/java/android/app/ActivityThread.java中。
上面的程式碼通過ClassLoader對映ContentProvider的物件例項,然後呼叫getIContentProvider()方法,獲取到IContentProvider的例項provider,然後賦值給ContentProviderHolder的例項holder的變數provider,所以provider正是acquireProvider()中獲取的的IContentProvider的例項,而繼續看getIContentProvider()方法檢視provider的本質是:
......
private Transport mTransport = new Transport();
public IContentProvider getIContentProvider() {
return mTransport;
}
class Transport extends ContentProviderNative {
public Cursor query(.....){
......
}
......
}
......
- 這個程式碼定義在檔案frameworks/base/core/java/android/content/ContentProvider.java中。
上面的程式碼可見,provider實質是Transport的例項mTransport,Transport是ContentProviderNative的子類,而ContentProviderNative是IContentProvider的子類。
下面繼續看看provider和ActivityManagerService的關係,回到上文中的installContentProviders()方法,呼叫了publishContentProviders()方法,如下:
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
......
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;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (DEBUG_MU) Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
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);
}
......
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
dst.notifyAll();
}
}
}
}
- 這個程式碼定義在檔案frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java中。
把應用的ContentProvider資訊快取到mProviderMap中,這裡重點關注語句dst.provider = src.provider,回到上文中getContentProviderImpl()方法中的程式碼,後面的while迴圈,等待cpr.provider的賦值,dst.provider = src.provider正好就是這個賦值過程,cpr.provider賦值後,getContentProviderImpl()返回ContentProviderHolder的例項cpr.newHolder(conn),而這個例項持有變數provider指向Transport的例項mTransport,也就是應用的ContentProvider。
關係圖
這個過程非常繞亂人的頭緒,下面把這個過程簡單轉換成一張簡單的關係圖,理清這個過程和關係,如下:
ContentProvider資料監聽
觀察者註冊過程
有時APP開發的時候,APP執行時,需要實時監聽一些ContentProvider資料的變化。如使用者添加了一個聯絡人,APP需要接收到聯絡人新增的通知。由於ContentProvider的提供者,和需求者不是同一個APP,所以,這個通知的紐帶,就需要ContentService來完成。從註冊一個觀察者開始,分析這個通知的來龍去脈,註冊程式碼如下:
ContentResolver cr = getContentResolver();
cr.registerContentObserver(@NonNull Uri uri, boolean notifyForDescendants,
@NonNull ContentObserver observer);
- 當ContentProvider的資料發生變化時,便會回撥到ContentObserver的onChange()方法,觀察者ContentObserver observer被註冊到哪裡去呢?繼續進入分析:
public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
ContentObserver observer, @UserIdInt int userHandle) {
try {
getContentService().registerContentObserver(uri, notifyForDescendents,
observer.getContentObserver(), userHandle);
} catch (RemoteException e) {
}
}
- 這個方法定義在檔案frameworks/base/core/java/android/content/ContentResolver.java中。
上面的程式碼getContentService()獲取到的物件是:
public static IContentService getContentService() {
if (sContentService != null) {
return sContentService;
}
IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
if (false) Log.v("ContentService", "default service binder = " + b);
sContentService = IContentService.Stub.asInterface(b);
if (false) Log.v("ContentService", "default service = " + sContentService);
return sContentService;
}
- 這個方法定義在檔案frameworks/base/core/java/android/content/ContentResolver.java中。
閱讀《Android系統之System Server大綱 》可知,啟動的CONTENT_SERVICE_NAME的系統服務如下:
private static final String CONTENT_SERVICE_CLASS =
"com.android.server.content.ContentService$Lifecycle";
traceBeginAndSlog("StartContentService");
mSystemServiceManager.startService(CONTENT_SERVICE_CLASS);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- 這個方法定義在檔案frameworks/base/services/java/com/android/server/SystemServer.java中。
所以getContentService()獲取到的是ContentService,繼續往下分析registerContentObserver():
public void registerContentObserver(Uri uri, boolean notifyForDescendants,
IContentObserver observer, int userHandle) {
......
synchronized (mRootNode) {
mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
uid, pid, userHandle);
if (false) Log.v(TAG, "Registered observer " + observer + " at " + uri +
" with notifyForDescendants " + notifyForDescendants);
}
}
- 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/content/ContentService.java中。
上面的程式碼把觀察者observer通過addObserverLocked放置在mRootNode物件中
private void addObserverLocked(Uri uri, int index, IContentObserver observer,
boolean notifyForDescendants, Object observersLock,
int uid, int pid, int userHandle) {
// If this is the leaf node add the observer
if (index == countUriSegments(uri)) {
mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
uid, pid, userHandle));
return;
}
// Look to see if the proper child already exists
String segment = getUriSegment(uri, index);
int N = mChildren.size();
for (int i = 0; i < N; i++) {
ObserverNode node = mChildren.get(i);
if (node.mName.equals(segment)) {
node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
observersLock, uid, pid, userHandle);
return;
}
}
// No child found, create one
ObserverNode node = new ObserverNode(segment);
mChildren.add(node);
node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
observersLock, uid, pid, userHandle);
}
- 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/content/ContentService.java中。
首先把觀察者observer再次封裝到mObservers中,mObservers是ArrayList的例項,後面依次遍歷整個需要監聽的Uri,用segment生產一個ObserverNode例項,儲存在ArrayList的例項mChildren中。
觀察者都被封裝好以後,就等待資料的變化了。在上文ContentProvider的原理中,可知,ContentProvider的需求者拿到ContentProvider提供者的遠端呼叫後,兩個APP直接進行互動了,任何資料變化並沒有經過ContentService,因此,ContentService是如何監測ContentProvider資料發生變化並通知到觀察者呢?這裡的機制便是ContentProvider提供者的資料發生變化時,ContentProvider提供者必須主動通過notifyChange()方法通知ContentService資料發生了變化。下面以聯絡人的資料庫變化為例,闡述這個過程。
資料變化通知過程
新增一個聯絡人,呼叫ContactsProvider的insert()方法,如下:
public Uri insert(Uri uri, ContentValues values) {
incrementStats(mInsertStats, mInsertInBatchStats);
ContactsTransaction transaction = startTransaction(false);
try {
Uri result = insertInTransaction(uri, values);
if (result != null) {
transaction.markDirty();
}
transaction.markSuccessful(false);
return result;
} finally {
endTransaction(false);
}
}
- 這個方法定義在檔案packages/providers/ContactsProvider/src/com/android/providers/contacts/AbstractContactsProvider.java中。
上面的程式碼中,一次插入的業務抽象成一個ContactsTransaction,然後呼叫insertInTransaction()方法把資料插入資料庫成功後,呼叫transaction.markDirty()標記當前的資料狀態有變化,然後呼叫endTransaction()結束當前的業務。如下:
private void endTransaction(boolean callerIsBatch) {
ContactsTransaction transaction = mTransactionHolder.get();
if (transaction != null && (!transaction.isBatch() || callerIsBatch)) {
boolean notify = false;
try {
if (transaction.isDirty()) {
notify = true;
}
transaction.finish(callerIsBatch);
if (notify) {
notifyChange();
}
} finally {
// No matter what, make sure we clear out the thread-local transaction reference.
mTransactionHolder.set(null);
}
}
}
- 這個方法定義在檔案packages/providers/ContactsProvider/src/com/android/providers/contacts/AbstractContactsProvider.java中。
上面的程式碼,通過mTransactionHolder.get()獲取剛剛進行的業務transaction,如果的這個業務被標記為markDirty(),transaction.isDirty()返回true,所以notify被置成true,所以會呼叫notifyChange()。往下看這個方法:
protected void notifyChange(boolean syncToNetwork, boolean syncToMetadataNetwork) {
getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null,
syncToNetwork);
getContext().getContentResolver().notifyChange(MetadataSync.METADATA_AUTHORITY_URI,
null, syncToMetadataNetwork);
}
- 這個方法定義在檔案packages/providers/ContactsProvider/src/com/android/providers/contacts/ContactsProvider2.java中。
上面的程式碼,獲取到ContentResolver()物件,和query()類似,然後呼叫notifyChange()通知ContentResolver資料發生了變化,如下:
public void notifyChange(@NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,
@UserIdInt int userHandle) {
try {
getContentService().notifyChange(
uri, observer == null ? null : observer.getContentObserver(),
observer != null && observer.deliverSelfNotifications(),
syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
userHandle);
} catch (RemoteException e) {
}
}
- 這個方法定義在檔案frameworks/base/core/java/android/content/ContentResolver.java中。
上面的程式碼呼叫getContentService()方法,從上文可知,getContentService()獲取到的是ContentService的遠端呼叫,因此getContentService().notifyChange()實質會執行到ContentService的notifyChange()方法,如下:
public void notifyChange(Uri uri, ......) {
......
try {
ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
synchronized (mRootNode) {
mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
flags, userHandle, calls);
}
final int numCalls = calls.size();
for (int i=0; i<numCalls; i++) {
ObserverCall oc = calls.get(i);
try {
oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
} catch (RemoteException ex) {
......
}
}
......
}
} finally {
restoreCallingIdentity(identityToken);
}
}
- 這個方法定義在檔案frameworks/base/services/core/java/com/android/server/content/ContentService.java中。
上面的程式碼中的mRootNode變數,在註冊觀察者的章節中,mRootNode封裝了所有的觀察者。呼叫collectObserversLocked()根據觀察者監聽的Uri型別把觀察者封裝到ArrayList的例項calls中,然後呼叫oc.mObserver.onChange()方法通知APP資料發生變化。
總結
本文詳細闡述了ContentProvider的原理,從ContentProvider的query過程入手,全面詳細分析了從query()的發起APP到ActivityManagerService,再到ContentProvider提供程式的整個流程和互動過程。以及分析了ContentService在ContentProvider的資料變化監測通知的架構中,扮演了觀察者模式中的抽象主題,具體主題部分。