天天看點

深入了解Activity啟動流程和AMS架構(一)二、Activity的生命周期三、應用程序的組成

一、前言

  • 一個App是怎麼啟動起來的?
  • App的程式入口到底是哪裡?
  • Activity生命周期到底是什麼時候調用的?被誰調用的?
  • 聽說還有個AMS的東西,它是做什麼的?它是怎樣管理和啟動Activity的?
  • ActivityThread、ApplicationThread、ActivityRecord、ActivityStack、TaskRecord都是些什麼鬼?它們之間又有什麼樣的聯系?
  • 我們項目中遇到的關于Activity啟動流程的例子?
  • 等等...
你是不是還有很多類似的疑問一直沒有解決?沒關系,今天我們将結合源碼和一些優秀文章,站在巨人的肩膀上,用更加通俗易懂的方式來試着解開謎底。畢竟代碼繁多、經驗有限,如果有纰漏的地方希望大家指正,互相學習。

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

本篇知識架構:

深入了解Activity啟動流程和AMS架構(一)二、Activity的生命周期三、應用程式的組成

二、Activity的生命周期

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

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

深入了解Activity啟動流程和AMS架構(一)二、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類:

深入了解Activity啟動流程和AMS架構(一)二、Activity的生命周期三、應用程式的組成
  • 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中對應的方法。

未完待續 >>>

本文由原作者彭小川獨家授權Open軟體開發小組釋出,著作權歸原作者所有。如需轉載請申請授權。