【Android源碼】Activity的啟動(dòng)流程

通常情況下女蜈,我們?cè)陲@式調(diào)用Activity的情況下涂屁,只需要通過如下代碼就能啟動(dòng)一個(gè)Activity:

Intent intent = new Intent(this, TestActivity.class);
startActivity(intent);

通過上面的代碼就能啟動(dòng)一個(gè)Activity牲览,之后新的Activity就能被系統(tǒng)展示給用戶寥枝。
那么系統(tǒng)是如何啟動(dòng)一個(gè)Activity盖奈?
新的Activity對(duì)象是在什么情況下被創(chuàng)建的混坞?
onCreate是在什么時(shí)機(jī)被系統(tǒng)回調(diào)的?

帶著這些問題钢坦,我們來一起分析下Activity的啟動(dòng)過程究孕。

我們通過startActivity開始分析:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
   if (mParent == null) {
       Instrumentation.ActivityResult ar =
           mInstrumentation.execStartActivity(
               this, mMainThread.getApplicationThread(), mToken, this,
               intent, requestCode, options);
       if (ar != null) {
           mMainThread.sendActivityResult(
               mToken, mEmbeddedID, requestCode, ar.getResultCode(),
               ar.getResultData());
       }
       if (requestCode >= 0) {
           // If this start is requesting a result, we can avoid making
           // the activity visible until the result is received.  Setting
           // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
           // activity hidden during this time, to avoid flickering.
           // This can only be done when a result is requested because
           // that guarantees we will get information back when the
           // activity is finished, no matter what happens to it.
           mStartedActivity = true;
       }

       cancelInputsAndStartExitTransition(options);
       // TODO Consider clearing/flushing other event sources and events for child windows.
   } else {
       if (options != null) {
           mParent.startActivityFromChild(this, intent, requestCode, options);
       } else {
           // Note we want to go through this method for compatibility with
           // existing applications that may have overridden it.
           mParent.startActivityFromChild(this, intent, requestCode);
       }
   }
}

通過上面的代碼可以發(fā)現(xiàn)調(diào)用了mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options)這個(gè)方法:

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
   IApplicationThread whoThread = (IApplicationThread) contextThread;
   Uri referrer = target != null ? target.onProvideReferrer() : null;
   if (referrer != null) {
       intent.putExtra(Intent.EXTRA_REFERRER, referrer);
   }
   if (mActivityMonitors != null) {
       synchronized (mSync) {
           final int N = mActivityMonitors.size();
           for (int i=0; i<N; i++) {
               final ActivityMonitor am = mActivityMonitors.get(i);
               if (am.match(who, null, intent)) {
                   am.mHits++;
                   if (am.isBlocking()) {
                       return requestCode >= 0 ? am.getResult() : null;
                   }
                   break;
               }
           }
       }
   }
   try {
       intent.migrateExtraStreamToClipData();
       intent.prepareToLeaveProcess(who);
       int result = ActivityManagerNative.getDefault()
           .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;
}

通過上面的代碼可以發(fā)現(xiàn),啟動(dòng)Activity的真正實(shí)現(xiàn)是ActivityManangerNative.getDefault()startActivity方法來完成的爹凹。

public abstract class ActivityManagerNative extends Binder implements IActivityManager{
}

通過觀察ActivityManangerNative可以發(fā)現(xiàn)繼承了Binder并實(shí)現(xiàn)了IActivityManager厨诸,所以ActivityManangerNative是一個(gè)Binder對(duì)象:

    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
   protected IActivityManager create() {
       IBinder b = ServiceManager.getService("activity");
       if (false) {
           Log.v("ActivityManager", "default service binder = " + b);
       }
       IActivityManager am = asInterface(b);
       if (false) {
           Log.v("ActivityManager", "default service = " + am);
       }
       return am;
   }
};

通過ActivityManangerNative.getDefault()方法可以看到這是一個(gè)單例的封裝:

IBinder b = ServiceManager.getService("activity");

通過ServiceManger來獲取到真正的Binder對(duì)象并返回,而這個(gè)對(duì)象就是ActivityManagerService禾酱,那么怎么確定就是它呢微酬?可以參考PackageManagerService的創(chuàng)建過程

