Android線程管理(二)——ActivityThread

上文分析了Handler、MessageQueue、Message及Looper四者的關(guān)系,本文將對(duì)Android中最重要的線程——ActivityThread進(jìn)行說(shuō)明。

二倦畅、ActivityThread的主要工作及實(shí)現(xiàn)機(jī)制

ActivityThread是Android應(yīng)用的主線程(UI線程),說(shuō)起ActivityThread绣的,不得不提到Activity的創(chuàng)建叠赐、啟動(dòng)過(guò)程以及ActivityManagerService欲账,但本文將僅從線程管理的角度來(lái)分析ActivityThread。ActivityManagerService芭概、ActivityStack赛不、ApplicationThread等會(huì)在后續(xù)文章中詳細(xì)分析,敬請(qǐng)期待喔~~不過(guò)為了說(shuō)清楚ActivityThread的由來(lái)罢洲,還是需要簡(jiǎn)單介紹下俄删。

以下引用自羅升陽(yáng)大師的博客:《Android應(yīng)用程序的Activity啟動(dòng)過(guò)程簡(jiǎn)要介紹和學(xué)習(xí)計(jì)劃

Step 1. 無(wú)論是通過(guò)Launcher來(lái)啟動(dòng)Activity,還是通過(guò)Activity內(nèi)部調(diào)用startActivity接口來(lái)啟動(dòng)新的Activity奏路,都通過(guò)Binder進(jìn)程間通信進(jìn)入到ActivityManagerService進(jìn)程中,并且調(diào)用ActivityManagerService.startActivity接口臊诊;
Step 2. ActivityManagerService調(diào)用ActivityStack.startActivityMayWait來(lái)做準(zhǔn)備要啟動(dòng)的Activity的相關(guān)信息鸽粉;
Step 3. ActivityStack通知ApplicationThread要進(jìn)行Activity啟動(dòng)調(diào)度了,這里的ApplicationThread代表的是調(diào)用ActivityManagerService.startActivity接口的進(jìn)程抓艳,對(duì)于通過(guò)點(diǎn)擊應(yīng)用程序圖標(biāo)的情景來(lái)說(shuō)触机,這個(gè)進(jìn)程就是Launcher了,而對(duì)于通過(guò)在Activity內(nèi)部調(diào)用startActivity的情景來(lái)說(shuō)玷或,這個(gè)進(jìn)程就是這個(gè)Activity所在的進(jìn)程了儡首;
Step 4. ApplicationThread不執(zhí)行真正的啟動(dòng)操作,它通過(guò)調(diào)用ActivityManagerService.activityPaused接口進(jìn)入到ActivityManagerService進(jìn)程中偏友,看看是否需要?jiǎng)?chuàng)建新的進(jìn)程來(lái)啟動(dòng)Activity蔬胯;
Step 5. 對(duì)于通過(guò)點(diǎn)擊應(yīng)用程序圖標(biāo)來(lái)啟動(dòng)Activity的情景來(lái)說(shuō),ActivityManagerService在這一步中位他,會(huì)調(diào)用startProcessLocked來(lái)創(chuàng)建一個(gè)新的進(jìn)程氛濒,而對(duì)于通過(guò)在Activity內(nèi)部調(diào)用startActivity來(lái)啟動(dòng)新的Activity來(lái)說(shuō),這一步是不需要執(zhí)行的鹅髓,因?yàn)樾碌腁ctivity就在原來(lái)的Activity所在的進(jìn)程中進(jìn)行啟動(dòng)舞竿;
Step 6. ActivityManagerServic調(diào)用ApplicationThread.scheduleLaunchActivity接口,通知相應(yīng)的進(jìn)程執(zhí)行啟動(dòng)Activity的操作窿冯;
Step 7. ApplicationThread把這個(gè)啟動(dòng)Activity的操作轉(zhuǎn)發(fā)給ActivityThread骗奖,ActivityThread通過(guò)ClassLoader導(dǎo)入相應(yīng)的Activity類,然后把它啟動(dòng)起來(lái)醒串。

大師的這段描述把ActivityManagerService执桌、ActivityStack、ApplicationThread及ActivityThread的調(diào)用關(guān)系講的很清楚厦凤,本文將從ActivityThread的main()方法開始分析其主要工作及實(shí)現(xiàn)機(jī)制鼻吮。(如有需要,可前往ActivityThread源碼

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    SamplingProfilerIntegration.start();

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    // Set the reporter for event logging in libcore
    EventLogger.setReporter(new EventLoggingReporter());

    AndroidKeyStoreProvider.install();

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

上述代碼中较鼓,從Looper.prepareMainLooper()Looper.loop()的代碼主要用于環(huán)境初始化椎木、AndroidKeyStoreProvider安裝等违柏,這里不做重點(diǎn)說(shuō)明。紅色部分的代碼主要分為兩個(gè)功能塊:1)綁定應(yīng)用進(jìn)程到ActivityManagerService香椎;2)主線程Handler消息處理漱竖。
關(guān)于線程通信機(jī)制,Handler畜伐、MessageQueue馍惹、Message及Looper四者的關(guān)系請(qǐng)參考上一篇文章《Android線程管理——線程通信》。

