Android線程管理(二)——ActivityThread

来源:http://www.cnblogs.com/younghao/archive/2016/01/19/5126408.html
-Advertisement-
Play Games

線程通信、ActivityThread及Thread類是理解Android線程管理的關鍵。 線程,作為CPU調度資源的基本單位,在Android等針對嵌入式設備的操作系統中,有著非常重要和基礎的作用。本小節主要從以下三個方面進行分析: 《Android線程管理(一)——線程通信》 《Android線...


      線程通信、ActivityThread及Thread類是理解Android線程管理的關鍵。

      線程,作為CPU調度資源的基本單位,在Android等針對嵌入式設備的操作系統中,有著非常重要和基礎的作用。本小節主要從以下三個方面進行分析:

  1. 《Android線程管理(一)——線程通信》
  2. Android線程管理(二)——ActivityThread》 
  3. 《Android線程管理(三)——Thread類的內部原理、休眠及喚醒》


二、ActivityThread的主要工作及實現機制

      ActivityThread是Android應用的主線程(UI線程),說起ActivityThread,不得不提到Activity的創建、啟動過程以及ActivityManagerService,但本文將僅從線程管理的角度來分析ActivityThread。ActivityManagerService、ActivityStack、ApplicationThread等會在後續文章中詳細分析,敬請期待喔~~不過為了說清楚ActivityThread的由來,還是需要簡單介紹下。

      以下引用自羅升陽大師的博客:《Android應用程式的Activity啟動過程簡要介紹和學習計劃

Step 1. 無論是通過Launcher來啟動Activity,還是通過Activity內部調用startActivity介面來啟動新的Activity,都通過Binder進程間通信進入到ActivityManagerService進程中,並且調用ActivityManagerService.startActivity介面; 

Step 2. ActivityManagerService調用ActivityStack.startActivityMayWait來做準備要啟動的Activity的相關信息;

Step 3. ActivityStack通知ApplicationThread要進行Activity啟動調度了,這裡的ApplicationThread代表的是調用ActivityManagerService.startActivity介面的進程,對於通過點擊應用程式圖標的情景來說,這個進程就是Launcher了,而對於通過在Activity內部調用startActivity的情景來說,這個進程就是這個Activity所在的進程了;

Step 4. ApplicationThread不執行真正的啟動操作,它通過調用ActivityManagerService.activityPaused介面進入到ActivityManagerService進程中,看看是否需要創建新的進程來啟動Activity;

Step 5. 對於通過點擊應用程式圖標來啟動Activity的情景來說,ActivityManagerService在這一步中,會調用startProcessLocked來創建一個新的進程,而對於通過在Activity內部調用startActivity來啟動新的Activity來說,這一步是不需要執行的,因為新的Activity就在原來的Activity所在的進程中進行啟動;

Step 6. ActivityManagerServic調用ApplicationThread.scheduleLaunchActivity介面,通知相應的進程執行啟動Activity的操作;

Step 7. ApplicationThread把這個啟動Activity的操作轉發給ActivityThread,ActivityThread通過ClassLoader導入相應的Activity類,然後把它啟動起來。

      大師的這段描述把ActivityManagerService、ActivityStack、ApplicationThread及ActivityThread的調用關係講的很清楚,本文將從ActivityThread的main()方法開始分析其主要工作及實現機制。

      ActivityThread源碼來自:https://github.com/android/platform_frameworks_base/blob/master/core/java/android/app/ActivityThread.java

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

      上述代碼中,紅色部分之前的代碼主要用於環境初始化、AndroidKeyStoreProvider安裝等,這裡不做重點說明。紅色部分的代碼主要分為兩個功能塊:1)綁定應用進程到ActivityManagerService;2)主線程Handler消息處理。

      關於線程通信機制,Handler、MessageQueue、Message及Looper四者的關係請參考上一篇文章《Android線程管理——線程通信》。

2.1 應用進程綁定

      main()方法通過thread.attach(false)綁定應用進程。ActivityManagerNative通過getDefault()方法返回ActivityManagerService實例,ActivityManagerService通過attachApplication將ApplicationThread對象綁定到ActivityManagerService,而ApplicationThread作為Binder實現ActivityManagerService對應用進程的通信和控制。

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內部,attachApplication實際是通過調用attachApplicationLocked實現的,這裡採用了synchronized關鍵字保證同步。

@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的實現較為複雜,其主要功能分為兩部分:

  • 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對象其實是ActivityThread里ApplicationThread對象在ActivityManagerService的代理對象,故此執行thread.bindApplication,最終會調用ApplicationThread的bindApplication方法。該bindApplication方法的實質是通過向ActivityThread的消息隊列發送BIND_APPLICATION消息,消息的處理調用handleBindApplication方法,handleBindApplication方法比較重要的是會調用如下方法:

mInstrumentation.callApplicationOnCreate(app);

      callApplicationOnCreate即調用應用程式Application的onCreate()方法,說明Application的onCreate()方法會比所有activity的onCreate()方法先調用。

      mStackSupervisor為ActivityManagerService的成員變數,類型為ActivityStackSupervisor。

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

      從註釋可以看出,mStackSupervisor為Activity堆棧管理輔助類實例。ActivityStackSupervisor的attachApplicationLocked()方法的調用了realStartActivityLocked()方法,在realStartActivityLocked()方法中,會調用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對象在ActivityManagerService的一個代理對象,最終會調用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()方法,最終是通過向ActivityThread的消息隊列發送消息,在ActivityThread完成實際的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()用於啟動Activity。具體的啟動流程不在這裡詳述了,這裡重點說明ApplicationThread及ActivityThread的線程通信機制。

