Android進(jìn)程啟動與Activity顯示

前言

這段時間腔呜,leader安排的任務(wù)進(jìn)行Android插件化核畴,熱修復(fù)相關(guān)的調(diào)研,對于插件化和熱修復(fù)涉及到的核心技術(shù)點跟束,在于對于類裝載冀宴,資源裝載的認(rèn)識還有對于啟動流程的熟悉温学,帶著該任務(wù)仗岖,于是有了接下來,一系列的文章揽祥,從進(jìn)程啟動拄丰,Activity顯示俐末,Dex裝載鹅搪,資源裝載,最后主流幾個插件化恢准,熱修復(fù)源碼實現(xiàn)的分析馁筐。本篇先從進(jìn)程的啟動敏沉,到一個Activity的顯示流程出發(fā)分析。

啟動一個進(jìn)程

在Anroid中秋泳,進(jìn)程是一個運行組件的容器迫皱,系統(tǒng)運行一個組件的時候辖众,啟動包含它的進(jìn)程凹炸,當(dāng)組件不再使用,進(jìn)程會被關(guān)閉奕筐。AMS負(fù)責(zé)對應(yīng)應(yīng)用進(jìn)程的啟動救欧。

開啟一個新的進(jìn)程锣光,在AMS中首先調(diào)用addAppLocked

final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
        String abiOverride) {
    ProcessRecord app;
    if (!isolated) {
        //從已經(jīng)開啟記錄下的進(jìn)程中查找進(jìn)程記錄
        app = getProcessRecordLocked(info.processName, info.uid, true);
    } else {
        app = null;
    }
    //app為空的時候誊爹,創(chuàng)建一個新的進(jìn)程频丘,同時更新內(nèi)部進(jìn)程管理結(jié)構(gòu)
    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);
      //調(diào)用進(jìn)程的啟動方法搂漠,啟動一個新的進(jìn)程
        startProcessLocked(app, "added application", app.processName, abiOverride,
                null /* entryPoint */, null /* entryPointArgs */);
    }

    return app;
}

首先會從已經(jīng)啟動的進(jìn)程中查找相應(yīng)的進(jìn)程信息桐汤,ProcessRecord靶壮,如果不存在則會創(chuàng)建一個出來腾降,然后調(diào)用startProcessLocked方法,來開啟一個新的進(jìn)程抗果。


private final void startProcessLocked(ProcessRecord app, String hostingType,
        String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
    //啟動應(yīng)用
    ....
    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);
      ....
  //發(fā)送定時消息窖张,如果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);
        }
    }
    ....

}

進(jìn)程的開啟調(diào)用的是Process的start方法睦霎。

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方法中副女,通過調(diào)用startViaZygote碑幅,通過zygote進(jìn)程來開啟一個新的進(jìn)程。

private static ProcessStartResult startViaZygote(final String processClass, ...){

    //配置通過Zygote啟動的參數(shù)恤批,最終通過socket寫入到Zygote進(jìn)程喜庞,來開啟一個新的進(jìn)程

}

方法的具體執(zhí)行是通過socket將進(jìn)程的啟動信息棋返,寫入到zygote中睛竣,然后通過其啟動一個新的進(jìn)程射沟,同時對于該進(jìn)程,也指定了一個類作為執(zhí)行的入口類幽污,這個類就是ActivityThread距误。

entryPoint = "android.app.ActivityThread";

start方法中的entryPoint准潭,這個就是進(jìn)程啟動后要執(zhí)行的Java類,進(jìn)程啟動后寺擂,所有操作就轉(zhuǎn)交到ActivityThread的執(zhí)行怔软,因此择镇,這個類也是整個應(yīng)用執(zhí)行的核心腻豌。這個類首先被執(zhí)行的是其main函數(shù)吝梅。

image.png
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 {
        //調(diào)用AMS的attachApplication方法兜叨,傳遞ApplicationThread進(jìn)去
        mgr.attachApplication(mAppThread);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
      .....
}

Ams的attachApplication方法會調(diào)用到Ams的attachApplicationLocked方法衩侥,

thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,...)

這個方法的主要功能是創(chuàng)建出應(yīng)用程序中的各種對象茫死,是比較核心的方法峦萎。

