深入理解Activity啟動流程和AMS框架(一)

最好不过如今發表於2024-07-29

連結 https://mp.weixin.qq.com/s?__biz=MzIwNjQ1NzQxNA==&mid=2247484149&idx=1&sn=fea623b475af3f05c657c1e55e3c478f&chksm=97201ddca05794cab15fa098ffb0ce4b5ca7791e1023e0e87969d89e0dbcfce28327ac9221e8&scene=21#wechat_redirect

一、前言

  • 一個App是怎麼啟動起來的?

  • App的程式入口到底是哪裡?

  • Activity生命週期到底是什麼時候呼叫的?被誰呼叫的?

  • 聽說還有個AMS的東西,它是做什麼的?它是怎樣管理和啟動Activity的?

  • ActivityThread、ApplicationThread、ActivityRecord、ActivityStack、TaskRecord都是些什麼鬼?它們之間又有什麼樣的聯絡?

  • 我們專案中遇到的關於Activity啟動流程的例子?

  • 等等...

你是不是還有很多類似的疑問一直沒有解決?沒關係,今天我們將結合原始碼和一些優秀文章,站在巨人的肩膀上,用更加通俗易懂的方式來試著解開謎底。畢竟程式碼繁多、經驗有限,如果有紕漏的地方希望大家指正,相互學習。

Android應用程式的載體是APK檔案,它是一個元件和資源的容器。APK檔案和我們常見可執行檔案的區別是:每個可執行檔案在一個單獨的程序中,但是APK檔案可能執行在一個單獨的程序也可能和其他APK檔案執行在同一個程序中。Android的設計理念是弱化程序的存在,取而代之以元件的概念。

本篇知識框架:

圖片

二、Activity的生命週期

Activity是最複雜的一種元件,它負責UI的顯示以及處理各種輸入事件。Activity通常表現為一個視覺化的使用者介面,包含了各種各樣的控制元件,讓使用者來完成某項工作,例如打電話、發簡訊、拍照等。

我們來看一下這一張經典的生命週期流程圖:

圖片

Android SDK中提供的Activity生命週期圖,隱含了Activity執行時的3種狀態:

  • 啟用態 :新啟動的Activity位於螢幕的最前端,接收使用者的輸入。>>onResume()

  • 暫停態 :當Activity被另一個透明或半透明的Activity覆蓋時所處的狀態,例如:Dialog。此時的Activity雖然已經不能接收使用者的輸入,但還是可見的。>>onPause()

  • 停止態 :當一個Activity完全被另外一個Activity覆蓋,不能接收使用者輸入也不可見。>>onStop()

Question:Activity生命週期到底是什麼時候呼叫的?被誰呼叫的?

三、應用程序的組成

Android建立在Linux系統之上,基礎的執行環境還是由程序組成。所有Android的應用程序都是由Zygote程序fork出來,因此,構成程序的底層基礎,包括虛擬機器、動態庫等都是相同的。除了從Zygote中繼承而來的基礎設施外,Android需要在應用的Java層建立一套框架來管理執行的元件。由於應用的配置各不相同,因此,不能在Zygote中完全建好後再繼承,只能在應用啟動時建立。而這套框架就構成了Android應用的基礎。

1、ActivityThread

Android應用程序的核心是ActivityThread 類,App的真正入口。每啟動一個App程序,都會建立ActivityThread與之對應的例項,會呼叫main()開始執行,開啟訊息迴圈佇列,這就是傳說中的UI執行緒或者叫主執行緒。這個類包含了應用框架中其他重要的類。

public final class ActivityThread {
...
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
final H mH = new H();
Application mInitialApplication;
Instrumentation mInstrumentation;
private final ResourcesManager mResourcesManager;
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
final ArrayMap<String, WeakReference<LoadedApk>> mPackages
        = new ArrayMap<String, WeakReference<LoadedApk>>();
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages
        = new ArrayMap<String, WeakReference<LoadedApk>>();
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
    = new ArrayMap<ProviderKey, ProviderClientRecord>();
...
public static void main(String[] args) {  
    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());  