到此為止颤陶,Activity的啟動(dòng)過程就轉(zhuǎn)到了ActivityManagerService中(不同版本的源碼略有區(qū)別):

   ActivityManagerService.startActivity
-> ActivityManagerService.startActivityAsUser
-> ActivityStarter.startActivityMayWait
-> ActivityStarter.startActivityLocked
-> ActivityStarter.startActivityUnchecked
-> ActivityStackSupervisor.resumeFocusedStackTopActivityLocked
-> ActivityStack.resumeTopActivityUncheckedLocked
-> ActivityStack.resumeTopActivityInnerLocked
-> ActivityStackSupervisor.startSpecificActivityLocked
-> ActivityStackSupervisor.realStartActivityLocked

通過不斷的跳轉(zhuǎn)最終跳轉(zhuǎn)到了ActivityStackSupervisor.realStartActivityLocked方法中:

app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);

app.threadIApplicationThread的類型:

public interface IApplicationThread extends IInterface {}

IApplicationThreadIInterface類型颗管,所以它也是一個(gè)Binder類型的接口,從IApplicationThread的聲明可以看出指郁,其中包含了大量的啟動(dòng)忙上、停止Activity的接口,還包含啟動(dòng)和停止Service的接口闲坎。通過聲明可以猜測這個(gè)Binder接口完成了大量的Activity和Service啟動(dòng)和停止相關(guān)的功能疫粥。

那么IApplicationThread的實(shí)現(xiàn)又是什么呢?

我們通過追溯thread可以知道是ApplicationThread

@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
      ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
      CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
      int procState, Bundle state, PersistableBundle persistentState,
      List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
      boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

  updateProcessState(procState, false);

  ActivityClientRecord r = new ActivityClientRecord();

  r.token = token;
  r.ident = ident;
  r.intent = intent;
  r.referrer = referrer;
  r.voiceInteractor = voiceInteractor;
  r.activityInfo = info;
  r.compatInfo = compatInfo;
  r.state = state;
  r.persistentState = persistentState;

  r.pendingResults = pendingResults;
  r.pendingIntents = pendingNewIntents;

  r.startsNotResumed = notResumed;
  r.isForward = isForward;

  r.profilerInfo = profilerInfo;

  r.overrideConfig = overrideConfig;
  updatePendingConfiguration(curConfig);

  sendMessage(H.LAUNCH_ACTIVITY, r);
}

這個(gè)方法很簡單腰懂,就是構(gòu)造了ActivityClientRecord并賦值梗逮,之后發(fā)送一個(gè)消息給Handler,這個(gè)Handler就是主線程的H:

case LAUNCH_ACTIVITY: {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
        
        r.packageInfo = getPackageInfoNoCheck(
                r.activityInfo.applicationInfo, r.compatInfo);
        handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    } break;
    
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    Activity a = performLaunchActivity(r, customIntent);
}

最終performLaunchActivity完成了Activity對(duì)象的創(chuàng)建和啟動(dòng)過程绣溜。

