這是 Android 9.0 AOSP 系列 的第五篇了,先來回顧一下前面幾篇的大體內容。java
Java 世界的盤古和女媧 —— Zygoteandroid
主要介紹了 Android 世界的第一個 Java 進程 Zygote
的啓動過程。git
主要介紹了 Zygote 進程 fork 的第一個進程 SystemServer
,它承載了各種系統服務的建立和啓動。github
語言、時區、地區等設置緩存
虛擬機內存設置微信
指紋信息,Binder 調用設置app
Looper.prepareMainLooper()
,建立主線程 Loopersocket
初始化 native 服務,加載 libandroid_servers.so
async
createSystemContext()
,初始化系統上下文
建立系統服務管理者 SystemServiceManager
startBootstrapServices
,啓動系統引導服務
startCoreServices
,啓動系統核心服務
startOtherServices
,啓動其餘服務
Looper.loop()
,開啓消息循環
在 startOtherServices
的最後會調用 AMS 的 onSystemReady()
方法啓動桌面 Activity。
主要介紹了 AMS 向 Zygote 請求建立應用進程的過程,即向 Zygote 進程進行 socket 通訊,與第一篇呼應。
調用 Process.start()
建立應用進程
ZygoteProcess
負責和 Zygote
進程創建 socket 鏈接,並將建立進程須要的參數發送給 Zygote 的 socket 服務端
Zygote
服務端接收到參數以後調用 ZygoteConnection.processOneCommand()
處理參數,並 fork 進程
最後經過 findStaticMain()
找到 ActivityThread
類的 main() 方法並執行,子進程就啓動了
主要介紹了 ActivityManagerService (AMS)
的啓動流程,它與四大組件的啓動,切換,調度以及應用進程的管理息息相關。
AMS 初始化,經過 ActivityManagerService.Lifecycle
的構造函數中初始化
setSystemProcess()
,註冊各類服務,建立 ProcessRecord,更新 oom_adj 值
安裝系統 Provider
systemReady()
,最終會啓動桌面 Home Activity
今天要介紹的就是 Activity 的啓動流程了。Activity 的啓動是個大工程,細節十分之多。這篇文章會簡單梳理整個啓動流程,不會過分深刻源碼細節。對其中的關鍵問題,如 launchMode 的處理,生命週期的處理,後續會經過單獨的文章深刻剖析。
先來一張流程圖,對照着看更方便理解。
接着以前的分析,ActivityManagerService 的 systemReady()
方法中最後會去啓動桌面 Hme Activity,調用的方法是 startHomeActivityLocked
。
> ActivityManagerService.java boolean startHomeActivityLocked(int userId, String reason) {
......
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
......
if (app == null || app.instr == null) {
intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
// 啓動桌面 Activity
mActivityStartController.startHomeActivity(intent, aInfo, myReason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}
return true;
}
複製代碼
調用 ActivityStartController
的 startHomeActivity()
方法:
> ActivityStartController.java void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(reason);
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
.setCallingUid(0)
.setActivityInfo(aInfo)
.execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
if (mSupervisor.inResumeTopActivity) {
mSupervisor.scheduleResumeTopActivities();
}
}
複製代碼
obtainStarter()
方法返回的是 ActivityStarter
對象,它負責 Activity 的啓動,一系列 setXXX()
方法傳入啓動所需的各類參數,最後的 execute()
是真正的啓動邏輯。
在繼續看源碼以前,先思考一下如今處於哪一個進程?AMS 是在 system_server
進程中初始化的,因此上面的工做都是在 system_server
進程發生的。而咱們一般在開發過程當中使用的 startActivity()
方法顯然是在應用進程調用的。那麼,普通的 startActivity()
方法又是怎麼樣的調用鏈呢?跟進 Activity.startActivity()
方法來看一下。
> Activity.java
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
// 調用 Instrumentation.execStartActivity() 方法
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
// 回調 ActivityResult
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
} else {
// 最終也是調用 Instrumentation.execStartActivity() 方法
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
複製代碼
最終都會調用 Instrumentation
的 execStartActivity()
方法。Instrumentation 是個很是重要的類,Activity 的啓動,生命週期的回調都離不開它。後面會屢次遇到這個類。
> Instrumentation.java public ActivityResult execStartActivity( Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
......
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
// Binder 調用 AMS 來啓動 Activity
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
// 檢測啓動結果
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
複製代碼
這裏經過 Binder 調用 AMS 的 startActivity()
方法。ActivityManager.getService()
不用多想確定是獲取 AMS 代理對象的。
> ActivityManager.java public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
複製代碼
接着就進入到 AMS 的 startActivity() 方法。
> ActivityManagerService.java
@Override
public final int startActivity(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {setMayWait return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, UserHandle.getCallingUserId());
}
public final int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
enforceNotIsolatedCaller("startActivity");
userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
// TODO: Switch to user app stacks here.
return mActivityStartController.obtainStarter(intent, "startActivityAsUser") // 獲取 ActivityStarter 對象
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute();
}
複製代碼
接下來和以前啓動 Home Activity 比較類似了。獲取 ActivityStarter
對象,提供參數,最後 execute()
。
obtainStarter()
經過工廠模式獲取 ActivityStarter 對象。
ActivityStarter obtainStarter(Intent intent, String reason) {
return mFactory.obtain().setIntent(intent).setReason(reason);
}
複製代碼
mFactory 的默認實現是 ActivityStarter.DefaultFactory
。
> ActivityStarter.java
static class DefaultFactory implements Factory {
/** * The maximum count of starters that should be active at one time: * 1. last ran starter (for logging and post activity processing) * 2. current running starter * 3. starter from re-entry in (2) * * 同時激活的 starter 最多隻能有三個。 */
private final int MAX_STARTER_COUNT = 3;
private ActivityStartController mController;
private ActivityManagerService mService;
private ActivityStackSupervisor mSupervisor;
private ActivityStartInterceptor mInterceptor;
private SynchronizedPool<ActivityStarter> mStarterPool =
new SynchronizedPool<>(MAX_STARTER_COUNT);
DefaultFactory(ActivityManagerService service,
ActivityStackSupervisor supervisor, ActivityStartInterceptor interceptor) {
mService = service;
mSupervisor = supervisor;
mInterceptor = interceptor;
}
@Override
public void setController(ActivityStartController controller) {
mController = controller;
}
@Override
public ActivityStarter obtain() {
// 從同步對象池 SynchronizedPool 中獲取
ActivityStarter starter = mStarterPool.acquire();
if (starter == null) {
starter = new ActivityStarter(mController, mService, mSupervisor, mInterceptor);
}
return starter;
}
@Override
public void recycle(ActivityStarter starter) {
starter.reset(true /* clearRequest*/);
mStarterPool.release(starter);
}
}
複製代碼
提供了一個容量爲 3 的同步對象緩存池來緩存 ActivityStarter 對象。setXXX()
方法均爲參數配置,注意 setMayWait
方法會將 mayWait
參數置爲 true。咱們直接看它的實際執行過程,execute()
函數。
> ActivityStarter.java int execute() {
try {
if (mRequest.mayWait) { // setMayWait() 方法中將 mayWait 置爲 true
return startActivityMayWait(mRequest.caller, mRequest.callingUid,
mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
mRequest.inTask, mRequest.reason,
mRequest.allowPendingRemoteAnimationRegistryLookup,
mRequest.originatingPendingIntent);
} else {
return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
mRequest.outActivity, mRequest.inTask, mRequest.reason,
mRequest.allowPendingRemoteAnimationRegistryLookup,
mRequest.originatingPendingIntent);
}
} finally {
// 回收當前 ActivityStarter 對象
onExecutionComplete();
}
}
複製代碼
接着調用 startActivityMayWait()
。
> ActivityStarter.java private int startActivityMayWait(IApplicationThread caller, int callingUid, String callingPackage, Intent intent, String resolvedType, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, WaitResult outResult, Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity, int userId, TaskRecord inTask, String reason, boolean allowPendingRemoteAnimationRegistryLookup, PendingIntentRecord originatingPendingIntent) {
.....
// Save a copy in case ephemeral needs it
final Intent ephemeralIntent = new Intent(intent);
// Don't modify the client's object!
// 從新建立,不修改客戶端原來的 intent
intent = new Intent(intent);
if (componentSpecified
&& !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
&& !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
&& !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
&& mService.getPackageManagerInternalLocked()
.isInstantAppInstallerComponent(intent.getComponent())) {
intent.setComponent(null /*component*/);
componentSpecified = false;
}
// 獲取 ResolveInfo
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
0 /* matchFlags */,
computeResolveFilterUid(
callingUid, realCallingUid, mRequest.filterCallingUid));
......
// Collect information about the target of the Intent.
// 獲取目標 Intent 的 ActivityInfo
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
synchronized (mService) {
final ActivityStack stack = mSupervisor.mFocusedStack;
stack.mConfigWillChange = globalConfig != null
&& mService.getGlobalConfiguration().diff(globalConfig) != 0;
......
final ActivityRecord[] outRecord = new ActivityRecord[1];
// 調用 startActivity() 方法
int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
allowPendingRemoteAnimationRegistryLookup, originatingPendingIntent);
Binder.restoreCallingIdentity(origId);
......
if (outResult != null) {
// 設置啓動結果
outResult.result = res;
final ActivityRecord r = outRecord[0];
switch(res) {
case START_SUCCESS: {
mSupervisor.mWaitingActivityLaunched.add(outResult);
do {
try {
// 等待啓動結果
mService.wait();
} catch (InterruptedException e) {
}
} while (outResult.result != START_TASK_TO_FRONT
&& !outResult.timeout && outResult.who == null);
if (outResult.result == START_TASK_TO_FRONT) {
res = START_TASK_TO_FRONT;
}
break;
}
......
break;
}
}
}
return res;
}
}
複製代碼
調動 startActivity()
方法來啓動 Activity,它有兩個重載方法被依次調用。這裏會等待啓動結果。
> ActivityStarter.java private int startActivity(IApplicationThread caller, Intent intent, Intent ephemeralIntent, String resolvedType, ActivityInfo aInfo, ResolveInfo rInfo, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, int realCallingPid, int realCallingUid, int startFlags, SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity, TaskRecord inTask, boolean allowPendingRemoteAnimationRegistryLookup, PendingIntentRecord originatingPendingIntent) {
int err = ActivityManager.START_SUCCESS;
ProcessRecord callerApp = null;
if (caller != null) {
// caller 不爲空時,經過 AMS 查找 ProcessRecord
callerApp = mService.getRecordForAppLocked(caller);
if (callerApp != null) {
callingPid = callerApp.pid;
callingUid = callerApp.info.uid;
} else {
err = ActivityManager.START_PERMISSION_DENIED;
}
}
// sourceRecord 用於描述發起本次請求的 Activity
// resultRecord 用戶描述接收啓動結果的 Activity
// 通常狀況下,這兩個 Activity 應該是同一個
ActivityRecord sourceRecord = null;
ActivityRecord resultRecord = null;
......
// 獲取啓動標誌
final int launchFlags = intent.getFlags();
......
if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
// 未找到能夠處理該 intent 的類
err = ActivityManager.START_INTENT_NOT_RESOLVED;
}
if (err == ActivityManager.START_SUCCESS && aInfo == null) {
// 沒有找到 intent 中指定的 Activity 類
err = ActivityManager.START_CLASS_NOT_FOUND;
}
......
// 權限檢查
boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
inTask != null, callerApp, resultRecord, resultStack);
abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
callingPid, resolvedType, aInfo.applicationInfo);
......
// 構建 ActivityRecord
ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
mSupervisor, checkedOptions, sourceRecord);
......
// 獲取當前獲取焦點的 ActivityStack
final ActivityStack stack = mSupervisor.mFocusedStack;
// 若是啓動一個和當前處於 resume 狀態的 activity 不一樣 uid 的新 activity,要檢查是否容許 app 切換
if (voiceSession == null && (stack.getResumedActivity() == null
|| stack.getResumedActivity().info.applicationInfo.uid != realCallingUid)) {
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
realCallingPid, realCallingUid, "Activity start")) {
mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
sourceRecord, startFlags, stack, callerApp));
ActivityOptions.abort(checkedOptions);
// 不容許切換,直接返回
return ActivityManager.START_SWITCHES_CANCELED;
}
}
......
// 調用重載方法
return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
true /* doResume */, checkedOptions, inTask, outActivity);
}
複製代碼
> ActivityStarter.java private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity) {
int result = START_CANCELED;
try {
// 延時佈局
mService.mWindowManager.deferSurfaceLayout();
// 調用 startActivityUnchecked() 方法
result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity);
} finally {
final ActivityStack stack = mStartActivity.getStack();
if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
null /* intentResultData */, "startActivity", true /* oomAdj */);
}
// 恢復佈局
mService.mWindowManager.continueSurfaceLayout();
}
postStartActivityProcessing(r, result, mTargetStack);
return result;
}
複製代碼
接着調用 startActivityUnchecked()
。
> ActivityStarter.java private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity) {
// 設置啓動 Activity 的初始狀態,包括 flag
setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
voiceInteractor);
// 計算 mLaunchFlags ,啓動標誌位
computeLaunchingTaskFlags();
// 計算 mSourceStack
computeSourceStack();
// 設置啓動標誌位
mIntent.setFlags(mLaunchFlags);
// 查找可複用的 Activity
ActivityRecord reusedActivity = getReusableIntentActivity();
......
// 不等於 null 說明新的 activity 應該插入已存在的任務棧中
if (reusedActivity != null) {
if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
(mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
return START_RETURN_LOCK_TASK_MODE_VIOLATION;
}
final boolean clearTopAndResetStandardLaunchMode =
(mLaunchFlags & (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED))
== (FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
&& mLaunchMode == LAUNCH_MULTIPLE;
if (mStartActivity.getTask() == null && !clearTopAndResetStandardLaunchMode) {
mStartActivity.setTask(reusedActivity.getTask());
}
if (reusedActivity.getTask().intent == null) {
reusedActivity.getTask().setIntent(mStartActivity);
}
if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
|| isDocumentLaunchesIntoExisting(mLaunchFlags)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK)) {
final TaskRecord task = reusedActivity.getTask();
// 清空任務棧
final ActivityRecord top = task.performClearTaskForReuseLocked(mStartActivity,
mLaunchFlags);
if (reusedActivity.getTask() == null) {
reusedActivity.setTask(task);
}
if (top != null) {
if (top.frontOfTask) {
top.getTask().setIntent(mStartActivity);
}
// 觸發 onNewIntent()
deliverNewIntent(top);
}
}
......
// 是否建立新的 task
boolean newTask = false;
final TaskRecord taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTask() : null;
......
// 將要啓動的 Activity 在 Task 中置頂
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
mOptions);
if (mDoResume) {
final ActivityRecord topTaskActivity =
mStartActivity.getTask().topRunningActivityLocked();
if (!mTargetStack.isFocusable()
|| (topTaskActivity != null && topTaskActivity.mTaskOverlay
&& mStartActivity != topTaskActivity)) {
mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mService.mWindowManager.executeAppTransition();
} else {
if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
// 調用 ActivityStackSupervisor.resumeFocusedStackTopActivityLocked() 方法
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
} else if (mStartActivity != null) {
mSupervisor.mRecentTasks.add(mStartActivity.getTask());
}
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredWindowingMode,
preferredLaunchDisplayId, mTargetStack);
return START_SUCCESS;
}
複製代碼
startActivityUnchecked()
方法主要除了處理了啓動標記 flag ,要啓動的任務棧等。這一塊源碼很長,上面做了大量刪減,僅保留了基本的調用鏈。感興趣的同窗能夠自行查看源文件。接下來 調用了 ActivityStackSupervisor
的 resumeFocusedStackTopActivityLocked()
方法。
> ActivityStackSupervisor.java boolean resumeFocusedStackTopActivityLocked( ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if (!readyToResume()) {
return false;
}
// 目標 Stack 就是 mFocusedStack
if (targetStack != null && isFocusedStack(targetStack)) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
// 獲取 mFocusedStack 棧頂的 ActivityRecord
final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
if (r == null || !r.isState(RESUMED)) {
mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
} else if (r.isState(RESUMED)) {
// Kick off any lingering app transitions form the MoveTaskToFront operation.
mFocusedStack.executeAppTransition(targetOptions);
}
return false;
}
複製代碼
獲取待啓動 Activity 的 ActivityStack 以後並調用其 resumeTopActivityUncheckedLocked()
方法。
> ActivityStack.java boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
if (mStackSupervisor.inResumeTopActivity) {
// 防止遞歸啓動
return false;
}
boolean result = false;
try {
mStackSupervisor.inResumeTopActivity = true;
// 執行 resumeTopActivityInnerLocked() 方法)
result = resumeTopActivityInnerLocked(prev, options);
final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
if (next == null || !next.canTurnScreenOn()) {
checkReadyForSleep();
}
} finally {
mStackSupervisor.inResumeTopActivity = false;
}
return result;
}
複製代碼
> ActivityStack.java private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
if (!mService.mBooting && !mService.mBooted) {
// AMS 還未啓動完成
return false;
}
......
if (!hasRunningActivity) {
// 當前 Stack 沒有 activity,就去找下一個 stack。可能會啓動 Home 應用
return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
}
// next 就是目標 Activity,將其從下面幾個隊列移除
mStackSupervisor.mStoppingActivities.remove(next);
mStackSupervisor.mGoingToSleepActivities.remove(next);
next.sleeping = false;
mStackSupervisor.mActivitiesWaitingForVisibleActivity.remove(next);
......
// mResumedActivity 指當前 Activity
if (mResumedActivity != null) {
// 當有其餘 Activity 正處於 onResume(),先暫停它
pausing |= startPausingLocked(userLeaving, false, next, false);
}
......
ActivityStack lastStack = mStackSupervisor.getLastStack();
if (next.app != null && next.app.thread != null) {
......
synchronized(mWindowManager.getWindowManagerLock()) {
// This activity is now becoming visible.
if (!next.visible || next.stopped || lastActivityTranslucent) {
next.setVisibility(true);
}
......
try {
final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread,
next.appToken);
// Deliver all pending results.
ArrayList<ResultInfo> a = next.results;
if (a != null) {
final int N = a.size();
if (!next.finishing && N > 0) {
if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
"Delivering results to " + next + ": " + a);
transaction.addCallback(ActivityResultItem.obtain(a));
}
}
if (next.newIntents != null) {
transaction.addCallback(NewIntentItem.obtain(next.newIntents,
false /* andPause */));
}
next.sleeping = false;
mService.getAppWarningsLocked().onResumeActivity(next);
mService.showAskCompatModeDialogLocked(next);
next.app.pendingUiClean = true;
next.app.forceProcessStateUpTo(mService.mTopProcessState);
next.clearOptionsLocked();
transaction.setLifecycleStateRequest(
ResumeActivityItem.obtain(next.app.repProcState,
mService.isNextTransitionForward()));
mService.getLifecycleManager().scheduleTransaction(transaction);
} catch (Exception e) {
next.setState(lastState, "resumeTopActivityInnerLocked");
// lastResumedActivity being non-null implies there is a lastStack present.
if (lastResumedActivity != null) {
lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
}
Slog.i(TAG, "Restarting because process died: " + next);
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
} else if (SHOW_APP_STARTING_PREVIEW && lastStack != null
&& lastStack.isTopStackOnDisplay()) {
next.showStartingWindow(null /* prev */, false /* newTask */,
false /* taskSwitch */);
}
// 調用 startSpecificActivityLocked()
mStackSupervisor.startSpecificActivityLocked(next, true, false);
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
}
// From this point on, if something goes wrong there is no way
// to recover the activity.
try {
next.completeResumeLocked();
} catch (Exception e) {
......
}
} else {
......
// 調用 startSpecificActivityLocked()
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
return true;
}
複製代碼
上面省略了 resumeTopActivityInnerLocked()
方法中的絕大部分代碼,原代碼大概有四百多行。其中須要注意的是 startPausingLocked()
和 startSpecificActivityLocked()
方法。
在啓動 Activity 以前,若是當前 Activity 正處於 onResume 狀態,那麼須要先暫停它,即調用它的 onPause。這就是 startPausingLocked()
方法的職責。這裏先不具體分析,後面會單獨寫一篇文章說明 Activity 的聲明週期調用。另外多說一句,先要執行當前 Activity 的 onPause 而後纔會啓動目標 Activity ,因此咱們不能在 onPause 中執行耗時任務,會形成切換 Activity 時卡頓。
另外一個方法 startSpecificActivityLocked()
就是啓動指定 Activity 了,咱們繼續跟下去。
> ActivityStackSupervisor.java void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
// 經過 AMS 查找進程是否已存在
ProcessRecord app = mService.getProcessRecordLocked(r.processName,
r.info.applicationInfo.uid, true);
// 應用進程已經存在而且已經綁定
if (app != null && app.thread != null) {
try {
if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
|| !"android".equals(r.info.packageName)) {
app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
mService.mProcessStats);
}
// 應用進程已存在時調用 realStartActivityLocked()
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
// 應用進程不存在則建立進程
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
複製代碼
首先經過 AMS 查找應用進程是否已經存在,若是已經存在而且 attach ,則調用 realStartActivityLocked()
直接啓動目標 Activity 。若是應用進程不存在,則先建立應用進程。
在 Android 世界中,誰喊醒了 Zygote ? 已經介紹過了應用進程的建立過程。這裏再簡單說一下,Zygote
進程啓動時開啓了 LocalSocket 服務端,等待客戶端請求。AMS 做爲 socket 客戶端向 Zygote 發出請求,Zygote 收到請求以後 fork 出子進程。
今天看到一個頗有意思的提問,Android 中的 IPC 通訊大多經過 Binder 機制實現,爲何 Zygote 經過 socket 跨進程通訊? 說實話,我也不知道,歡迎你們留下你的見解。
接着就是 realStartActivityLocked()
,如其名字同樣,真正的要啓動 Activity 了。
> ActivityStackSupervisor.java final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app, boolean andResume, boolean checkConfig) throws RemoteException {
if (!allPausedActivitiesComplete()) {
// 直到全部的 onPause() 執行結束纔會去啓動新的 activity
return false;
}
final TaskRecord task = r.getTask();
final ActivityStack stack = task.getStack();
beginDeferResume();
try {
......
// 更新進程 oom-adj 值
mService.updateLruProcessLocked(app, true, null);
mService.updateOomAdjLocked();
try {
......
// 添加 LaunchActivityItem
final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
r.appToken);
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, mService.isNextTransitionForward(),
profilerInfo));
// 設置生命週期狀態
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// 重點
// // 調用 ClientLifecycleManager.scheduleTransaction()
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
......
} catch (RemoteException e) {
if (r.launchFailed) {
// 第二次啓動失敗,finish activity
mService.appDiedLocked(app);
stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null,
"2nd-crash", false);
return false;
}
// 第一次失敗,重啓進程並重試
r.launchFailed = true;
app.activities.remove(r);
throw e;
}
} finally {
endDeferResume();
}
r.launchFailed = false;
......
return true;
}
複製代碼
上面的重點是這句代碼,mService.getLifecycleManager().scheduleTransaction(clientTransaction);
。
這裏又用到了 ClientTransaction
。還記得上面提到的暫停 Activity 嗎 ,也是經過這個類來實現的。原本準備寫到生命週期的單獨文章再分析,看來仍是逃不過。這裏穿插着說一下 ClientTransaction 。
首先 mService.getLifecycleManager()
返回的是 ClientLifecycleManager
對象,這是在 Android 9.0 中新增的類。咱們看一下它的 scheduleTransaction()
方法。
> ClientLifecycleManager.java void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
final IApplicationThread client = transaction.getClient(); // -> ApplicationThread
transaction.schedule(); // ClientTransaction
if (!(client instanceof Binder)) {
transaction.recycle();
}
}
複製代碼
跟進 schedule()
方法。
> ClientTransaction.java public void schedule() throws RemoteException {
mClient.scheduleTransaction(this);
}
複製代碼
這裏的 mClient
是 IApplicationThread 類型,它是 ApplicationThread
的 Binder 代理對象,因此這裏會跨進程調用到 ApplicationThread.scheduleTransaction()
方法 。 ApplicationThread
是 ActivityThread
的內部類,但不管是 ApplicationThread 仍是 ActivityThread 其實都沒有 scheduleTransaction() 方法,因此調用的是其父類 ClientTransactionHandler
的方法。
> ClientTransactionHandler.java
public abstract class ClientTransactionHandler {
/** Prepare and schedule transaction for execution. */
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
// sendMessage() 方法在 ActivityThread類中實現
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
}
複製代碼
在回到 ActivityThread 類中看一下 sendMessage()
方法。
> ActivityThread.java private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
複製代碼
這裏向 mH
發送了 EXECUTE_TRANSACTION
消息,並攜帶了 transaction 。mH 是一個 叫作 H
的 Handler 類。它負責主線程消息處理,定義了大概五十多種事件。查找一下它是如何處理 EXECUTE_TRANSACTION 消息的。
> ActivityThread.java
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
// 執行 TransactionExecutor.execute()
mTransactionExecutor.execute(transaction);
if (isSystem()) {
transaction.recycle();
}
複製代碼
調用了 TransactionExecutor
的 execute()
方法。
> TransactionExecutor.java`
public void execute(ClientTransaction transaction) {
final IBinder token = transaction.getActivityToken();
log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
// 執行 callBack
executeCallbacks(transaction);
// 執行生命週期狀態
executeLifecycleState(transaction);
mPendingActions.clear();
log("End resolving transaction");
}
複製代碼
先來看看 executeCallbacks()
方法。
> TransactionExecutor.java
@VisibleForTesting
public void executeCallbacks(ClientTransaction transaction) {
......
final int size = callbacks.size();
for (int i = 0; i < size; ++i) {
final ClientTransactionItem item = callbacks.get(i);
......
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
....
}
複製代碼
核心代碼就這些。執行傳入的 callback 的 execute()
方法和 postExecute()
方法。還記得以前 realStartActivityLocked()
方法中調用 addCallback()
傳入的參數嗎?
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent), ......);
複製代碼
也就是說會執行 LaunchActivityItem
的 execute()
方法。
> LaunchActivityItem.java
@Override
public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client);
// 調用 ActivityThread.handleLaunchActivity()
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
複製代碼
兜兜轉轉,再次回到 ActivityThread ,執行其 handleLaunchActivity()
方法。
> ActivityThread.java
@Override
public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent) {
......
final Activity a = performLaunchActivity(r, customIntent);
......
return a;
}
複製代碼
> ActivityThread.java private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
// 獲取 ComponentName
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
// 獲取 Context
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// 反射建立 Activity
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
......
}
try {
// 獲取 Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
// 設置主題
activity.setTheme(theme);
}
activity.mCalled = false;
// 執行 onCreate()
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
}
r.setState(ON_CREATE);
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
......
}
return activity;
}
複製代碼
這裏又出現了 Instrumentation
的身影,分別調用了 newActivity()
方法和 callActivityOnCreate()
方法。
newActivity()
方法反射建立 Activity ,並調用其 attach()
方法。
> Instrumentation.java public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws InstantiationException, IllegalAccessException {
Activity activity = (Activity)clazz.newInstance();
ActivityThread aThread = null;
// Activity.attach expects a non-null Application Object.
if (application == null) {
application = new Application();
}
activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration(), null /* referrer */, null /* voiceInteractor */,
null /* window */, null /* activityConfigCallback */);
return activity;
}
複製代碼
callActivityOnCreate()
方法調用 Activity.performCreate()
方法,最終回調 onCreate()
方法。
> Instrumentation.java public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
複製代碼
> Activity.java final void performCreate(Bundle icicle) {
performCreate(icicle, null);
}
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
// 回調 onCreate()
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");
mActivityTransitionState.readState(icicle);
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
}
複製代碼
看到這裏,有一種如釋重負的感受,終於執行到 onCreate() 方法了。其實 Activity 的每一個生命週期回調都是相似的調用鏈。
還記得是從哪一個方法一路追蹤到 onCreate 的嗎?是 TransactionExecutor
的 execute()
方法。
> TransactionExecutor.java`
public void execute(ClientTransaction transaction) {
final IBinder token = transaction.getActivityToken();
log("Start resolving transaction for client: " + mTransactionHandler + ", token: " + token);
// 執行 callBack
executeCallbacks(transaction);
// 執行生命週期狀態
executeLifecycleState(transaction);
mPendingActions.clear();
log("End resolving transaction");
}
複製代碼
前面分析 executeCallBack()
一路追蹤到 onCreate() ,接下來就要分析 executeLifecycleState()
方法了。
> TransactionExecutor.java private void executeLifecycleState(ClientTransaction transaction) {
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
if (lifecycleItem == null) {
// No lifecycle request, return early.
return;
}
final IBinder token = transaction.getActivityToken();
final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
if (r == null) {
// Ignore requests for non-existent client records for now.
return;
}
// Cycle to the state right before the final requested state.
cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
// Execute the final transition with proper parameters.
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}
複製代碼
很熟悉,又看到了 lifecycleItem.execute()
。這裏的 lifecycleItem
仍是在 realStartActivityLocked()
方法中賦值的。
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
複製代碼
但在分析 ResumeActivityItem
以前,注意一下 execute()
方法以前的 cycleToPath()
方法。具體源碼就不去分析了,它的做用時根據上次最後執行到的生命週期狀態,和即將執行的生命週期狀態進行同步。說的不是那麼容易理解,舉個例子,上次已經回調了 onCreate() 方法,此次要執行的是 ResumeActivityItem
,中間還有一個 onStart()
狀態,那麼 cycleToPath()
方法就會去回調 onStart()
,也就是調用 ActivityThread.handleStartActivity()
。和 handleLaunchActivity()
差很少的調用鏈。
那麼,再回到 ResumeActivityItem.execute()
。
> ResumeActivityItem.java
@Override
public void execute(ClientTransactionHandler client, IBinder token, PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
"RESUME_ACTIVITY");
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
複製代碼
依舊是調用 ActivityThread.handleResumeActivity()
。不過這裏有一點比較特殊,仍是得拎出來講一下。
文章首發微信公衆號:
秉心說
, 專一 Java 、 Android 原創知識分享,LeetCode 題解。更多最新原創文章,掃碼關注我吧!
> ActivityThread.java public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
......
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
// 頁面可見
r.activity.makeVisible();
}
}
// 主線程空閒時會執行 Idler
Looper.myQueue().addIdleHandler(new Idler());
}
複製代碼
makeVisible()
方法讓 DecorView 可見。
> Activity.java void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
複製代碼
最後要注意的就是 Looper.myQueue().addIdleHandler(new Idler())
。因爲篇幅緣由,這裏先不介紹了,後面單獨寫 Activity 生命週期的時候再作分析。你們能夠先去源碼中找找答案。
一路分析過來,Activity終於展現給用戶了。
文章其實又臭又長,不少人可能會有疑問,看這些真的有用嗎?在我看來,一個程序員最重要的兩樣東西就是基本功和內功。良好的基本功可讓咱們輕鬆上手一門技術,而深厚的內功就可讓咱們面對難題迎刃而解。源碼能帶給你的,正是這些。
最近看了 Jetpack 中一些組件的源碼,下一篇文章應該就是Jetpack 相關了。敬請期待!
文章首發微信公衆號:
秉心說
, 專一 Java 、 Android 原創知識分享,LeetCode 題解。更多最新原創文章,掃碼關注我吧!