Android Frameworks系列(二) 徹底弄懂startActivity

yangxi_001發表於2017-09-22

Android Frameworks系列(一) startService啟動 一文中我們介紹了startService啟動的過程,和startService一樣,startActivity也是以ActivityManagerService為核心工作的。原理也差不多,不過要比startService複雜,因為Activity的生命週期更多,並且還涉及UI方面的工作。為了分析包括zygote孵化目標程式,建立task等過程,就以Launcher啟動應用來說明,另外因為在startService中分析過其詳細的呼叫鏈,本篇不會再詳細的貼出其呼叫鏈的程式碼,只會分析核心方法的功能和程式執行的流程,具體程式碼大家可以參照流程圖具體去分析。 
  我們知道當使用者點選Launcher應用中對應的應用程式(TargetApp)圖示時,Launcher將啟動TargetApp。具體過程為Launcher通知ActivityManagerService啟動TargetApp的入口Activity,ActivityManagerService發現TargetApp程式還沒有建立,所以通知Zygote去fork一個程式,然後在該程式中經過一系列的操作執行ActivityThread的main方法。在TargetApp準備就緒後會通知ActivityManagerService應用已經啟動,ActivityManagerService會持有TargetApp的一個代理物件,通過這個代理物件ActivityManagerService可以通知TargetApp建立Activity例項,開始Activity的生命週期。 
   
  從上圖可以看出整個過程涉及4個程式間通訊。通訊方式其實在Android Frameworks系列(一) startService啟動 一文中已經分析過。用到了Binder和Socket兩種方式。具體可以看一下上文。 
  我們上邊說了,當使用者點選Launcher上的應用圖示,Launcher會通知ActivityManagerService啟動TargetApp,也就是說,當使用者點選圖示後,經常一系列的操作,會進入到ActivityManagerService的startActivity方法。我們就從startActivity開始分析吧! 
  在此之前,先認識一下下面幾個看起來非常相似的方法: 
  ActivityManagerService:startActivityAsUser() 
  ActivityStarter:startActivityMayWait() 
  ActivityStarter:startActivityLocked() 
  ActivityStarter:startActivityUnchecked() 
  這幾個方法會依次呼叫,並且都有明確的分工。接下來分析他們都做了什麼,和我們平時的開發有什麼關係。 
  

@Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

  ActivityManagerService的startActivityAsUser()方法只比startActivity多了最後一個UserId引數。這個UserId可以通過Binder機制的getCallingUid()方法獲取到。有了這個UserId,ActivityManagerService就可以檢查呼叫者的許可權檢查: 
  

@Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
        enforceNotIsolatedCaller("startActivity");
        userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                userId, false, ALLOW_FULL_ONLY, "startActivity", null);
        // TODO: Switch to user app stacks here.
        return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, bOptions, false, userId, null, null);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  enforceNotIsolatedCaller(“startActivity”)檢查呼叫者是否被隔離,如果被隔離會丟擲SecurityException。handleIncomingUser()檢查呼叫者是否有許可權執行call操作。 
   
         
         
  總得來說startActivityAsUser就是做一下呼叫者的基本許可權的檢查。通過之後,就交個ActivityStarter的startActivityMayWait()方法。這個方法很長,但是其功能還是非常清晰的: 
   
         
         
  相信我們開發Android應用都清楚,start一個Activity時,Intent可以是顯式的,也可以是隱式的。當Intent是顯式的時候,Intent已經有了目標Activity的資訊,但是如果是隱式的話,就需要通過ActivityStackSupervisor的resolveActivity來匹配了: 
  

ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
  • 1

  接著會判斷目標程式是不是重量級程式(heavy-weight),判斷是否已經存在不同於目標程式的重量級程式,如果存在則會重新賦值intent。之後會繼續執行startActivityLocked方法。最後會根據傳入的outResult是否為空來等待是否將執行的結果寫入到outResult中,在原始碼中是一個while迴圈來達到等待的目的,我們在startActivity時傳入的outResult為null,所在在這個過程中不會執行while循序。不過這或許是startActivityMayWait名字中有一個mayWait的原因。接著分析startActivityLocked()方法。 
  在分析startService一文說過方法名後帶有Locked的一般都是要求同步的,因為方法中會操作臨界資源。

         
   
  startActivityLocked()方法做的第一件事就是判斷caller程式是否還存在,因為caller程式在發出啟動請求後,被系統kill掉了,所以這裡還是要判斷一下,如果已經kill掉了,直接返回。如果還存活著,接著處理launchFlags,判斷是不是Intent.FLAG_ACTIVITY_FORWARD_RESULT,那麼這個標記是什麼意思呢?舉個開發中可能遇到的需求: 
  這裡寫圖片描述

  ActivityA啟動ActivityB,ActivityB再啟動ActivityC,當C處理完成後直接返回ActivityA而不是ActivityB,也就是跨越式傳遞。Intent.FLAG_ACTIVITY_FORWARD_RESULT就是達到這個目的而存在的。 
  當啟動ActivityC的launchFlags是Intent.FLAG_ACTIVITY_FORWARD_RESULT時,需要將caller由ActivityB修改為ActivityA,這樣才能達到ActivityC在處理完事件後setResult時返回到ActivityA。也正是由於這個原因,ActivityB不能以startActivityForResult啟動ActivityC,否則會報 
ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT的異常而退出。 
  接著會判斷是否匹配到Intent的目標,如果沒有匹配到,那麼將不會再處理該Intent,程式直接返回。如果匹配到,那麼接著校驗caller是否有許可權啟動目標Activity。許可權必須全部校驗通過,否則將報SecurityException。以上的判斷結果都將存放在ActivityRecord型別的變數r中,然後繼續執行startActivityUnchecked()方法,該方法涉及一系列的啟動模式,可以結合Android啟動模式之lunchMode 來加深印象。 
  在startActivityUnchecked方法中開始會執行以下兩個方法: 
  

setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);

        computeLaunchingTaskFlags();
  • 1
  • 2
  • 3
  • 4

  setInitialState()方法校驗intent的flag是否是特定的flag

         

  FLAG_ACTIVITY_NO_USER_ACTION這個flag表示不是使用者觸發的啟動Activity,而是由於像來電,鬧鈴等觸發的Activity啟動。 
  START_FLAG_ONLY_IF_NEEDED表示只有在需要的時候才啟動目標Activity。也就是說如果呼叫者和被啟動的是一個,那麼就沒有必要去進行重複的步驟了。 
  FLAG_ACTIVITY_NO_ANIMATION表示是否有Animation。 
   
  computeLaunchingTaskFlags()主要就是判斷是否需要啟動新的task。其核心程式碼如下: 
  

if (mSourceRecord == null) {
                // This activity is not being started from another...  in this
                // case we -always- start a new task.
                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                }
            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
                // The original activity who is starting us is running as a single
                // instance...  this new activity it is starting must go on its
                // own task.
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            } else if (mLaunchSingleInstance || mLaunchSingleTask) {
                // The activity being started is a single instance...  it always
                // gets launched into its own task.
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  如果mSourceRecord為null,表明我們應該新開一個task,也就是會設定mLaunchFlags 為FLAG_ACTIVITY_NEW_TASK。如果mSourceRecord的launchMode為LAUNCH_SINGLE_INSTANCE或者目標Activity的啟動模式為singleTask或者singleInstance都需要從新建立一個task。 
  回到startActivityUnchecked中會繼續處理flag,由於flag有很多,我將挑幾個我們平時可能遇到的講。 
  1.如果同時設定了FLAG_ACTIVITY_NEW_TASK 和FLAG_ACTIVITY_CLEAR_TASK 那麼將清理整個的task,然後將目標Activity放入task中,如圖ActivityB啟動ActivityC時設定這兩個標誌,那麼最後ActivityA和ActivityB都將被清理。 
  這裡寫圖片描述

  2.如果設定了FLAG_ACTIVITY_CLEAR_TOP或者launchMode為singleTask、singleInstance。那麼將會清理目標Activity之上的元素。否則如果top元素就是目標Activity那麼將整個task放到最前邊。並且會執行Activity的onNewIntent()方法,這一點相信大家都熟悉。和Android啟動模式之lunchMode 的內容是對應的。 
   
        

  處理這些之後,會執行ActivityStack的startActivityLocked方法。這個方法很關鍵,處理和WindowManagerService之間的互動,保證Activity對應的UI能在螢幕上顯示出來。 
  首先判斷目標Activity所在的task是不是新建的task,如果不是在新task,那麼還得查詢是哪一個task。接著將目標Activity放到整個stack的最頂層,這樣才能和使用者產生互動。之後還會根據之前是否有FLAG_ACTIVITY_NO_ANIMATION標記來決定是否需要執行切換動畫。我們知道一個Activity的UI能在螢幕上顯示出來,必須通過appToken在WindowManagerService中已經註冊。這個註冊的過程就是在startActivity流程中註冊的,原始碼很簡單:  

void addConfigOverride(ActivityRecord r, TaskRecord task) {
        final Rect bounds = task.updateOverrideConfigurationFromLaunchBounds();
        // TODO: VI deal with activity
        mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
                (r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
                task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
                task.mResizeMode, r.isAlwaysFocusable(), task.isHomeTask(),
                r.appInfo.targetSdkVersion);
        r.taskConfigOverride = task.mOverrideConfig;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  ActivityStack的startActivityLocked方法通過呼叫addConfigOverride方法完成這一操作。執行完這些操作之後,startActivity的一系列準備工作基本完成了,這時會回到ActivityStarter的startActivityUnchecked()方法中繼續進行啟動流程的處理。 
  ActivityManagerService在以上步驟準備就緒後就要開始resume目標Activity了,不過在這之前需要先pause當前執行的Activity,這裡其實也是涉及到Binder通訊的,不過原理和之前startService中ActivityManagerService同應用通訊的原理是一樣的,這裡不要贅述。在應用pause Activity成功之後,會通知ActivityManagerService,其接收到通知後,會有兩個情況(同startService原理其實類似,可以參考): 
  第一,如果目標Activity所在的程式已經存在,那麼通過一系列的呼叫,利用ClassLoader將目標Activity,載入到記憶體中來,然後在開始呼叫其onCreate(),onResume()等一系列生命週期方法。這樣Activity就真正的被啟動起來了。 
  第二,如果目標Activity所在的程式不存在,那麼ActivityManagerService還得通知Zygote程式fork一個程式,之後初始化新程式,之後在載入目標Activity到記憶體中來,最終呼叫到目標Activity的onCreate(),onResume()等一系列生命週期方法,這和第一種情況是一樣的。 
  總結:startActivity整個流程程式碼呼叫鏈很長,涉及好幾個跨程式通訊。但是其核心的流程還是很清晰的。 
  1.校驗caller程式是否存在,caller程式是否有許可權啟動目標 
  2.校驗Intent引數是否合法,匹配Intent所對應的目標程式。 
  3.根據各種flag和啟動模式,處理源task,目標Activity所在task。 
  4.向WindowManagerService新增AppToken,處理和WIndowManagerService的通訊 
  5.根據目標程式是否存在,決定是否通知Zygote孵化程式。之後將目標Activity載入到記憶體中,並執行其生命週期方法。

相關文章