private void handleBindApplication(AppBindData data) {
    // 注冊當(dāng)前UI線程到Runtime作為一個敏感線程
    VMRuntime.registerSensitiveThread();

    // 設(shè)置進(jìn)程的啟動時間
    Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());

    // 創(chuàng)建進(jìn)程的配置對象
    mBoundApplication = data;
    mConfiguration = new Configuration(data.config);
    mCompatConfiguration = new Configuration(data.config);


    //當(dāng)版本低于Honeycomb MR1爱榔,將AsyncTask的實現(xiàn)通過使用線程池
    if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {
        AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    //設(shè)置應(yīng)用的時區(qū)和應(yīng)用的地區(qū)
    TimeZone.setDefault(null);
    LocaleList.setDefault(data.config.getLocales());

    synchronized (mResourcesManager) {
        mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
        mCurDefaultDisplayDpi = data.config.densityDpi;
        applyCompatConfiguration(mCurDefaultDisplayDpi);
    }


    // 創(chuàng)建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 {
            //調(diào)用Instrumentation的onCreate方法
            mInstrumentation.onCreate(data.instrumentationArgs);
        }
        catch (Exception e) {
            
        }

        try {
            //調(diào)用Application的onCreate方法
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
           
        }
    } finally {
        StrictMode.setThreadPolicy(savedPolicy);
    }
}

至此一個應(yīng)用進(jìn)程被打開柱搜,同時其Instrumentation和Application的onCreate方法也被調(diào)用了剥险,接下來就是Activity的執(zhí)行表制。

ActivityThread mian函數(shù)執(zhí)行

Activity啟動到顯示

從上面的進(jìn)程啟動可以得知每一個進(jìn)程對應(yīng)一個Application么介,對應(yīng)一個ActivityThread,也對應(yīng)這一個Instrumentation魔熏。對于Activity的啟動會調(diào)用到其handleLaunchActivity方法蒜绽。

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做了初始化操作躲雅,然后調(diào)用了performLaunchActivity方法相赁,返回一個Activity對象后慰于,返回對象偽非空婆赠,則調(diào)用handleResumeActivity。如果為空調(diào)用ActivityManager的finishActivity方法蛆挫。對于啟動悴侵,這里performLaunchActivityhandleResumeActivity兩個方法是核心可免。接下來將針對這兩個方法來進(jìn)行分析。

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);
   }
        ...
      //創(chuàng)建Activity實例
    java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
    activity = mInstrumentation.newActivity(    
    cl, component.getClassName(), r.intent);
    //獲取Application實例
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);

   //創(chuàng)建窗口實例呕缭,并調(diào)用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設(shè)置主題
    int theme = r.activityInfo.getThemeResource();
    if (theme != 0) {
         activity.setTheme(theme);
     }
     ....
   //調(diào)用該Activity的onCreate方法
    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    ....
    mActivities.put(r.token, r);
    ...
}

首先根據(jù)Activity的信息來獲取相關(guān)的包信息片仿,這里調(diào)用了getPackInfo來獲得相關(guān)的包信息尤辱。得到一個LoadedApk類型來表示當(dāng)前的Activity的包信息光督。

public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
            int flags) {
    return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
}

其中包含了Activity相關(guān)的信息Application信息结借,資源目錄等等。在獲得了LoadedApk實例之后咖熟,調(diào)用其makApplication方法馍管,我們會疑問咽斧,在啟動一個Activity的時候躬存,難道每次都要創(chuàng)建一個Application對象嗎岭洲?跟進(jìn)源碼盾剩。

public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
  if (mApplication != null) {
      return mApplication;
  }
    ....
 //創(chuàng)建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;
    //調(diào)用Application的onCreate方法
   instrumentation.callApplicationOnCreate(app);
}

通過makeApplication的方法實現(xiàn)告私,我們可以看到其首先判斷Application對象是否創(chuàng)建驻粟,如果沒有創(chuàng)建,則初始化類裝載器挤巡,然后創(chuàng)建Application對象矿卑,創(chuàng)建完成沃饶,則調(diào)用Application對象的onCreate方法糊肤。這里也就是我們所熟知的在我們自定義Application的時候重寫的onCreate方法將會被調(diào)用轩褐。
繼續(xù)回到上面代碼的分析,這里的Activity通過類裝載器被裝載出來勤讽,然后實例化出一個對象脚牍,然后調(diào)用了其attach方法诸狭,進(jìn)行了一系列信息的配置君纫。然后調(diào)用了mInstrumentation蓄髓,調(diào)用了callActivityOnCreate。同時也會將我們的Activity添加到mActivitys中陡叠,這里其定義如下枉阵。

final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();

通過這段代碼可以看到兴溜,調(diào)用了acticity的attach方法昵慌,跟進(jìn)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已經(jīng)被創(chuàng)建出來了侧蘸。Window實際類型為PhoneWindow讳癌。同時為該Window設(shè)置了WindowManager晌坤。至此旦袋,我們雖然不了解Window是個什么東西疤孕,但是至少祭阀,我們可以知道的一點就是每一個Activity的創(chuàng)建是會有持有一個Window對象的专控。然后Instrumentation的callActivityOnCreate方法被調(diào)用。

