相關文章
Android深刻理解四大組件系列html
Content Provider作爲四大組件之一,一般狀況下並無其餘的組件使用頻繁,但這不能做爲咱們不去深刻學習它的理由。關於Content Provider一篇文章是寫不完的,這一篇文章先來介紹它的啓動過程。java
在Android IPC機制(四)用ContentProvider進行進程間通訊這篇文章我舉了一個Content Provider使用的例子,在Activity中我是使用以下代碼調用Content Provider的:android
public class ContentProviderActivity extends AppCompatActivity {
private final static String TAG = "ContentProviderActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_provider);
Uri uri = Uri.parse("content://com.example.liuwangshu.mooncontentprovide.GameProvider");
ContentValues mContentValues = new ContentValues();
mContentValues.put("_id", 2);
mContentValues.put("name", "大航海時代ol");
mContentValues.put("describe", "最好玩的航海網遊");
getContentResolver().insert(uri, mContentValues);//1
Cursor gameCursor = getContentResolver().query(uri, new String[]{"name", "describe"}, null, null, null);
...
}
}複製代碼
要想調用Content Provider,首先須要使用註釋1處的getContentResolver方法,以下所示。
frameworks/base/core/Java/android/content/ContextWrapper.java緩存
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}複製代碼
這裏mBase指的是ContextImpl,ContextImpl的getContentResolver方法以下所示。微信
frameworks/base/core/java/android/app/ContextImpl.javaapp
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}複製代碼
上面的代碼return了ApplicationContentResolver類型的mContentResolver對象,ApplicationContentResolver是ContextImpl中的靜態內部類,繼承自ContentResolver,它在ContextImpl的構造方法中被建立。
當咱們調用ContentResolver的insert、query、update等方法時就會啓動Content Provider,這裏拿query方法來進行舉例。
query方法的實如今ApplicationContentResolver的父類ContentResolver中,代碼以下所示。
frameworks/base/core/java/android/content/ContentResolver.javaide
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");
IContentProvider unstableProvider = acquireUnstableProvider(uri);//1
...
try {
...
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);//2
} catch (DeadObjectException e) {
...
}
...
}複製代碼
在註釋1處經過acquireUnstableProvider方法返回IContentProvider類型的unstableProvider對象,在註釋2處調用unstableProvider的query方法。咱們查看acquireUnstableProvider方法作了什麼,以下所示。
frameworks/base/core/java/android/content/ContentResolver.javaoop
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {//1
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
return acquireUnstableProvider(mContext, uri.getAuthority());//2
}
return null;
}複製代碼
註釋1處用來檢查Uri的scheme是否等於"content",若是不是則返回null。註釋2處調用了acquireUnstableProvider方法,這是個抽象方法,它的實如今ContentResolver的子類ApplicationContentResolver中:
frameworks/base/core/java/android/app/ContextImpl.java學習
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}複製代碼
return了ActivityThread類型的mMainThread對象的acquireProvider方法:
frameworks/base/core/java/android/app/ActivityThread.javaui
public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);//1
if (provider != null) {
return provider;
}
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);//2
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);//3
return holder.provider;
}複製代碼
註釋1處檢查ActivityThread中的ArrayMap類型的mProviderMap中是否有目標ContentProvider存在,有則返回,沒有就會在註釋2處調用AMP的getContentProvider方法,最終會調用AMS的getContentProvider方法。註釋3處的installProvider方法用來將註釋2處返回的ContentProvider相關的數據存儲在mProviderMap中,起到緩存的做用,這樣使用相同的Content Provider時,就不須要每次都要調用AMS的getContentProvider方法。使用咱們接着查看AMS的getContentProvider方法,代碼以下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) {
...
return getContentProviderImpl(caller, name, null, stable, userId);
}複製代碼
getContentProvider方法return了getContentProviderImpl方法:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) {
...
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);//1
if (proc != null && proc.thread != null && !proc.killed) {
...
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
proc.thread.scheduleInstallProvider(cpi);//2
} catch (RemoteException e) {
}
}
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);//3
checkTime(startTime, "getContentProviderImpl: after start process");
...
}
...
}複製代碼
getContentProviderImpl方法的代碼不少,這裏截取了關鍵的部分。註釋1處經過getProcessRecordLocked方法來獲取目標ContentProvider的應用程序進程信息,這些信息用ProcessRecord類型的proc來表示,若是該應用進程已經啓動就會調用註釋2處的代碼,不然就會調用註釋3的startProcessLocked方法來啓動進程。這裏咱們假設ContentProvider的應用進程尚未啓動,關於應用進程啓動過程,我在Android應用程序進程啓動過程(前篇)已經講過,最終會調用ActivityThread的main方法,代碼以下所示。
frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
...
Looper.prepareMainLooper();//1
ActivityThread thread = new ActivityThread();//2
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();//3
throw new RuntimeException("Main thread loop unexpectedly exited");
}複製代碼
註釋1處經過prepareMainLooper方法在ThreadLocal中獲取Looper,並在註釋3處開啓消息循環。在註釋2處建立了ActivityThread並調用了它的attach方法:
frameworks/base/core/java/android/app/ActivityThread.java
private void attach(boolean system) {
...
final IActivityManager mgr = ActivityManagerNative.getDefault();//1
try {
mgr.attachApplication(mAppThread);//2
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
}複製代碼
註釋1處最終會獲得AMS,在註釋2處調用AMS的attachApplication方法,並將ApplicationThread類型的mAppThread對象傳進去。
query方法到AMS的調用過程,以下面時序圖所示(省略應用程序進程啓動過程)。
咱們接着來查看AMS的attachApplication方法,以下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}複製代碼
attachApplication方法中又調用了attachApplicationLocked方法:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
...
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
...
}複製代碼
attachApplicationLocked方法中調用了thread的bindApplication方法,thread是IApplicationThread類型的,從類型名字就能夠看出來是用於進程間通訊,這裏實現bindApplication方法的是ApplicationThreadProxy類,它實現了IApplicationThread接口。
frameworks/base/core/java/android/app/ApplicationThreadNative.java
class ApplicationThreadProxy implements IApplicationThread {
...
@Override
public final void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers, ComponentName testName, ProfilerInfo profilerInfo, Bundle testArgs, IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
...
mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
}
...
}複製代碼
到目前爲止,上面的調用過程仍是在AMS進程中執行的,所以,須要經過IBinder類型的mRemote對象向新建立的應用程序進程(目標Content Provider所在的進程)發送BIND_APPLICATION_TRANSACTION類型的通訊請求。處理這個通訊請求的是在新建立的應用程序進程中執行的ApplicationThread的bindApplication方法,以下所示。
frameworks/base/core/java/android/app/ActivityThread.java
public final void bindApplication(String processName, ApplicationInfo appInfo, List<ProviderInfo> providers, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) {
...
sendMessage(H.BIND_APPLICATION, data);
}複製代碼
調用sendMessage方法像H發送BIND_APPLICATION類型消息,H的handleMessage方法以下所示。
frameworks/base/core/java/android/app/ActivityThread.java
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
...
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
...
}
...
}複製代碼
咱們接着查看handleBindApplication方法:
frameworks/base/core/java/android/app/ActivityThread.java
private void handleBindApplication(AppBindData data) {
...
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);//1
try {
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();//2
} catch (Exception e) {
...
}
final ComponentName component = new ComponentName(ii.packageName, ii.name);
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);//3
...
Application app = data.info.makeApplication(data.restrictedBackupMode, null);//4
mInitialApplication = app;
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);//5
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
...
mInstrumentation.callApplicationOnCreate(app);//6
...
}複製代碼
handleBindApplication方法的代碼很長,這裏截取了主要的部分。註釋1處建立了ContextImpl 。註釋2處經過反射建立Instrumentation並在註釋3處初始化Instrumentation。註釋4處建立Application而且在註釋6處調用Application的onCreate方法,這意味着Content Provider所在的應用程序進程已經啓動完畢,在這以前,註釋5處調用installContentProviders方法來啓動Content Provider,代碼以下所示。
frameworks/base/core/java/android/app/ActivityThread.java
private void installContentProviders( Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
for (ProviderInfo cpi : providers) {//1
...
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);//2
...
}
try {
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);//3
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}複製代碼
註釋1處遍歷當前應用程序進程的ProviderInfo列表,獲得每一個Content Provider的ProviderInfo(存儲Content Provider的信息),並在註釋2處調用installProvider方法來啓動這些Content Provider。在註釋3處經過AMS的publishContentProviders方法將這些Content Provider存儲在AMS的mProviderMap中,這個mProviderMap在前面提到過,起到緩存的做用,防止每次使用相同的Content Provider時都會調用AMS的getContentProvider方法。來查看installProvider方法時如何啓動Content Provider的,installProvider方法以下所示。
frameworks/base/core/java/android/app/ActivityThread.java
private IActivityManager.ContentProviderHolder installProvider(Context context, IActivityManager.ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
...
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();//1
provider = localProvider.getIContentProvider();
if (provider == null) {
...
return null;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
localProvider.attachInfo(c, info);//2
} catch (java.lang.Exception e) {
...
}
return null;
}
}
...
return retHolder;
}複製代碼
在註釋1處經過反射來建立ContentProvider類型的localProvider對象,並在註釋2處調用了它的attachInfo方法:
frameworks/base/core/java/android/content/ContentProvider.java
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
...
ContentProvider.this.onCreate();
}
}複製代碼
在attachInfo方法中調用了onCreate方法,它是一個抽象方法。這樣Content Provider就啓動完畢。
最後給出AMS啓動Content Provider的時序圖。
歡迎關注個人微信公衆號,第一時間得到博客更新提醒,以及更多成體系的Android相關原創技術乾貨。
掃一掃下方二維碼或者長按識別二維碼,便可關注。