這個(gè)方法主要完成了這幾個(gè)操作:

  1. 通過ActivityClientRecord獲取到需要啟動(dòng)的Activity的組件信息

    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
      r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
              Context.CONTEXT_INCLUDE_CODE);
    }
    
    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);
    }
    
    
  2. 通過類加載器創(chuàng)建Activity

    Activity activity = null;
    try {
      java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
      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) {
      if (!mInstrumentation.onException(activity, e)) {
          throw new RuntimeException(
              "Unable to instantiate activity " + component
              + ": " + e.toString(), e);
      }
    }
    public Activity newActivity(ClassLoader cl, String className,
       Intent intent)
       throws InstantiationException, IllegalAccessException,
       ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }
    
  3. 通過makeApplication嘗試創(chuàng)建Application

    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    

    從makeApplication中也可以看到如果Application已經(jīng)存在了慷彤,那么就不會(huì)在創(chuàng)建Application,保證一個(gè)應(yīng)用只有一個(gè)Application對(duì)象。Application也是通過類加載器來創(chuàng)建的底哗。

    instrumentation.callApplicationOnCreate(app);
    

    當(dāng)Application第一次創(chuàng)建之后就會(huì)調(diào)用Application的onCreate方法岁诉,這就是為什么當(dāng)啟動(dòng)一個(gè)App的時(shí)候,Application會(huì)被首先調(diào)用的原因跋选。

  4. 創(chuàng)建ContextImpl對(duì)象并通過Activity的attach方法來完成一系列操作

    Context appContext = createBaseContextForActivity(r, activity);
     CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
     Configuration config = new Configuration(mCompatConfiguration);
     if (r.overrideConfig != null) {
         config.updateFrom(r.overrideConfig);
     }
     if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
             + r.activityInfo.name + " with config " + config);
     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);
    
    

    attach方法會(huì)關(guān)聯(lián)ContextImpl對(duì)象涕癣,還會(huì)關(guān)聯(lián)Window對(duì)象,并建立自己和Window對(duì)象的關(guān)聯(lián)前标。

  5. 調(diào)用Activity的onCreate方法

    mInstrumentation.callActivityOnCreate(activity, r.state);
    -> activity.performCreate(icicle);
    
    final void performCreate(Bundle icicle) {
       restoreHasCurrentPermissionRequest(icicle);
       onCreate(icicle);
       mActivityTransitionState.readState(icicle);
       performCreateCommon();
    }
    

    最終調(diào)用了我們熟悉的onCreate的生命周期中坠韩,此時(shí)Activity就完成了整個(gè)的啟動(dòng)過程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末炼列,一起剝皮案震驚了整個(gè)濱河市只搁,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌俭尖,老刑警劉巖氢惋,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異目溉,居然都是意外死亡明肮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門缭付,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人循未,你說我怎么就攤上這事陷猫。” “怎么了的妖?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵绣檬,是天一觀的道長。 經(jīng)常有香客問我嫂粟,道長娇未,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任星虹,我火速辦了婚禮零抬,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宽涌。我一直安慰自己平夜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布卸亮。 她就那樣靜靜地躺著忽妒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上段直,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天吃溅,我揣著相機(jī)與錄音,去河邊找鬼鸯檬。 笑死罕偎,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的京闰。 我是一名探鬼主播颜及,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蹂楣!你這毒婦竟也來了俏站?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤痊土,失蹤者是張志新(化名)和其女友劉穎肄扎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赁酝,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡犯祠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酌呆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衡载。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖隙袁,靈堂內(nèi)的尸體忽然破棺而出痰娱,到底是詐尸還是另有隱情,我是刑警寧澤菩收,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布梨睁,位于F島的核電站,受9級(jí)特大地震影響娜饵,放射性物質(zhì)發(fā)生泄漏坡贺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一箱舞、第九天 我趴在偏房一處隱蔽的房頂上張望遍坟。 院中可真熱鬧,春花似錦褐缠、人聲如沸政鼠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽公般。三九已至万搔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間官帘,已是汗流浹背瞬雹。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刽虹,地道東北人酗捌。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像涌哲,于是被迫代替她去往敵國和親胖缤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

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

  • Android Activity的啟動(dòng)過程過程分析 前言 在了解Activity的啟動(dòng)的過程的時(shí)候阀圾,我們需要先了解...
    真的有照片閱讀 1,280評(píng)論 0 6
  • 本文重點(diǎn)介紹應(yīng)用程序的啟動(dòng)過程哪廓,應(yīng)用程序的啟動(dòng)過程實(shí)際上就是應(yīng)用程序中的默認(rèn)Activity的啟動(dòng)過程,本文將詳細(xì)...
    天宇sonny閱讀 405評(píng)論 1 0
  • 從源碼的角度描述Activity的啟動(dòng)過程 Activity作為Android四大組件之一初烘,也是我們平時(shí)開發(fā)中使用...
    淡雅如蘭_往事隨風(fēng)閱讀 324評(píng)論 0 0
  • 誰啟動(dòng)了你的activity 涡真? 首先拋出一個(gè)問題,當(dāng)你點(diǎn)擊桌面應(yīng)用圖標(biāo)的時(shí)候肾筐,你的Launch-Activity...
    JeremyDai閱讀 1,451評(píng)論 2 4
  • 莫問君有幾多愁哆料, 人上有人樓外樓。 鹍鵬展翼九萬里吗铐, 敢于拼搏和奮斗东亦。 問君能有幾多愁, 一江春水向東流抓歼。 長江后...
    講誠信的人閱讀 1,229評(píng)論 5 29