    Process.setArgV0("<pre-initialized>");  // 設定程序名稱 

    Looper.prepareMainLooper();  

    ActivityThread thread = new ActivityThread();  // 建立ActivityThread例項
    thread.attach(false);                          // 呼叫attach

    if (sMainThreadHandler == null) {   // 儲存主執行緒的handler 
        sMainThreadHandler = thread.getHandler();  
    }  

    AsyncTask.init();  // 初始化AsynTask類

    Looper.loop();  // 進入訊息迴圈

    throw new RuntimeException("Main thread loop unexpectedly exited");  
}  
...
/**
* 呼叫mH傳送訊息排隊處理事件
*/
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
 }
...
}

我們梳理一下這個ActivityThread類:

圖片

  • mActivities、mServices和mProviderMap分別儲存了應用中所有的Activity物件、Service物件和ContentProvider物件。BroadcastReceiver物件的生命週期很短暫,屬於呼叫一次執行一次的型別,因此不需要儲存其物件。

  • mInitialApplication變數是一個唯一的Application物件,允許自定義。

  • mResourceManager管理應用的資源。

  • mPackages和mResourcesPackages儲存的是應用apk包的資訊。(比如,透過屬性process設定相同的應用名稱後,兩個有著相同ShareUserId和簽名的應用可以合併在同一個程序執行)

ActivityThread 管理應用程序的主執行緒的執行(相當於普通Java程式的main入口函式),並根據AMS的要求(透過IApplicationThread介面,AMS為Client、ActivityThread.ApplicationThread為Server)負責排程和執行四大元件activities、services、broadcasts、providers,以及其它操作。

2、main()方法

main()法的邏輯比較簡單,主要是初始化環境,然後讓執行緒進入訊息迴圈。在進行訊息迴圈前,main方法建立了ActivityThread物件,並使用引數false呼叫了他的attach()方法。

3、attach()方法

我們看看attach()方法中引數為false時的分支程式碼:

    private void attach(boolean system) {
            sCurrentActivityThread = this;
            mSystemThread = system;
            if (!system) {
                ......
                android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                        UserHandle.myUserId());
                RuntimeInit.setApplicationObject(mAppThread.asBinder());
                // 客戶端呼叫ActivityManagerNative.getDefault()返回的是ActivityManagerProxy
                // 也就是從ServiceManger獲取AMS的IBinder物件
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                try {
                    logAppLaunchTime(TAG, "attachApplication -> AMS"); /// M: It's for debugging App Launch time
                    mgr.attachApplication(mAppThread);  // 呼叫AMS的attachApplication方法
                } catch (RemoteException ex) {
                    // Ignore
                }
               ......
            } else {
            ......
            }
        }
    } 

attach()方法中主要做了兩件事情,一是呼叫setApplicationObject()方法把物件mAppThread放到了RuntimeInit類中的靜態變數mApplicationObject中。

public class RuntimeInit {
    ......
    public static final void setApplicationObject(Ibinder app) {
        mApplicationObject = app;
    }
    ......
}

第二件事情是呼叫ActivityManagerService的attachApplication方法,同時將mAppThread作為引數傳遞給了AMS中,這樣AMS就能透過它來呼叫應用的介面了(Binder通訊)。attachApplication方法最終實現是attachApplicationLocked。

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        ......
    private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
        ......
        try {
           ......
           // 呼叫應用的bindApplication介面
           thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            ......
        } catch (Exception e) {
            ......
            return false;
        }
        ......
        // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                // 呼叫mStackSupervisor.attachApplicationLocked介面
                // 如果程序沒有啟動,最後就是呼叫realStartActivityLocked()啟動程序
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                badApp = true;
            }
        }

        // Find any services that should be running in this process...
        if (!badApp) {
            try {
                didSomething |= mServices.attachApplicationLocked(app, processName);
            } catch (Exception e) {
                Slog.e(TAG, "Exception thrown starting services in " + app, e);
                badApp = true;
            }
        }

        // Check if a next-broadcast receiver is in this process...
        if (!badApp && isPendingBroadcastProcessLocked(pid)) {
            try {
                didSomething |= sendPendingBroadcastsLocked(app);
            } catch (Exception e) {
                // If the app died trying to launch the receiver we declare it 'bad'
                Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
                badApp = true;
            }
        }
        ......
    }
    ......
}