callActivityOnCreate

public void callActivityOnCreate(Activity activity, Bundle icicle) {
    prePerformCreate(activity);
    activity.performCreate(icicle);
    postPerformCreate(activity);
}

這里對于Activity的具體啟動細(xì)節(jié)却桶,我們不做關(guān)心颖系,具體細(xì)節(jié)嘁扼,接下來的源碼分析會做介紹趁啸,這里先看一下Activity的performCreate方法督惰。

final void performCreate(Bundle icicle) {
   restoreHasCurrentPermissionRequest(icicle);
   onCreate(icicle);
   mActivityTransitionState.readState(icicle);
   performCreateCommon();
}

這個時候調(diào)用了Activity的performCreate函數(shù)赏胚,調(diào)用了Activity的onCreate觉阅,我們一般會在onCreate中調(diào)用setContentView典勇,進(jìn)行我們布局文件的設(shè)置割笙。這也是比較奇怪的一點,為什么豪嚎,我們調(diào)用了該方法侈询,傳遞一個xml布局文件糯耍,我們的View就顯示出來了呢温技?這便是這次代碼分析的核心舵鳞,所有圍繞的相關(guān)知識點也會在此被引出。接下來博其,讓我們剝繭抽絲慕淡,逐層遞進(jìn)沸毁。

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徐紧,調(diào)用installDecor(),其中勒葱,我們可以看到有調(diào)用對于transitions這個feature的判斷浪汪,這個是在Android系統(tǒng)5.0之后添加的一個功能,可以用來實現(xiàn)Activity的過渡死遭,同時還是實現(xiàn)Activity之間的元素共享,使得Activity間切換更加的絲滑流暢凯旋。這里對于該場景不做分析呀潭,我們跳過看其具體的View裝載,然后調(diào)用了

mLayoutInflater.inflate(layoutResID, mContentParent);

private void installDecor() {
    mDecor = generateDecor(-1);
    ....
    mContentParent = generateLayout(mDecor);
    ...
     //過渡動畫至非,標(biāo)題钠署,logo,UI選項的顯示處理
}

在installDecor中荒椭,首先調(diào)用了generateDecor方法谐鼎,然后根據(jù)創(chuàng)建的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());
    }

根據(jù)是否使用DecorContext來創(chuàng)建相應(yīng)的context狸棍,然后利用該Context來創(chuàng)建DecorView。那么這個DecorView到底是個什么呢味悄?

public class DecorView extends FrameLayout

DecorView其實就是一個FrameLayout草戈。這個FrameLayout則是我們所看到的Activity試圖的根View,在創(chuàng)建了一個DecorView之后侍瑟,又根據(jù)這個DecorView實例來創(chuàng)建了ContentView唐片。

這里創(chuàng)建的DecorView其實是一個FrameLayout,

由上面函數(shù)可以看出,mContentParent是和mDecor有關(guān)的,下面來看一下ContentParent的創(chuàng)建過程费韭。

protected ViewGroup generateLayout(DecorView decor) {
... 
//根據(jù)樣式茧球,選擇相應(yīng)的資源文件,進(jìn)行相應(yīng)的資源裝載
    mDecor.startChanging();
    mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    ...
        return contentParent;
}

開始之前調(diào)用了DecorView的onResourcesLoaded揽思,然后通過findViewById的方式袜腥,返回一個VieGroup作為contentParent见擦。這里的findViewById的實現(xiàn)在基類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();
}

根據(jù)相應(yīng)的UI樣式配置鲤屡,選擇合適的布局資源文件损痰,然后通過inflate裝載相應(yīng)的資源文件,創(chuàng)建ContentView酒来,同時將其添加到DecorView中卢未。
在創(chuàng)建了DecorView和ContentParent之后,接下來堰汉,則利用了我們傳遞的xml布局文件id辽社。

mLayoutInflater.inflate(layoutResID, mContentParent);

LayoutInflater中inflate的實現(xiàn)如下

 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);
   }
   ...
}

根據(jù)我們的布局文件ID,創(chuàng)建出一個View翘鸭,然后將該View添加到我們的contentView之中滴铅。在setContentView中,當(dāng)我們結(jié)束了instalDecor方法之后就乓,會調(diào)用initWindowDecorActionBar來進(jìn)行ActionBar的初始化操作汉匙,創(chuàng)建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());
}

handleResumeActivity