2.1 應(yīng)用進(jìn)程綁定

main()方法通過(guò)thread.attach(false)綁定應(yīng)用進(jìn)程玛界。ActivityManagerNative通過(guò)getDefault()方法返回ActivityManagerService實(shí)例万矾,ActivityManagerService通過(guò)attachApplication將ApplicationThread對(duì)象綁定到ActivityManagerService,而ApplicationThread作為Binder實(shí)現(xiàn)ActivityManagerService對(duì)應(yīng)用進(jìn)程的通信和控制慎框。

private void attach(boolean system) {
    sCurrentActivityThread = this;
    mSystemThread = system;
    if (!system) {
        ……            
        RuntimeInit.setApplicationObject(mAppThread.asBinder());
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            // Ignore
        }
        ……        
    } else {}
}

在ActivityManagerService內(nèi)部良狈,attachApplication實(shí)際是通過(guò)調(diào)用attachApplicationLocked實(shí)現(xiàn)的,這里采用了synchronized關(guān)鍵字保證同步笨枯。

@Override
public final void attachApplication(IApplicationThread thread) {
    synchronized (this) {
        int callingPid = Binder.getCallingPid();
        final long origId = Binder.clearCallingIdentity();
        attachApplicationLocked(thread, callingPid);
        Binder.restoreCallingIdentity(origId);
    }
}

attachApplicationLocked的實(shí)現(xiàn)較為復(fù)雜薪丁,其主要功能分為兩部分:

  • thread.bindApplication
  • mStackSupervisor.attachApplicationLocked(app)
private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid) {

    // Find the application record that is being attached...  either via
    // the pid if we are running in multiple processes, or just pull the
    // next app record if we are emulating process with anonymous threads.
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid);
        }
    } else {
        app = null;
    }
   // ……
    try {
       // ……
        thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.persistent,
                new Configuration(mConfiguration), app.compat,
                getCommonServicesLocked(app.isolated),
                mCoreSettingsObserver.getCoreSettingsLocked());
        updateLruProcessLocked(app, false, null);
        app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
    } catch (Exception e) {
        // todo: Yikes!  What should we do?  For now we will try to
        // start another process, but that could easily get us in
        // an infinite loop of restarting processes...
        Slog.wtf(TAG, "Exception thrown during bind of " + app, e);

        app.resetPackageList(mProcessStats);
        app.unlinkDeathRecipient();
        startProcessLocked(app, "bind fail", processName);
        return false;
    }

    // See if the top visible activity is waiting to run in this process...
    if (normalMode) {
        try {
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;
            }
        } catch (Exception e) {
            Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
            badApp = true;
        }
    }
// ……
}

thread對(duì)象其實(shí)是ActivityThread里ApplicationThread對(duì)象在ActivityManagerService的代理對(duì)象,故此執(zhí)行thread.bindApplication馅精,最終會(huì)調(diào)用ApplicationThread的bindApplication方法严嗜。該bindApplication方法的實(shí)質(zhì)是通過(guò)向ActivityThread的消息隊(duì)列發(fā)送BIND_APPLICATION消息,消息的處理調(diào)用handleBindApplication方法洲敢,handleBindApplication方法比較重要的是會(huì)調(diào)用如下方法:

mInstrumentation.callApplicationOnCreate(app);

callApplicationOnCreate即調(diào)用應(yīng)用程序Application的onCreate()方法漫玄,說(shuō)明Application的onCreate()方法會(huì)比所有activity的onCreate()方法先調(diào)用。

mStackSupervisor為ActivityManagerService的成員變量压彭,類型為ActivityStackSupervisor称近。

/** Run all ActivityStacks through this */
ActivityStackSupervisor mStackSupervisor;

從注釋可以看出,mStackSupervisor為Activity堆棧管理輔助類實(shí)例哮塞。ActivityStackSupervisor的attachApplicationLocked()方法的調(diào)用了realStartActivityLocked()方法刨秆,在realStartActivityLocked()方法中,會(huì)調(diào)用scheduleLaunchActivity()方法:

final boolean realStartActivityLocked(ActivityRecord r,
        ProcessRecord app, boolean andResume, boolean checkConfig)
        throws RemoteException {
 
    //...  
    try {
        //...
        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                System.identityHashCode(r), r.info,
                new Configuration(mService.mConfiguration),
                r.compat, r.icicle, results, newIntents, !andResume,
                mService.isNextTransitionForward(), profileFile, profileFd,
                profileAutoStop);
 
        //...
 
    } catch (RemoteException e) {
        //...
    }
    //...    
    return true;
}

app.thread也是ApplicationThread對(duì)象在ActivityManagerService的一個(gè)代理對(duì)象忆畅,最終會(huì)調(diào)用ApplicationThread的scheduleLaunchActivity方法衡未。

// we use token to identify this activity without having to send the
// activity itself back to the activity manager. (matters more with ipc)
@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();

        ……
        sendMessage(H.LAUNCH_ACTIVITY, r);
}

