這段時間,leader安排的任務進行Android插件化,熱修復相關的調研,對於插件化和熱修復涉及到的核心技術點,在於對於類裝載,資源裝載的認識還有對於啓動流程的熟悉,帶着該任務,因而有了接下來,一系列的文章,從進程啓動,Activity顯示,Dex裝載,資源裝載,最後主流幾個插件化,熱修復源碼實現的分析。本篇先從進程的啓動,到一個Activity的顯示流程出發分析。java
在Anroid中,進程是一個運行組件的容器,系統運行一個組件的時候,啓動包含它的進程,當組件再也不使用,進程會被關閉。AMS負責對應應用進程的啓動。android
開啓一個新的進程,在AMS中首先調用addAppLocked
bash
final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
String abiOverride) {
ProcessRecord app;
if (!isolated) {
//從已經開啓記錄下的進程中查找進程記錄
app = getProcessRecordLocked(info.processName, info.uid, true);
} else {
app = null;
}
//app爲空的時候,建立一個新的進程,同時更新內部進程管理結構
if (app == null) {
app = newProcessRecordLocked(info, null, isolated, 0);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked();
}
.....
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
//調用進程的啓動方法,啓動一個新的進程
startProcessLocked(app, "added application", app.processName, abiOverride,
null /* entryPoint */, null /* entryPointArgs */);
}
return app;
}
複製代碼
首先會從已經啓動的進程中查找相應的進程信息,ProcessRecord,若是不存在則會建立一個出來,而後調用startProcessLocked
方法,來開啓一個新的進程。app
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
//啓動應用
....
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
....
//發送定時消息,若是App的啓動超時,則會ANR
synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(startResult.pid, app);
if (isActivityProcess) {
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, startResult.usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
}
....
}
複製代碼
進程的開啓調用的是Process的start方法。socket
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
}
}
複製代碼
start方法中,經過調用startViaZygote,經過zygote進程來開啓一個新的進程。ide
private static ProcessStartResult startViaZygote(final String processClass, ...){
//配置經過Zygote啓動的參數,最終經過socket寫入到Zygote進程,來開啓一個新的進程
}
複製代碼
方法的具體執行是經過socket將進程的啓動信息,寫入到zygote中,而後經過其啓動一個新的進程,同時對於該進程,也指定了一個類做爲執行的入口類,這個類就是ActivityThread。函數
entryPoint = "android.app.ActivityThread";
複製代碼
在start
方法中的entryPoint
,這個就是進程啓動後要執行的Java類,進程啓動後,全部操做就轉交到ActivityThread
的執行,所以,這個類也是整個應用執行的核心。這個類首先被執行的是其main函數。oop
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
Looper.loop();
....
}
複製代碼
ActivityThread的attach方法。源碼分析
private void attach(boolean system) {
......
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
//調用AMS的attachApplication方法,傳遞ApplicationThread進去
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
.....
}
複製代碼
Ams的attachApplication方法會調用到Ams的attachApplicationLocked方法,佈局
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,...)
複製代碼
這個方法的主要功能是建立出應用程序中的各類對象,是比較核心的方法。
private void handleBindApplication(AppBindData data) {
// 註冊當前UI線程到Runtime做爲一個敏感線程
VMRuntime.registerSensitiveThread();
// 設置進程的啓動時間
Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
// 建立進程的配置對象
mBoundApplication = data;
mConfiguration = new Configuration(data.config);
mCompatConfiguration = new Configuration(data.config);
//當版本低於Honeycomb MR1,將AsyncTask的實現經過使用線程池
if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {
AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
//設置應用的時區和應用的地區
TimeZone.setDefault(null);
LocaleList.setDefault(data.config.getLocales());
synchronized (mResourcesManager) {
mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
mCurDefaultDisplayDpi = data.config.densityDpi;
applyCompatConfiguration(mCurDefaultDisplayDpi);
}
// 建立Instrumentation
final InstrumentationInfo ii;
if (data.instrumentationName != null) {
try {
ii = new ApplicationPackageManager(null, getPackageManager())
.getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find instrumentation info for: " + data.instrumentationName);
}
mInstrumentationPackageName = ii.packageName;
mInstrumentationAppDir = ii.sourceDir;
mInstrumentationSplitAppDirs = ii.splitSourceDirs;
mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
mInstrumentedAppDir = data.info.getAppDir();
mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
mInstrumentedLibDir = data.info.getLibDir();
} else {
ii = null;
}
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
updateLocaleListFromAppContext(appContext,
mResourcesManager.getConfiguration().getLocales());
// Continue loading instrumentation.
if (ii != null) {
final ApplicationInfo instrApp = new ApplicationInfo();
ii.copyTo(instrApp);
instrApp.initForUser(UserHandle.myUserId());
final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
appContext.getClassLoader(), false, true, false);
final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
//
try {
final 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);
}
final ComponentName component = new ComponentName(ii.packageName, ii.name);
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
if (mProfiler.profileFile != null && !ii.handleProfiling
&& mProfiler.profileFd == null) {
mProfiler.handlingProfiling = true;
final File file = new File(mProfiler.profileFile);
file.getParentFile().mkdirs();
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
}
} else {
mInstrumentation = new Instrumentation();
}
//Application中指定了big heap,清除限制
if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
} else {
dalvik.system.VMRuntime.getRuntime().clampGrowthLimit();
}
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
try {
//生成Application對象
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
.....
try {
//調用Instrumentation的onCreate方法
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
}
try {
//調用Application的onCreate方法
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
}
複製代碼
至此一個應用進程被打開,同時其Instrumentation和Application的onCreate方法也被調用了,接下來就是Activity的執行。
從上面的進程啓動能夠得知每個進程對應一個Application,對應一個ActivityThread,也對應這一個Instrumentation。對於Activity的啓動會調用到其handleLaunchActivity
方法。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
....
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
...
} else {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
}
}
複製代碼
該方法首先對WindowManagerGlobal作了初始化操做,而後調用了performLaunchActivity
方法,返回一個Activity對象後,返回對象僞非空,則調用handleResumeActivity
。若是爲空調用ActivityManager
的finishActivity
方法。對於啓動,這裏performLaunchActivity
和handleResumeActivity
兩個方法是核心。接下來將針對這兩個方法來進行分析。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//獲取該Activity的包信息,這裏爲LoadedApk類型
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
...
//建立Activity實例
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
//獲取Application實例
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//建立窗口實例,並調用activity的attch方法,attach該窗口
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
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);
....
//爲Activity設置主題
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
....
//調用該Activity的onCreate方法
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
....
mActivities.put(r.token, r);
...
}
複製代碼
首先根據Activity的信息來獲取相關的包信息,這裏調用了getPackInfo來得到相關的包信息。獲得一個LoadedApk類型來表示當前的Activity的包信息。
public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
int flags) {
return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
}
複製代碼
其中包含了Activity相關的信息Application信息,資源目錄等等。在得到了LoadedApk實例以後,調用其makApplication方法,咱們會疑問,在啓動一個Activity的時候,難道每次都要建立一個Application對象嗎?跟進源碼。
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
....
//建立Application實例
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
mActivityThread.mAllApplications.add(app);
mApplication = app;
//調用Application的onCreate方法
instrumentation.callApplicationOnCreate(app);
}
複製代碼
經過makeApplication的方法實現,咱們能夠看到其首先判斷Application對象是否建立,若是沒有建立,則初始化類裝載器,而後建立Application對象,建立完成,則調用Application對象的onCreate方法。這裏也就是咱們所熟知的在咱們自定義Application的時候重寫的onCreate方法將會被調用。 繼續回到上面代碼的分析,這裏的Activity經過類裝載器被裝載出來,而後實例化出一個對象,而後調用了其attach方法,進行了一系列信息的配置。而後調用了mInstrumentation,調用了callActivityOnCreate
。同時也會將咱們的Activity添加到mActivitys中,這裏其定義以下。
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
複製代碼
經過這段代碼能夠看到,調用了acticity的attach方法,跟進attach方法。
final void attach(Context context, ....,Window window) {
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
.....
mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
.....);
.....
mWindowManager = mWindow.getWindowManager();
}
複製代碼
至此,咱們Activity中的Window已經被建立出來了。Window實際類型爲PhoneWindow。同時爲該Window設置了WindowManager。至此,咱們雖然不瞭解Window是個什麼東西,可是至少,咱們能夠知道的一點就是每個Activity的建立是會有持有一個Window對象的。而後Instrumentation的callActivityOnCreate
方法被調用。
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
複製代碼
這裏對於Activity的具體啓動細節,咱們不作關心,具體細節,接下來的源碼分析會作介紹,這裏先看一下Activity的performCreate
方法。
final void performCreate(Bundle icicle) {
restoreHasCurrentPermissionRequest(icicle);
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
複製代碼
這個時候調用了Activity的performCreate函數,調用了Activity的onCreate,咱們通常會在onCreate中調用setContentView,進行咱們佈局文件的設置。這也是比較奇怪的一點,爲何,咱們調用了該方法,傳遞一個xml佈局文件,咱們的View就顯示出來了呢?這即是此次代碼分析的核心,全部圍繞的相關知識點也會在此被引出。接下來,讓咱們剝繭抽絲,逐層遞進。
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製代碼
此處的Window即爲在attach中獲得的PhoneWindow的實例。
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}
複製代碼
首先判斷mContentParent是否爲空,mContentParent是用來放置contentView的,若是不爲空則要清理掉全部的view,若是爲null,調用installDecor()
,其中,咱們能夠看到有調用對於transitions這個feature的判斷,這個是在Android系統5.0以後添加的一個功能,能夠用來實現Activity的過渡,同時仍是實現Activity之間的元素共享,使得Activity間切換更加的絲滑流暢。這裏對於該場景不作分析,咱們跳過看其具體的View裝載,而後調用了mLayoutInflater.inflate(layoutResID, mContentParent);
private void installDecor() {
mDecor = generateDecor(-1);
....
mContentParent = generateLayout(mDecor);
...
//過渡動畫,標題,logo,UI選項的顯示處理
}
複製代碼
在installDecor中,首先調用了generateDecor
方法,而後根據建立的DecorView,來生成ContentView。
protected DecorView generateDecor(int featureId) {
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
複製代碼
根據是否使用DecorContext來建立相應的context,而後利用該Context來建立DecorView。那麼這個DecorView究竟是個什麼呢?
public class DecorView extends FrameLayout
複製代碼
DecorView其實就是一個FrameLayout。這個FrameLayout則是咱們所看到的Activity試圖的根View,在建立了一個DecorView以後,又根據這個DecorView實例來建立了ContentView。
這裏建立的DecorView實際上是一個FrameLayout,
由上面函數能夠看出,mContentParent是和mDecor有關的,下面來看一下ContentParent的建立過程。
protected ViewGroup generateLayout(DecorView decor) {
...
//根據樣式,選擇相應的資源文件,進行相應的資源裝載
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
...
return contentParent;
}
複製代碼
開始以前調用了DecorView的onResourcesLoaded,而後經過findViewById的方式,返回一個VieGroup做爲contentParent。這裏的findViewById的實如今基類Window中。
public View findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
複製代碼
onResourcesLoaded的方法以下。
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
....
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
複製代碼
根據相應的UI樣式配置,選擇合適的佈局資源文件,而後經過inflate裝載相應的資源文件,建立ContentView,同時將其添加到DecorView中。 在建立了DecorView和ContentParent以後,接下來,則利用了咱們傳遞的xml佈局文件id。
mLayoutInflater.inflate(layoutResID, mContentParent);
複製代碼
LayoutInflater中inflate的實現以下
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
....
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
....
if (root != null && attachToRoot) {
root.addView(temp, params);
}
...
}
複製代碼
根據咱們的佈局文件ID,建立出一個View,而後將該View添加到咱們的contentView之中。在setContentView中,當咱們結束了instalDecor
方法以後,會調用initWindowDecorActionBar
來進行ActionBar的初始化操做,建立ActionBar。
private void initWindowDecorActionBar() {
Window window = getWindow();
window.getDecorView();
if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) {
return;
}
mActionBar = new WindowDecorActionBar(this);
mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
mWindow.setDefaultIcon(mActivityInfo.getIconResource());
mWindow.setDefaultLogo(mActivityInfo.getLogoResource());
}
複製代碼
經過performLaunchActivity
,咱們已經裝載出了資源,同時建立了DecorView和contentParentView,同時也完成了Window的建立,同時也將咱們設置的資源文件,裝載出來成爲了View。可是咱們知道一個Activity可見時,咱們的onResume方法是被調用的了,在performLaunchActivity
被調用以後又調用了handleResumeActivity()
。
final void handleResumeActivity(IBinder token,
boolean clearHide, ....) {
ActivityClientRecord r = mActivities.get(token);
...
r = performResumeActivity(token, clearHide, reason);
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
}
複製代碼
該方法首先調用了performResumeActivity
函數。performResumeActivity中進行了大量的狀態相關的判斷,而對於首次啓動的分析,咱們所關心的核心就是其調用了Activity的performResume,也就是Activity的onResume函數被調用了。
public final ActivityClientRecord performResumeActivity(IBinder token,boolean clearHide, String reason) {
...
r.activity.performResume();
...
}
複製代碼
最開始調用了Activity的Resume 函數,而後進行了後續的調用。這裏看到一個ViewRootImpl中,經過decor調用getViewRootImpl()
來得到
public ViewRootImpl getViewRootImpl() {
if (mAttachInfo != null) {
return mAttachInfo.mViewRootImpl;
}
return null;
}
複製代碼
該方法中核心代碼爲
wm.addView(decor, l);
複製代碼
調用了WindowManager的addView方法,來添加當前的DecorView。
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
....
ViewRootImpl root;
View panelParentView = null;
....
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
....
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
....
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
}
複製代碼
在WindowManager中的addView調用了WindowManagerGlobal的addView方法,在最開始的時候,咱們調用過WindowManagerGlobal.initialize();
WindowManagerGlobal是個單例類,其保證了對於整個應用層,只具有一個WindowManagerService。同時其具有三個ArryList,分別保存一個應用的根View,ViewRootImpl和LayoutParams,該方法,建立了一個ViewRootImpl實例,而後將View,ViewRoot,LayoutParams添加到相應的ArrayList中,最後調用ViewRoot的setView方法。這裏的setView方法會將其設置爲自身的View,之後的繪製等事件都交給ViewRootImpl來實現。繪製涉及到佈局,測量,繪製三個環節,具體的過程此處再也不展開,本篇主要目的是爲了接下來的插件化和熱修復作一個基礎。
至此,咱們已經知道了從一個Activity的啓動到咱們的View逐步被建立的過程,可是這裏並無涉及到繪製相關的內容,那麼這個View最終如何繪製出來的呢?接下來,咱們首先從ViewRootImpl來切入作分析,逐步理清楚接下來作的事情。