通過performLaunchActivity生蚁,我們已經(jīng)裝載出了資源噩翠,同時創(chuàng)建了DecorView和contentParentView,同時也完成了Window的創(chuàng)建邦投,同時也將我們設(shè)置的資源文件伤锚,裝載出來成為了View。但是我們知道一個Activity可見時志衣,我們的onResume方法是被調(diào)用的了屯援,在performLaunchActivity被調(diào)用之后又調(diào)用了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);
      }
}

該方法首先調(diào)用了performResumeActivity函數(shù)蠢涝。performResumeActivity中進(jìn)行了大量的狀態(tài)相關(guān)的判斷玄呛,而對于首次啟動的分析,我們所關(guān)心的核心就是其調(diào)用了Activity的performResume和二,也就是Activity的onResume函數(shù)被調(diào)用了徘铝。

public final ActivityClientRecord performResumeActivity(IBinder token,boolean clearHide, String reason) {
    ...
    r.activity.performResume();
    ...
}

最開始調(diào)用了Activity的Resume 函數(shù),然后進(jìn)行了后續(xù)的調(diào)用。這里看到一個ViewRootImpl中惕它,通過decor調(diào)用getViewRootImpl()來獲得

public ViewRootImpl getViewRootImpl() {
   if (mAttachInfo != null)  {
      return mAttachInfo.mViewRootImpl;
   }
    return null;
}

該方法中核心代碼為

wm.addView(decor, l);

調(diào)用了WindowManager的addView方法怕午,來添加當(dāng)前的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調(diào)用了WindowManagerGlobal的addView方法淹魄,在最開始的時候郁惜,我們調(diào)用過

WindowManagerGlobal.initialize();

WindowManagerGlobal是個單例類,其保證了對于整個應(yīng)用層甲锡,只具備一個WindowManagerService兆蕉。同時其具備三個ArryList,分別保存一個應(yīng)用的根View缤沦,ViewRootImpl和LayoutParams虎韵,該方法,創(chuàng)建了一個ViewRootImpl實例缸废,然后將View包蓝,ViewRoot,LayoutParams添加到相應(yīng)的ArrayList中企量,最后調(diào)用ViewRoot的setView方法测萎。這里的setView方法會將其設(shè)置為自身的View,以后的繪制等事件都交給ViewRootImpl來實現(xiàn)届巩。繪制涉及到布局硅瞧,測量,繪制三個環(huán)節(jié)姆泻,具體的過程此處不再展開零酪,本篇主要目的是為了接下來的插件化和熱修復(fù)做一個基礎(chǔ)。

總結(jié)

View創(chuàng)建流程

至此拇勃,我們已經(jīng)知道了從一個Activity的啟動到我們的View逐步被創(chuàng)建的過程四苇,但是這里并沒有涉及到繪制相關(guān)的內(nèi)容,那么這個View最終如何繪制出來的呢方咆?接下來月腋,我們首先從ViewRootImpl來切入做分析,逐步理清楚接下來做的事情瓣赂。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末榆骚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子煌集,更是在濱河造成了極大的恐慌妓肢,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苫纤,死亡現(xiàn)場離奇詭異碉钠,居然都是意外死亡纲缓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門喊废,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祝高,“玉大人,你說我怎么就攤上這事污筷」す耄” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵瓣蛀,是天一觀的道長陆蟆。 經(jīng)常有香客問我,道長揪惦,這世上最難降的妖魔是什么遍搞? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任罗侯,我火速辦了婚禮器腋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钩杰。我一直安慰自己纫塌,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布讲弄。 她就那樣靜靜地躺著措左,像睡著了一般。 火紅的嫁衣襯著肌膚如雪避除。 梳的紋絲不亂的頭發(fā)上怎披,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音瓶摆,去河邊找鬼凉逛。 笑死,一個胖子當(dāng)著我的面吹牛群井,可吹牛的內(nèi)容都是我干的状飞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼书斜,長吁一口氣:“原來是場噩夢啊……” “哼诬辈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起荐吉,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤焙糟,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后样屠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體穿撮,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡搓劫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了混巧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枪向。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咧党,靈堂內(nèi)的尸體忽然破棺而出秘蛔,到底是詐尸還是另有隱情,我是刑警寧澤傍衡,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布深员,位于F島的核電站,受9級特大地震影響蛙埂,放射性物質(zhì)發(fā)生泄漏倦畅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一绣的、第九天 我趴在偏房一處隱蔽的房頂上張望叠赐。 院中可真熱鬧,春花似錦屡江、人聲如沸芭概。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罢洲。三九已至,卻和暖如春文黎,著一層夾襖步出監(jiān)牢的瞬間惹苗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工耸峭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留桩蓉,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓抓艳,卻偏偏與公主長得像触机,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子玷或,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容