2.2 主線程消息處理

      在《Android線程管理——線程通信》中談到了普通線程中Handler、MessageQueue、Message及Looper四者的關係,那麼,ActivityThread中的線程通信又有什麼不同呢?不同之處主要表現為兩點:1)Looper的初始化方式;2)Handler生成。

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

/** 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()方法預設quitAllowed參數為true,表示允許退出,ActivityThread在prepareMainLooper()方法中調用prepare()方法,參數為false,表示主線程不允許退出。
  • 普通線程只調用prepare()方法,ActivityThread在調用完prepare()方法之後,會通過myLooper()方法將本地線程<ThreadLocal>的Looper對象的引用交給sMainLooper。myLooper()其實就是調用sThreadLocal的get()方法實現的。
/**
     * 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();
    }
  • 之所以要通過sMainLooper指向ActivityThread的Looper對象,就是希望通過getMainLooper()方法將主線程的Looper對象開放給其他線程。
/** 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生成方式也不一樣。普通線程生成一個與Looper綁定的Handler即可,ActivityThread通過sMainThreadHandler指向getHandler()的返回值,而getHandler()方法返回的其實是一個繼承Handler的H對象。。

private class H extends Handler {
    ……
}

final H mH = new H();

final Handler getHandler() {
    return mH;
}

      真正實現消息機制“通”信的其實是Looper的loop()方法,loop()方法的核心實現如下:

/**
     * 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();
        }
    }

      大致流程如下:

  • 首先通過上述myLooper()方法獲取Looper對象,取出Looper持有的MessageQueue;
  • 然後從MessageQueue取出Message,如果Message為null,說明線程正在退出;
  • Message不為空,則調用Message的target handler對該Message進行分發,具體分發、處理流程可參考《Android線程管理——線程通信》;
  • 消息處理完畢,調用recycle()方法進行回收。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 將搜索關鍵字設置為高亮顯示實例代碼:搜索關鍵詞以高亮狀態呈現是一種比較人性化的舉措,例如百度或者本站都有這樣的功能,可以極大的提高辨識度,下麵就通過代碼實例介紹一下如何實現此功能。代碼實例如下:螞蟻部落 螞蟻部落歡迎您,只有努力非都才會有美好的未來,螞蟻部落位於青島市南區 以上代碼實現了我們的...
  • 使用jquery獲取被選中checkbox覆選框的值:checkbox是重要的表單元素,在很多多項選擇中使用,下麵就通過代碼實例介紹一下如何獲取覆選框中所有被選中項的值,希望能夠給需要的朋友帶來一定的幫助。代碼如下:螞蟻部落 螞蟻部落一 螞蟻部落二 螞蟻部落三 以上代碼非常的簡單這裡就不多介...
  • javascript如何將字元串轉換成數組:數組和字元串的相互轉換是比較常見的操作,關於數組如何轉換成字元串可以參與js將數組轉換為字元串一章節,這裡就不介紹了,下麵將通過代碼實例介紹一下如何將字元串轉換為數組。代碼實例如下:var arr=[1,2,4,5,6,2,4];var str=arr.s...
  • css如何實現未知寬高div中圖片垂直水平居中效果:在有時候可能有這樣的情況,那就是想讓一個圖片在div中實現垂直水平居中效果,但是有時候div的尺寸是位置的,下麵通過代碼實例介紹一下在這種情況下如何實現圖片的垂直水平居中效果。代碼如下:實例一:螞蟻部落上面你的代碼可以讓圖片垂直水平居中,當然這裡,...
  • 點擊回車實現按鈕點擊功能:在實際應用中,可能有這樣的需求,點擊一個按鈕可以執行一個功能,當點擊回車的時候也可以實現此功能,也就是說點擊回車的時候也觸發的點擊事件,下麵就通過代碼實例介紹一下如何實現此功能。代碼如下:螞蟻部落 以上代碼實現了我們的要求,點擊按鈕或者點擊回車都可以將div的背景色設置為藍...
  • js將數組轉換為字元串:有時候將數組元素轉換成一個字元串更容易操作,下麵就通過代碼實例介紹一下如何實現此效果。代碼實例如下:var arr=[1,2,4,5,6,2,4];console.log(arr.join(""));使用Array自帶的join()函數即可實現此效果。關於join()函數可以...
  • 利用div和css製作三角形效果:本章節介紹一下如何利用div和css實現三角形效果,雖然看起來表神奇,但是原理是非常的簡單。代碼如下:螞蟻部落其實這個三角形是利用div的邊框"擠出"來的,邊框由於採用了不同的顏色,所以很好區分,如果只保留一個方位的三角形,可以自行設置邊框顏色或者刪除邊框就可以了。...
  • UITabBarController是開發中經常會用到的一個視圖控制器,但是預設的UITabBarController經常不能夠完全滿足我們的需求,所以我們經常需要自定義一個UITabBarController。 接下來,我們就來自定義一個UITabBarController。首先我們應該明白...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...