attachApplicationLocked()方法首先檢查呼叫的引數,然後又回撥ApplicationThread的介面bindApplication(),bindApplication()傳送完訊息BIND_APPLICATION然後就返回了,這樣執行過程就轉到應用的訊息處理程式碼中。除了開啟Activity能啟動程序外,啟動Service、傳送廣播也可能需要啟動程序,因此,這裡也呼叫了mServices.attachApplication()方法,以及sendPendingBroadcastsLocked來啟動程序。

下面再回頭看看ActivityThread是如何處理BIND_APPLICATION訊息的。

    private void handleBindApplication(AppBindData data) {
            mBoundApplication = data;
            // 建立系統配置物件
            mConfiguration = new Configuration(data.config);
            mCompatConfiguration = new Configuration(data.config);
            ......
            // 設定程序的名稱和DDMS中的程序名
            Process.setArgV0(data.processName);
            android.ddm.DdmHandleAppName.setAppName(data.processName,
                                                    UserHandle.myUserId());
            if (data.persistent) {
                // 帶有Persistent標記的程序在低記憶體的裝置上不能使用硬體加速
                if (!ActivityManager.isHighEndGfx()) {
                    /// M: ALPS01978329 for 512m project
                    final long LOW_MEMORY_SIZE = 256 * 1024 * 1024;
                    MemInfoReader minfo = new MemInfoReader();
                    minfo.readMemInfo();
                    if ((minfo.getTotalSize() <= LOW_MEMORY_SIZE) || (!"com.android.systemui".equals(data.processName))) {
                        HardwareRenderer.disable(false);
                        Slog.w(TAG, "Disable HWUI for Process(" + data.processName + "); Total Memory = " + minfo.getTotalSize());
                    }
                }
            }
            ......
            // 用系統的配置設定應用的時區
            TimeZone.setDefault(null);
            // 用系統的配置設定應用的地區
            Locale.setDefault(data.config.locale);

            // 生成資源管理物件mResourcesManager,並用系統配置初始化
            mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
            mCurDefaultDisplayDpi = data.config.densityDpi;
            applyCompatConfiguration(mCurDefaultDisplayDpi);
            ......

            // 生成ContentImpl物件
            final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
            if (!Process.isIsolated()) {
                // 設定cache目錄
                final File cacheDir = appContext.getCacheDir();

                if (cacheDir != null) {
                    // Provide a usable directory for temporary files
                    System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
                } else {
                    Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property due to missing cache directory");
                }

                // Use codeCacheDir to store generated/compiled graphics code
                final File codeCacheDir = appContext.getCodeCacheDir();
                if (codeCacheDir != null) {
                    setupGraphicsSupport(data.info, codeCacheDir);
                } else {
                    Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
                }
            }
            ......

            IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
            if (b != null) {
                // 設定網路代理
                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
                try {
                    final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
                    Proxy.setHttpProxySystemProperty(proxyInfo);
                } catch (RemoteException e) {}
            }

            if (data.instrumentationName != null) {
               ......
            } else { // 建立Instrumentation物件
               mInstrumentation = new Instrumentation();
            }

            if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
                // 如果應用指定使用big heap,則清除dalvik中的限制
                dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
            } else {
                // Small heap, clamp to the current growth limit and let the heap release
                // pages after the growth limit to the non growth limit capacity. b/18387825
                dalvik.system.VMRuntime.getRuntime().clampGrowthLimit();
            }

            final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
            try {
                // 最終,生成Application物件
                Application app = data.info.makeApplication(data.restrictedBackupMode, null);
                mInitialApplication = app;
                if (!data.restrictedBackupMode) {
                    List<ProviderInfo> providers = data.providers;
                    if (providers != null) {
                        // 初始化應用的ContentProvider
                        installContentProviders(app, providers);
                        // For process that contains content providers, we want to
                        // ensure that the JIT is enabled "at some point".
                        mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                    }
                }

                try { 
                    // 呼叫mInstrumentation物件的onCreate()方法
                    mInstrumentation.onCreate(data.instrumentationArgs);
                }
                catch (Exception e) {
                    throw new RuntimeException(
                        "Exception thrown in onCreate() of "
                        + data.instrumentationName + ": " + e.toString(), e);
                }

                try {
                    // 呼叫Application物件的onCreate()方法
                    mInstrumentation.callApplicationOnCreate(app);
                } catch (Exception e) {
                    if (!mInstrumentation.onException(app, e)) {
                        throw new RuntimeException(
                            "Unable to create application " + app.getClass().getName()
                            + ": " + e.toString(), e);
                    }
                }
            } finally {
                StrictMode.setThreadPolicy(savedPolicy);
            }
        }

