咱們熟知通常 Android 工程師都是在應用層上開發,不會涉及系統源碼,可是若是你想往底層發展,或者深刻插件化、Framework 系統層等開發工做,若是不瞭解 Android 源碼但是不行的,那麼接下來我基於本身的理解跟學習來記錄跟 Android 開發息息相關的源碼分析,大概從 Android 中的 SystemServer 啓動、四大組件啓動、AMS、PMS 等幾個維度來介紹,下面是個人計劃,固然在將來也有可能改變。java
尚未關注的小夥伴,能夠先關注一波,系列文章會持續更新。git
Android 8.0 源碼分析 (一) SystemServer 進程啓動github
Android 8.0 源碼分析 (二) Launcher 啓動緩存
Android 8.0 源碼分析 (三) 應用程序進程建立到應用程序啓動的過程app
Android 8.0 源碼分析 (四) Activity 啓動ide
Android 8.0 源碼分析 (五) Service 啓動函數
Android 8.0 源碼分析 (六) BroadcastReceiver 啓動oop
Android 8.0 源碼分析 (七) ContentProvider 啓動源碼分析
ContentProvider (內容提供者) 屬於四大組件之一,能夠說它是在四大組件中開發者使用率最少的一個,它的做用就是進程間進行數據交互,底層採用 Binder 機制進行進程間通訊。使用方式我這裏就不在具體說明了,想要了解使用的能夠參考 Android:關於ContentProvider的知識都在這裏了! 下面咱們就以分析 ContentProvider 工做流程爲主來進行全面分析。post
下面先來看一個代碼示例,代碼以下:
fun getContactsLists(): MutableList<String> {
var contactsLists: MutableList<String> = mutableListOf<String>()
lateinit var cursor: Cursor
try {
//使用 getContentResolver() 查詢聯繫人列表
cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null)
//對 cursor 進行遍歷
if (cursor != null) {
while (cursor.moveToNext()) {
//獲取姓名
val displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))
//電話號碼
val number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
contactsLists.add("姓名:$displayName 電話:$number")
}
}
cursor.close()
} catch (error: Exception) {
logger.error("error:$error")
}
return contactsLists
}
//測試
val contactsLists = getContactsLists()
contactsLists.forEach { it -> println("經過ContentResolver獲取聯繫人: $contactsLists") }
複製代碼
上面代碼就是經過內容提供者來獲取手機中的通信錄列表,輸出爲:
I/System.out: 經過ContentResolver獲取聯繫人: [姓名:Xiao 電話:110, 姓名:ming 電話:112, 姓名:華爲客服 電話:4008308300]
複製代碼
那麼這一流程內部是怎麼運行的,這纔是咱們這篇文章主要分析內容,有沒有發現上面代碼示例是用 kotlin 代碼寫的例子,若是感興趣的能夠經過我開源的 KotlinGithub APP 來進行學習 (ps: 在 11 月 10 號左右進行開源,能夠先關注個人 GitHub,後續會更新地址)
老規矩,仍是看一下該小節分析流程,這裏以時序圖爲主
經過上面代碼示例想要 query 數據先要拿到 contentResolver
對象,經過父類 getContentResolver()
方法得到,代碼以下:
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
複製代碼
這裏的 mBase 是 Context 對象,根據前面幾篇文章咱們知道,它的實現類就是 ContextImpl
咱們直接看它具體實現,代碼以下:
//ContextImpl.java
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
複製代碼
這裏的 getContentResolver
方法中返回了 ApplicationContentResolver
對象,它是 ContextImpl 的靜態內部類,繼承自 ContentResolver
,它在 ContextImpl
的構造方法中被建立,這說明當咱們調用它的 query、insert、update
方法的時候,就會啓動 ContentProvider,這裏以上面咱們示例 query
來進行分析,咱們看它的具體實現,代碼以下:
//ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs, @Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
/** * 1. 拿到 IContentProvider 對象,它是 ContentProvider 的本地代理 */
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
try {
/** * 2. 調用 IContentProvider 的 query 函數來進行 query */
qCursor = unstableProvider.query(mPackageName, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
//...
return wrapper;
} catch (RemoteException e) {
return null;
} finally {
...
}
}
複製代碼
在註釋處經過 acquireUnstableProvider
方法拿到 ContentProvider 的本地代理對象,而後註釋 2 調用本地代理對象的 query 方法返回一個 Cursor
對象,咱們先來看下注釋 1 的 acquireUnstableProvider
方法怎麼拿到 ContentProvider 本地代理對象,代碼以下:
//ContentResolver.java
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
//調用內部抽象方法
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
/** @hide */
protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
複製代碼
經過上面代碼 acquireUnstableProvider
返回的是一個抽象函數,具體實現交於子類實現,這裏的子類也就是 ApplicationContentResolver
對象,咱們看下它的具體實現,代碼以下:
//ContextImpl.java
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
複製代碼
經過該方法內部 return 返回的對象,首先拿到 mMainThread
對象,而後調用它內部的 acquireProvider 方法,具體咱們看下它的實現,代碼以下:
//ActivityThread.java
public final IContentProvider acquireProvider( Context c, String auth, int userId, boolean stable) {
/** * 1. acquireExistingProvider 方法主要檢查 ActivityThread 全局變量 mProviderMap 中是否有目標 ContentProvider 存在,有就返回,沒有就經過註釋 2 處獲取, */
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;
try {
/** * 2. 調用 IAcitivityManager 獲取 ContentProviderHolder 對象 */
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
/** * 3. 用來安裝 ContentProvider */
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
複製代碼
註釋 1 處的 acquireExistingProvider
方法內部會檢查 ActivityThread 的全局變量 mProviderMap 中是否有 ContentProvider 存在,若是有則返回,沒有就調用註釋 2 的 IActivityManager 的 getContentProvider
方法與 AMS 進行通訊來獲取,註釋 3 是安裝 ContentProvider,並將 ContentProvider 相關數據存儲在 mProviderMap
中,起到緩存做用,這樣使用相同的內容提供者時,就不須要每次調用 AMS 來獲取了,AMS 的 getContentProvider
方法具體實現,代碼以下:
//AMS.java
@Override
public final ContentProviderHolder getContentProvider( IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
...
//調動內部 getContentProviderImpl 方法
return getContentProviderImpl(caller, name, null, stable, userId);
}
複製代碼
//AMS.java
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller, String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
...
/** * 1. 獲取目標 ContentProvider 應用程序進程的信息,若是進程已經啓動就調用註釋 2 ,不然調用註釋 3 */
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
"Installing in existing process " + proc);
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
/** * 2. 調用 IApplicationThread scheduleInstallProvider 函數 */
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
/** * 3. 啓動新進程 */
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": process is bad");
return null;
}
}
cpr.launchingApp = proc;
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
....
}
複製代碼
上面的代碼工做流程就是先判斷 ContentProvider 所在的應用程序進程是否啓動,若是啓動就調用註釋 2 ,若是沒有啓動就調用註釋 3 ,由於前面 3 個組件都是直接走的應用程序進程啓動的狀況,那麼最後這個組件咱們就以應用程序進程沒有啓動的狀況下,看怎麼執行的。註釋 2 我這裏就大概歸納下執行流程,首先調用 ActivityThread 的內部類 IApplication 的 scheduleInstallProvider
函數,而後經過 H sendMessage 通知進行安裝 ContentProvider 。下面咱們直接看註釋 3 吧,代碼以下:
//AMS.java
final ProcessRecord startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting, boolean isolated, boolean keepIfLarge) {
return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}
複製代碼
下面建立進程代碼分析我就不在繼續跟了,最後會執行 ActivityThread 的 main 函數,這裏啓動進程源碼執行流程能夠看我另外一篇文章 Android 8.0 源碼分析 (三) 應用程序進程建立到應用程序啓動的過程 ,咱們直接看 ActivityThread main 函數,代碼以下
//ActivityThread.java
//經過反射調用執行的
public static void main(String[] args) {
....
//主線程消息循環
Looper.prepareMainLooper();
//建立 ActivityThread 對象
ActivityThread thread = new ActivityThread();
//Application,Activity 入口
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();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
主要看 ActivityThread attach 函數具體實現,代碼以下:
//ActivityThread.java
private void attach(boolean system) {
...
//1.
final IActivityManager mgr = ActivityManager.getService();
try {
//2. 關聯 Application
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
}
複製代碼
註釋 1 獲得 AMS 的代理類 IActivityManager ,在註釋 2 處調用 AMS 的 attachApplication
函數,並將 IApplicationThread 對象傳入 AMS 保持應用進程和 AMS 跨進程通訊,應用程序調用 AMS 的過程就分析完了,下面咱們分析 AMS 到應用程序進程的 ContentProvider 安裝過程。
老規矩,看來看一下時序圖:
上一小節咱們知道,應用程序進程啓動成功以後會通知 AMS 進程 調用 attachApplication 方法,代碼以下:
//AMS.java
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
...
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
...
}
複製代碼
在 attachApplicationLocked
函數中調用了 thread.bindApplication 方法,thread 是 IApplicationThread ,這裏和 IActivityManager 同樣採用了 aidl 進行進程間傳輸數據,咱們回到 ActivityThread 內部類 ApplicationThread 的 bindApplication
方法,代碼以下:
//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 services, Bundle coreSettings, String buildSerial) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
setCoreSettings(coreSettings);
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.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
//發送消息給 H 類
sendMessage(H.BIND_APPLICATION, data);
}
複製代碼
ActivityThread 內部類 H 收到消息,開始處理 BIND_APPLICSTION
消息,代碼以下:
//ActivityThread.java
...
public void handleMessage(Message msg) {
...
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
...
}
...
複製代碼
調用內部 handleBindApplication
方法,代碼以下:
//ActivityThread.java
private void handleBindApplication(AppBindData data) {
...
/** * 1. 建立 ContentImpl */
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
...
/** * 2. */
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
...
}
final ComponentName component = new ComponentName(ii.packageName, ii.name);
/** * 3. */
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
....
try {
/** * 4. */
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
/** * 5. */
installContentProviders(app, data.providers);
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
...
}
try {
/** * 6. */
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
}
複製代碼
上面代碼咱們按照我標註的註釋來分析,註釋 1 建立了 Context 的實現類 ContentImp了 這個類相信你們都很熟悉了吧,它就是四大組件中父類 Context 的實現類。註釋 2 處經過反射建立 Instrumentation 並在註釋 3 處初始化 Instrumentaion, 在註釋 4 處建立 Application 並在註釋 6 處調用 Application onCreate 生命週期方法,這說明 ContentProvider 已經在中間啓動了,咱們直接看上面註釋 5 看 ContentProvider 是如何啓動的,代碼以下:
//ActivityThread.java
private void installContentProviders( Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
/** * 1. */
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());
}
/** * 2. */
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
/** * 3. */
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
複製代碼
能夠看到上面代碼主要分爲 3 個步驟,註釋 1 遍歷當前應用程序進程的 ProviderInfo 列表,獲得每一個 ContentProvicer 的存儲信息,並在註釋 2 調用 installProvider 方法來啓動這些 ContentProvider .註釋 3 處是將啓動了的 ContentProvider 存入 AMS 的 mProviderMap
中,這個全局變量在上一小節介紹過,就是用來緩存啓動過的 ContentProvidec,下面咱們來看上面的註釋 2 ,代碼以下:
//ActivityThread.java
private ContentProviderHolder installProvider(Context context, ContentProviderHolder holder, ProviderInfo info, boolean noisy, boolean noReleaseNeeded, boolean stable) {
....
try {
final java.lang.ClassLoader cl = c.getClassLoader();
/** * 1. */
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
...
/** * 2. */
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
if (!mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ": " + e.toString(), e);
}
return null;
}
....
}
複製代碼
註釋 1 經過反射實例化 ContentProvider
對象,並在註釋 2 處調用它的 attachInfo
方法,代碼以下:
//ContentProvider.java
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
if (mContext == null) {
mContext = context;
if (context != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
/** * 安裝成功,調用生命週期函數 onCreate */
ContentProvider.this.onCreate();
}
}
public abstract boolean onCreate();
複製代碼
能夠看到最後在 ContentProvider 的 attachInfo
函數中進行調用了抽象方法 onCreate, 那麼它的子類就會進行實現 onCreate 達到啓動成功的通知。
到這裏四大組件都已經講解完成了,但願這幾篇文章能帶給閱讀者一些收穫,感謝你們閱讀。