同bindApplication()方法,最終是通過(guò)向ActivityThread的消息隊(duì)列發(fā)送消息家凯,在ActivityThread完成實(shí)際的LAUNCH_ACTIVITY的操作缓醋。

public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        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);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
    ……
}

handleLaunchActivity()用于啟動(dòng)Activity。具體的啟動(dòng)流程不在這里詳述了绊诲,這里重點(diǎn)說(shuō)明ApplicationThread及ActivityThread的線程通信機(jī)制送粱。

2.2 主線程消息處理

在《Android線程管理——線程通信》中談到了普通線程中Handler、MessageQueue掂之、Message及Looper四者的關(guān)系抗俄,那么脆丁,ActivityThread中的線程通信又有什么不同呢?不同之處主要表現(xiàn)為兩點(diǎn):1)Looper的初始化方式动雹;2)Handler生成槽卫。

首先,ActivityThread通過(guò)Looper.prepareMainLooper()初始化Looper胰蝠,為了直觀比較ActivityThread與普通線程初始化Looper的區(qū)別歼培,把兩種初始化方法放在一起:

/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(true);
}

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

/**
 * Initialize the current thread as a looper, marking it as an
 * application's main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
  • 普通線程的prepare()方法默認(rèn)quitAllowed參數(shù)為true,表示允許退出茸塞,ActivityThread在prepareMainLooper()方法中調(diào)用prepare()方法躲庄,參數(shù)為false,表示主線程不允許退出钾虐。
  • 普通線程只調(diào)用prepare()方法读跷,ActivityThread在調(diào)用完prepare()方法之后,會(huì)通過(guò)myLooper()方法將本地線程<ThreadLocal>的Looper對(duì)象的引用交給sMainLooper禾唁。myLooper()其實(shí)就是調(diào)用sThreadLocal的get()方法實(shí)現(xiàn)的。
/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static Looper myLooper() {
    return sThreadLocal.get();
}
  • 之所以要通過(guò)sMainLooper指向ActivityThread的Looper對(duì)象无切,就是希望通過(guò)getMainLooper()方法將主線程的Looper對(duì)象開放給其他線程荡短。
/** Returns the application's main looper, which lives in the main thread of the application.*/
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

其次,ActivityThread與普通線程的Handler生成方式也不一樣哆键。普通線程生成一個(gè)與Looper綁定的Handler即可掘托,ActivityThread通過(guò)sMainThreadHandler指向getHandler()的返回值,而getHandler()方法返回的其實(shí)是一個(gè)繼承Handler的H對(duì)象籍嘹。

private class H extends Handler {
    ……
}

final H mH = new H();

final Handler getHandler() {
    return mH;
}

真正實(shí)現(xiàn)消息機(jī)制“通”信的其實(shí)是Looper的loop()方法闪盔,loop()方法的核心實(shí)現(xiàn)如下:

/**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop.
 */
public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycle();
    }
}

大致流程如下:

  • 首先通過(guò)上述myLooper()方法獲取Looper對(duì)象,取出Looper持有的MessageQueue辱士;
  • 然后從MessageQueue取出Message泪掀,如果Message為null,說(shuō)明線程正在退出颂碘;
  • Message不為空异赫,則調(diào)用Message的target handler對(duì)該Message進(jìn)行分發(fā),具體分發(fā)头岔、處理流程可參考《Android線程管理——線程通信》塔拳;
  • 消息處理完畢,調(diào)用recycle()方法進(jìn)行回收峡竣。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末靠抑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子适掰,更是在濱河造成了極大的恐慌颂碧,老刑警劉巖荠列,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異稚伍,居然都是意外死亡弯予,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門个曙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)锈嫩,“玉大人,你說(shuō)我怎么就攤上這事垦搬『舸纾” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵猴贰,是天一觀的道長(zhǎng)对雪。 經(jīng)常有香客問(wèn)我,道長(zhǎng)米绕,這世上最難降的妖魔是什么瑟捣? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮栅干,結(jié)果婚禮上迈套,老公的妹妹穿的比我還像新娘。我一直安慰自己碱鳞,他們只是感情好桑李,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窿给,像睡著了一般贵白。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上崩泡,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天禁荒,我揣著相機(jī)與錄音,去河邊找鬼角撞。 笑死圈浇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的靴寂。 我是一名探鬼主播磷蜀,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼百炬!你這毒婦竟也來(lái)了褐隆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤剖踊,失蹤者是張志新(化名)和其女友劉穎庶弃,沒(méi)想到半個(gè)月后衫贬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡歇攻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年固惯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缴守。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡葬毫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屡穗,到底是詐尸還是另有隱情贴捡,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布村砂,位于F島的核電站烂斋,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏础废。R本人自食惡果不足惜汛骂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望评腺。 院中可真熱鬧帘瞭,春花似錦、人聲如沸歇僧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)诈悍。三九已至,卻和暖如春兽埃,著一層夾襖步出監(jiān)牢的瞬間侥钳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工柄错, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留舷夺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓售貌,卻偏偏與公主長(zhǎng)得像给猾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子颂跨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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