### 9.5 ContentProvider的工作過程
ContentProvider的使用方法在第2章已經做了介紹,這里再簡單說明一下。ContentProvider是一種內容共享型組件,它通過Binder向其他組件乃至其他應用提供數據。當ContentProvider所在的進程啟動時,ContentProvider會同時啟動并被發布到AMS中。需要注意的是,這個時候ContentProvider的onCreate要先于Application的onCreate而執行,這在四大組件中是一個少有的現象。
當一個應用啟動時,入口方法為ActivityThread的main方法,main方法是一個靜態方法,在main方法中會創建ActivityThread的實例并創建主線程的消息隊列,然后在ActivityThread的attach方法中會遠程調用AMS的attachApplication方法并將ApplicationThread對象提供給AMS。ApplicationThread是一個Binder對象,它的Binder接口是IApplicationThread,它主要用于ActivityThread和AMS之間的通信,這一點在前面多次提到。在AMS的attachApplication方法中,會調用ApplicationThread的bindApplication方法,注意這個過程同樣是跨進程完成的,bindApplication的邏輯會經過ActivityThread中的mH Handler切換到ActivityThread中去執行,具體的方法是handleBindApplication。在handleBindApplication方法中,ActivityThread會創建Application對象并加載ContentProvider。需要注意的是,ActivityThread會先加載ContentProvider,然后再調用Application的onCreate方法,整個流程可以參看圖9-2。
:-: 
圖9-2 ContentProvider的啟動過程
這就是ContentProvider的啟動過程,ContentProvider啟動后,外界就可以通過它所提供的增刪改查這四個接口來操作ContentProvider中的數據源,即insert、delete、update和query四個方法。這四個方法都是通過Binder來調用的,外界無法直接訪問ContentProvider,它只能通過AMS根據Uri來獲取對應的ContentProvider的Binder接口IConentProvider,然后再通過IConentProvider來訪問ContentProvider中的數據源。
一般來說,ContentProvider都應該是單實例的。ContentProvider到底是不是單實例,這是由它的android:multiprocess屬性來決定的,當android:multiprocess為false時,ContentProvider是單實例,這也是默認值;當android:multiprocess為true時,ContentProvider為多實例,這個時候在每個調用者的進程中都存在一個ContentProvider對象。由于在實際的開發中,并未發現多實例的ContentProvider的具體使用場景,官方文檔中的解釋是這樣可以避免進程間通信的開銷,但是這在實際開發中仍然缺少使用價值。因此,我們可以簡單認為ContentProvider都是單實例的。下面分析單實例的ContentProvider的啟動過程。
訪問ContentProvider需要通過ContentResolver, ContentResolver是一個抽象類,通過Context的getContentResolver方法獲取的實際上是ApplicationContentResolver對象,ApplicationContentResolver類繼承了ContentResolver并實現了ContentResolver中的抽象方法。當ContentProvider所在的進程未啟動時,第一次訪問它時就會觸發ContentProvider的創建,當然這也伴隨著ContentProvider所在進程的啟動。通過ContentProvider的四個方法的任何一個都可以觸發ContentProvider的啟動過程,這里選擇query方法。
ContentProvider的query方法中,首先會獲取IContentProvider對象,不管是通過acquireUnstableProvider方法還是直接通過acquireProvider方法,它們的本質都是一樣的,最終都是通過acquireProvider方法來獲取ContentProvider。下面是ApplicationContent-Resolver的acquireProvider方法的具體實現:
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
ApplicationContentResolver的acquireProvider方法并沒有處理任何邏輯,它直接調用了ActivityThread的acquireProvider方法,ActivityThread的acquireProvider方法的源碼如下所示。
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth,
userId, stable);
if (provider ! = null) {
return provider;
}
// There is a possible race here. Another thread may try to acquire
// the same provider at the same time. When this happens, we want to
ensure
// that the first one wins.
// Note that we cannot hold the lock while acquiring and installing the
// provider since it might take a long time to run and it could also
potentially
// be re-entrant in the case where the provider is in the same process.
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
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;
}
上面的代碼首先會從ActivityThread中查找是否已經存在目標ContentProvider了,如果存在就直接返回。ActivityThread中通過mProviderMap來存儲已經啟動的ContentProvider對象,mProviderMap的聲明如下所示。
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
= new ArrayMap<ProviderKey, ProviderClientRecord>();
如果目前ContentProvider沒有啟動,那么就發送一個進程間請求給AMS讓其啟動目標ContentProvider,最后再通過installProvider方法來修改引用計數。那么AMS是如何啟動ContentProvider的呢?我們知道,ContentProvider被啟動時會伴隨著進程的啟動,在AMS中,首先會啟動ContentProvider所在的進程,然后再啟動ContentProvider。啟動進程是由AMS的startProcessLocked方法來完成的,其內部主要是通過Process的start方法來完成一個新進程的啟動,新進程啟動后其入口方法為ActivityThread的main方法,如下所示。
public static void main(String[] args) {
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
Security.addProvider(new AndroidKeyStoreProvider());
// Make sure TrustedCertificateStore looks in the right place for CA
certificates
final File configDir = Environment.getUserConfigDirectory(User-
Handle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
可以看到,ActivityThread的main方法是一個靜態方法,在它內部首先會創建Activity-Thread的實例并調用attach方法來進行一系列初始化,接著就開始進行消息循環了。ActivityThread的attach方法會將ApplicationThread對象通過AMS的attachApplication方法跨進程傳遞給AMS,最終AMS會完成ContentProvider的創建過程,源碼如下所示。
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
AMS的attachApplication方法調用了attachApplicationLocked方法,attachApplication-Locked中又調用了ApplicationThread的bindApplication,注意這個過程也是進程間調用,如下所示。
thread.bindApplication(processName, appInfo, providers, app.instrumen-
tationClass,
profilerInfo, app.instrumentationArguments, app.instrumentation-
Watcher,
app.instrumentationUiAutomationConnection, testMode, enableOpen-
GlTrace,
isRestrictedBackupMode || ! normalMode, app.persistent,
new Configuration(mConfiguration), app.compat, getCommonServices-
Locked(),
mCoreSettingsObserver.getCoreSettingsLocked());
ActivityThread的bindApplication會發送一個BIND_APPLICATION類型的消息給mH, mH是一個Handler,它收到消息后會調用ActivityThread的handleBindApplication方法,bindApplication發送消息的過程如下所示。
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableOpenGlTrace = enableOpenGlTrace;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
sendMessage(H.BIND_APPLICATION, data);
ActivityThread的handleBindApplication則完成了Application的創建以及Content-Provider的創建,可以分為如下四個步驟。
1.創建ContextImpI和Instrumentation
ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try {
java.lang.ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
mInstrumentation.init(this, instrContext, appContext,
new ComponentName(ii.packageName, ii.name), data.instrumentation-
Watcher,
data.instrumentationUiAutomationConnection);
2.創建AppIication對象
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
3.啟動當前進程的ContentProvider并調用其onCreate方法
List<ProviderInfo> providers = data.providers;
if (providers ! = null) {
installContentProviders(app, providers);
// For process that contains content providers, we want to
// ensure that the JIT is enabled "at some point".
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
installContentProviders完成了ContentProvider的啟動工作,它的實現如下所示。首先會遍歷當前進程的ProviderInfo的列表并一一調用調用installProvider方法來啟動它們,接著將已經啟動的ContentProvider發布到AMS中,AMS會把它們存儲在ProviderMap中,這樣一來外部調用者就可以直接從AMS中獲取ContentProvider了。
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
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());
}
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) {
}
}
下面看一下ContentProvider對象的創建過程,在installProvider方法中有下面一段代碼,其通過類加載器完成了ContentProvider對象的創建:
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
localProvider.attachInfo(c, info);
在上述代碼中,除了完成ContentProvider對象的創建,還會通過ContentProvider的attachInfo方法來調用它的onCreate方法,如下所示。
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
...
if (mContext == null) {
mContext = context;
if (context ! = null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystem-
Service(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
...
ContentProvider.this.onCreate();
}
}
到此為止,ContentProvider已經被創建并且其onCreate方法也已經被調用,這意味著ContentProvider已經啟動完成了。
4.調用AppIication的onCreate方法
try {
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (! mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
經過上面的四個步驟,ContentProvider已經成功啟動,并且其所在進程的Application也已經啟動,這意味著ContentProvider所在的進程已經完成了整個的啟動過程,然后其他應用就可以通過AMS來訪問這個ContentProvider了。拿到了ContentProvider以后,就可以通過它所提供的接口方法來訪問它了。需要注意的是,這里的ContentProvider并不是原始的ContentProvider,而是ContentProvider的Binder類型的對象IContentProvider, IContentProvider的具體實現是ContentProviderNative和ContentProvider.Transport,其中ContentProvider.Transport繼承了ContentProviderNative。這里仍然選擇query方法,首先其他應用會通過AMS獲取到ContentProvider的Binder對象即IContentProvider,而IContentProvider的實現者實際上是ContentProvider.Transport。因此其他應用調用IContentProvider的query方法時最終會以進程間通信的方式調用到ContentProvider. Transport的query方法,它的實現如下所示。
public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri) ! = AppOpsManager.MODE_
ALLOWED) {
return rejectQuery(uri, projection, selection, selectionArgs,
sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
}
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.query(
uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingPackage(original);
}
}
很顯然,ContentProvider.Transport的query方法調用了ContentProvider的query方法,query方法的執行結果再通過Binder返回給調用者,這樣一來整個調用過程就完成了。除了query方法,insert、delete和update方法也是類似的,這里就不再分析了。
- 前言
- 第1章 Activity的生命周期和啟動模式
- 1.1 Activity的生命周期全面分析
- 1.1.1 典型情況下的生命周期分析
- 1.1.2 異常情況下的生命周期分析
- 1.2 Activity的啟動模式
- 1.2.1 Activity的LaunchMode
- 1.2.2 Activity的Flags
- 1.3 IntentFilter的匹配規則
- 第2章 IPC機制
- 2.1 Android IPC簡介
- 2.2 Android中的多進程模式
- 2.2.1 開啟多進程模式
- 2.2.2 多進程模式的運行機制
- 2.3 IPC基礎概念介紹
- 2.3.1 Serializable接口
- 2.3.2 Parcelable接口
- 2.3.3 Binder
- 2.4 Android中的IPC方式
- 2.4.1 使用Bundle
- 2.4.2 使用文件共享
- 2.4.3 使用Messenger
- 2.4.4 使用AIDL
- 2.4.5 使用ContentProvider
- 2.4.6 使用Socket
- 2.5 Binder連接池
- 2.6 選用合適的IPC方式
- 第3章 View的事件體系
- 3.1 View基礎知識
- 3.1.1 什么是View
- 3.1.2 View的位置參數
- 3.1.3 MotionEvent和TouchSlop
- 3.1.4 VelocityTracker、GestureDetector和Scroller
- 3.2 View的滑動
- 3.2.1 使用scrollTo/scrollBy
- 3.2.2 使用動畫
- 3.2.3 改變布局參數
- 3.2.4 各種滑動方式的對比
- 3.3 彈性滑動
- 3.3.1 使用Scroller7
- 3.3.2 通過動畫
- 3.3.3 使用延時策略
- 3.4 View的事件分發機制
- 3.4.1 點擊事件的傳遞規則
- 3.4.2 事件分發的源碼解析
- 3.5 View的滑動沖突
- 3.5.1 常見的滑動沖突場景
- 3.5.2 滑動沖突的處理規則
- 3.5.3 滑動沖突的解決方式
- 第4章 View的工作原理
- 4.1 初識ViewRoot和DecorView
- 4.2 理解MeasureSpec
- 4.2.1 MeasureSpec
- 4.2.2 MeasureSpec和LayoutParams的對應關系
- 4.3 View的工作流程
- 4.3.1 measure過程
- 4.3.2 layout過程
- 4.3.3 draw過程
- 4.4 自定義View
- 4.4.1 自定義View的分類
- 4.4.2 自定義View須知
- 4.4.3 自定義View示例
- 4.4.4 自定義View的思想
- 第5章 理解RemoteViews
- 5.1 RemoteViews的應用
- 5.1.1 RemoteViews在通知欄上的應用
- 5.1.2 RemoteViews在桌面小部件上的應用
- 5.1.3 PendingIntent概述
- 5.2 RemoteViews的內部機制
- 5.3 RemoteViews的意義
- 第6章 Android的Drawable
- 6.1 Drawable簡介
- 6.2 Drawable的分類
- 6.2.1 BitmapDrawable2
- 6.2.2 ShapeDrawable
- 6.2.3 LayerDrawable
- 6.2.4 StateListDrawable
- 6.2.5 LevelListDrawable
- 6.2.6 TransitionDrawable
- 6.2.7 InsetDrawable
- 6.2.8 ScaleDrawable
- 6.2.9 ClipDrawable
- 6.3 自定義Drawable
- 第7章 Android動畫深入分析
- 7.1 View動畫
- 7.1.1 View動畫的種類
- 7.1.2 自定義View動畫
- 7.1.3 幀動畫
- 7.2 View動畫的特殊使用場景
- 7.2.1 LayoutAnimation
- 7.2.2 Activity的切換效果
- 7.3 屬性動畫
- 7.3.1 使用屬性動畫
- 7.3.2 理解插值器和估值器 /
- 7.3.3 屬性動畫的監聽器
- 7.3.4 對任意屬性做動畫
- 7.3.5 屬性動畫的工作原理
- 7.4 使用動畫的注意事項
- 第8章 理解Window和WindowManager
- 8.1 Window和WindowManager
- 8.2 Window的內部機制
- 8.2.1 Window的添加過程
- 8.2.2 Window的刪除過程
- 8.2.3 Window的更新過程
- 8.3 Window的創建過程
- 8.3.1 Activity的Window創建過程
- 8.3.2 Dialog的Window創建過程
- 8.3.3 Toast的Window創建過程
- 第9章 四大組件的工作過程
- 9.1 四大組件的運行狀態
- 9.2 Activity的工作過程
- 9.3 Service的工作過程
- 9.3.1 Service的啟動過程
- 9.3.2 Service的綁定過程
- 9.4 BroadcastReceiver的工作過程
- 9.4.1 廣播的注冊過程
- 9.4.2 廣播的發送和接收過程
- 9.5 ContentProvider的工作過程
- 第10章 Android的消息機制
- 10.1 Android的消息機制概述
- 10.2 Android的消息機制分析
- 10.2.1 ThreadLocal的工作原理
- 10.2.2 消息隊列的工作原理
- 10.2.3 Looper的工作原理
- 10.2.4 Handler的工作原理
- 10.3 主線程的消息循環
- 第11章 Android的線程和線程池
- 11.1 主線程和子線程
- 11.2 Android中的線程形態
- 11.2.1 AsyncTask
- 11.2.2 AsyncTask的工作原理
- 11.2.3 HandlerThread
- 11.2.4 IntentService
- 11.3 Android中的線程池
- 11.3.1 ThreadPoolExecutor
- 11.3.2 線程池的分類
- 第12章 Bitmap的加載和Cache
- 12.1 Bitmap的高效加載
- 12.2 Android中的緩存策略
- 12.2.1 LruCache
- 12.2.2 DiskLruCache
- 12.2.3 ImageLoader的實現446
- 12.3 ImageLoader的使用
- 12.3.1 照片墻效果
- 12.3.2 優化列表的卡頓現象
- 第13章 綜合技術
- 13.1 使用CrashHandler來獲取應用的crash信息
- 13.2 使用multidex來解決方法數越界
- 13.3 Android的動態加載技術
- 13.4 反編譯初步
- 13.4.1 使用dex2jar和jd-gui反編譯apk
- 13.4.2 使用apktool對apk進行二次打包
- 第14章 JNI和NDK編程
- 14.1 JNI的開發流程
- 14.2 NDK的開發流程
- 14.3 JNI的數據類型和類型簽名
- 14.4 JNI調用Java方法的流程
- 第15章 Android性能優化
- 15.1 Android的性能優化方法
- 15.1.1 布局優化
- 15.1.2 繪制優化
- 15.1.3 內存泄露優化
- 15.1.4 響應速度優化和ANR日志分析
- 15.1.5 ListView和Bitmap優化
- 15.1.6 線程優化
- 15.1.7 一些性能優化建議
- 15.2 內存泄露分析之MAT工具
- 15.3 提高程序的可維護性