handleBindApplication()方法的主要功能就是建立應用框架中的各種物件,這些物件大都已經介紹過了。最終呼叫到應用的Application.onCreate(),整個應用程序也就建立完畢。

4、ApplicationThread的作用

ApplicationThread是ActivityThead的一個內部類,是一個Binder服務類,ActivityManagerService操作應用就是透過ApplicationThread提供的介面完成的

final ApplicationThread mAppThread = new ApplicationThread();

private class ApplicationThread extends ApplicationThreadNative { 
    ...
    public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
                    configChanges);
        }

        public final void scheduleStopActivity(IBinder token, boolean showWindow,
                int configChanges) {
           sendMessage(
                showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
                token, 0, configChanges);
        }

        public final void scheduleSleeping(IBinder token, boolean sleeping) {
            sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
        }

        public final void scheduleResumeActivity(IBinder token, int processState,
                boolean isForward, Bundle resumeArgs) {
            updateProcessState(processState, false);
            sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
        }

        public final void scheduleSendResult(IBinder token, List<ResultInfo> results) {
            ResultData res = new ResultData();
            res.token = token;
            res.results = results;
            sendMessage(H.SEND_RESULT, res);
        }

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

ApplicationThread類從ApplicationThreadNative類派生,ApplicationThreadNative類中封裝了Binder的實現。ApplicationThread雖然定義了大量的介面,但是介面的實現模式都是把Binder呼叫轉換成**mH 的訊息來排隊處理 **。

5、H(Handler)訊息處理

final H mH = new H();

private class H extends Handler {  
    ...
    public void handleMessage(Message msg) {  
        switch (msg.what) {  
            case LAUNCH_ACTIVITY: {  
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");  
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;  

                r.packageInfo = getPackageInfoNoCheck(  
                        r.activityInfo.applicationInfo, r.compatInfo);  
                handleLaunchActivity(r, null);  
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
            } break; 
            case RELAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                handleRelaunchActivity(r);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            case PAUSE_ACTIVITY:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                        (msg.arg1&2) != 0);
                maybeSnapshot();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break; 
            ...  
        }  
    }  
    ...  
} 

訊息的處理最終會呼叫ActivityThread類的某個方法完成。 透過這種模式,從Binder來的呼叫就轉換成非同步的方式來執行了。理解了這個過程之後,再分析ApplicationThread類的介面是時,我們可以忽略中間的訊息傳遞過程,直接檢視ActivityThread中對應的方法。

相關文章