Activity、View、Window之間的關係能夠用如下的簡要UML關係圖表示,在這裏貼出來,比較可以幫組後面流程分析部分的閱讀。java
1、Activity的啓動流程android
在startActivity()後,通過一些邏輯流程會通知到ActivityManagerService(後面以AMS簡稱),AMS接收到啓動acitivty的請求後,會經過跨進程通訊調用AcitivtyThread.handleLauncherActivity()方法,咱們從這裏開始分析,首先來看handleLauncherActivity()方法。
windows
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
...// Initialize before creating the activity WindowManagerGlobal.initialize(); Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;數組
//該方法會調用到Activity的onResume()方法
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);app
...異步
}ide
... }
這裏重點關注三個方法(加粗的地方),首先來看WindowManagerGlobal.initialize(),WindowManagerGlobal是單例模式的,一個進程內只有一個,這裏調用該類的初始化方法,後續咱們再對該類的做用和相關方法進行分析;第三個是在建立好Activity後調用Acitivty的onResume()方法。這裏咱們來看需關注的第二個方法performLaunchActivity(),代碼以下。佈局
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//經過反射方式建立Activity Activity activity = null; try { java.lang.ClassLoader cl = r.packageInfo.getClassLoader(); activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); StrictMode.incrementExpectedActivityCount(activity.getClass()); r.intent.setExtrasClassLoader(cl); r.intent.prepareToEnterProcess(); if (r.state != null) { r.state.setClassLoader(cl); } } catch (Exception e) { if (!mInstrumentation.onException(activity, e)) { throw new RuntimeException( "Unable to instantiate activity " + component + ": " + e.toString(), e); } } try { ...
if (activity != null) { Context appContext = createBaseContextForActivity(r, activity); CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager()); Configuration config = new Configuration(mCompatConfiguration); if (r.overrideConfig != null) { config.updateFrom(r.overrideConfig); } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity " + r.activityInfo.name + " with config " + config); Window window = null; if (r.mPendingRemoveWindow != null && r.mPreserveWindow) { window = r.mPendingRemoveWindow; r.mPendingRemoveWindow = null; r.mPendingRemoveWindowManager = null; } activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window);
...
//調用acitivity的onCreate()方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}post
...
}
return activity; }
這個方法主要是讀取Acitivity這裏利用反射建立出ActivityClientRecord所要求的Activity對象,而後調用了acitivity.attach()方法。注意attach()傳入的參數有不少,在performLacunchActivity()方法流程中,調用attach()方前,咱們省略掉的步驟基本都在爲這些參數作準備,attach()方法的做用其實就是將這些參數配置到新建立的Activity對象中;而在attach以後則會回調到acitivity的onCreate()方法。咱們進入Activity.java類詳細來看下attach方法。動畫
此外,在attach以前會初始化一個Window對象,Window.java是一個抽象類,表明了一個矩形不可見的容器,主要負責加載顯示界面,每一個Activity都會對應了一個Window對象。若是ActivityClientRecord.mPendingRevomeWindow變量中已經保存了一個Window對象,則會在後面的attach方法中被使用,具體使用的場景會在後面中介紹。
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window) { attachBaseContext(context); mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window);//(1) mWindow.setWindowControllerCallback(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { mWindow.setSoftInputMode(info.softInputMode); } if (info.uiOptions != 0) { mWindow.setUiOptions(info.uiOptions); }
... //初始化Acitity相關屬性 mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//(2) if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); mCurrentConfig = config; }
重點關注初始化window對象的操做,首先建立了PhoneWindow對象爲activity的mWindow變量,在建立時傳入了上一個activity對應的window對象,以後又將這個acitivity設置爲window對象的回調。Activity中不少操做view相關的方法,例如setContentView()、findViewById()、getLayoutInflater()等,實際上都是直接調用到PhoneWindow裏面的相關方法。建立完acitivty對應的PhoneWindow以後便會調用setWindowManager()方法。首先來看PhonewWindow構造方法。
public PhoneWindow(Context context, Window preservedWindow) { this(context); // Only main activity windows use decor context, all the other windows depend on whatever // context that was given to them. mUseDecorContext = true;if (preservedWindow != null) { //快速重啓activity機制 mDecor = (DecorView) preservedWindow.getDecorView(); mElevation = preservedWindow.getElevation(); mLoadElevation = false; mForceDecorInstall = true; // If we're preserving window, carry over the app token from the preserved // window, as we'll be skipping the addView in handleResumeActivity(), and // the token will not be updated as for a new window. getAttributes().token = preservedWindow.getAttributes().token; } // Even though the device doesn't support picture-in-picture mode, // an user can force using it through developer options. boolean forceResizable = Settings.Global.getInt(context.getContentResolver(), DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0; mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_PICTURE_IN_PICTURE); }
首先要關注的就是preserviedWindow參數,這個參數就是上一段中提到的mPendingRevomeWindow變量,這個參數在何時會不爲空呢?其實這裏的邏輯是用來快速重啓acitivity的,好比你的一個activity已經啓動了,可是主題換了或者configuration變了,這裏只須要從新加載資源和view,沒必從新再執行DecorView的建立工做。
另外一個要關注的就是mDecor變量,這個變量是DecorView類型的,若是這裏沒有初始化的話,則會在調用setContentView方法中new一個DecorView對象出來。DecorView對象繼承自FrameLayout,因此他本質上仍是一個view,只是對FrameLayout作了必定的包裝,例如添加了一些與window須要調用的方法setWindowBackground()、setWindowFrame()等。咱們知道,acitivty界面的view是呈樹狀結構的,而mDecor變量在這裏做爲activity的界面的根view存在。這三個點關係就好比,PhoneWindow是一塊手機電子屏,DecorView就是電子屏要顯示的內容,Activity就是手機電子屏安裝位置。
再來看建立PhonewWindow以後調用的setWindowManager()方法的邏輯,這段代碼是在PhonewWindow.java的父類Window.java中代碼以下。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { mAppToken = appToken; mAppName = appName; mHardwareAccelerated = hardwareAccelerated || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false); if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }
對於wWindowManager變量,實際上這裏是建立了一個WindowManagerImpl對象。首先是這種首先獲取系統服務的代理到wm上,而後強制轉換爲WindowManagerImpl調用createLocalWindowManager(),在createLocalWindowManager()實際是執行了一個new WindowManagerImpl()到方法來建立。關於這部分代碼看了好久很困惑的一個點,就是爲啥要弄個這麼複雜的邏輯,直接把上面加粗的代碼改成new WindowManagerImpl(...),這養會有什麼區別嗎?若是有大蝦看到這裏,但願能幫我解答。
在WindowManager中保存了對於單例對象WindowManagerGloble的引用,即mGlobal變量。此外,WindowManager.java實現了WindowManager又,而WindowManager繼承自ViewManager接口,ViewManager接口方法以下方代碼。
public interface ViewManager { /** * Assign the passed LayoutParams to the passed View and add the view to the window. * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. * <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link android.app.Presentation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }
在WindowManager對於addView()、updateViewLayout()和removeView()的實現,都是調用到mGlobal變量對應的addView()、updateViewLayout()和removeView()方法來實現的。這裏咱們
這樣咱們分析完activity以及對應的window對象的建立,回到performLauncerActivity()方法中Activity a = performLaunchActivity(r, customIntent)這一步驟,以後便回調activity方法的onCreate(),在onCreate()的setContentView方法會初始化DecorView,並根據傳入參數加載佈局,詳細步驟在下一節介紹。
再回到最初的handlerLaunchActivity()方法中,經過調用performLauncerActivity()建立出一個Acitivty對象後,若是建立成功則執行handleResumeActivity(),便執行到了Acitivity的onResume()方法,便是完成了acitivty的啓動。
2、setContentView()流程
首先,咱們通常在onCreate()裏調用setContentView()的方法。
@override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
這裏實際調用到到地方是Acitivity.java類中的setContentView()方法,以下。
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
這裏getWindow()返回的是Acitivity.java類中的mWindow變量,就是Activity建立時一塊兒建立的PhoneWindow對象,進入到
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews();//若是屢次調用setContentView則會執行removeAllView操做 } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { //過渡動畫機制相關 final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }
代碼裏涉及到FEATURE_CONTENT_TRANSITIONS的屬性,這裏是Android的過渡動畫相關機制,這裏咱們再也不展開詳述。通常的Acitivty啓動時,會進入mContentParent爲null的邏輯,首先調用的是installDecor()方法,完成DecorView的建立工做;以後調用mLayoutInflater.inflate()方法將咱們傳入的資源文件轉換爲view樹,裝載到mContentParent中。首先來看installDecor()代碼。
private void installDecor() { mForceDecorInstall = false; if (mDecor == null) { //建立DecorView mDecor = generateDecor(-1); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } else { mDecor.setWindow(this); } if (mContentParent == null) { mContentParent = generateLayout(mDecor); ... }
在這個方法又兩個主要步驟,首先是使用generateDecor()方法建立了DecorView對象,generateDecor()方法比較簡單,主要就是執行new DecorView(context, featureId, this, getAttributes())方法,這裏再也不貼出代碼;重點來看generateLayout()方法,這個方法生成的mContentParent是做爲來咱們後續加載加載的用戶的佈局的父佈局存在的。
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. //獲取當前主題的相關屬性 TypedArray a = getWindowStyle(); ... //一大段的根據獲取到到主題屬性,解析保存到PhonwWindow的相關參數的變量中 int layoutResource; int features = getLocalFeatures();
... //一大段根據PhoneWindow的設定好的屬性(features和mIsFloating)的判斷,爲layoutResource進行賦值,
//值能夠爲R.layout.screen_custom_title、R.layout.screen_action_bar等 mDecor.startChanging();
//將layoutRsourece值對應的佈局文件加載到DecorView中 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//在加載給DecorView的佈局文件中有一塊id爲ID_ANDROID_CONTENT的區域是用於用戶顯示本身佈局的,也是setContextView傳入的佈局顯示的地方
//這塊區域會以ViewGroup的形式賦值給mContentParent變量,這個ViewGroup便是用戶佈局的父佈局節點 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
... //繼續一大段的屬性配置 mDecor.finishChanging(); return contentParent; }
generateLayout方法實際就是解析出主題的相關屬性,根據不一樣的主題樣式的屬性值選擇不一樣的佈局文件設置到DecorView中(DecorView本事就是FrameLayout)。在view的樹狀結構下,DecorView便是整個Window顯示的視圖的根節點,在DecorView的子節點中又有一塊id爲ID_ANDROID_CONTENT的區域有一塊區域做爲mContentParent變量用於加載用戶的佈局,與mContentParent平級的視圖有ActionBar視圖和Title的視圖。總結來講,installDecor()方法實質就是產生mDecor和mContentParent對象。在installDecor以後,會執行到mLayoutInflater.inflate(layoutResID, mContentParent)方法將用戶傳入的佈局轉化爲view再加入到mContentParent上。這樣就完成了setContentView()流程。
3、handleResumeActivity()流程
在文章開頭貼出的第一段AcitityThread.handleLauncherActivity()方法的代碼中,執行完performLaunchAcitity()建立好Acitivity後,便會執行到handleResumeActivity()方法,該方法代碼以下。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ...// TODO Push resumeArgs into the activity for consideration // 該方法執行過程當中會調用到Acitity的onResume()方法,返回的ActivityClientRecord對象描述的便是建立好的Activity
r = performResumeActivity(token, clearHide, reason); if (r != null) { final Activity a = r.activity;//返回以前建立的Acitivty if (localLOGV) Slog.v( TAG, "Resume " + r + " started activity: " + a.mStartedActivity + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished); final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window.
// 判斷該Acitivity是否可見,mStartedAcitity記錄的是一個Activity是否還處於啓動狀態
// 若是還處於啓動狀態則mStartedAcitity爲true,表示該activity還未啓動好,則該Activity還不可見
boolean willBeVisible = !a.mStartedActivity;
// 若是啓動的組建不是全屏的,mStartedActivity也會是true,此時依然須要willBeVisible爲true如下的if邏輯就是針對這種狀況的校訂 if (!willBeVisible) { try { willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit;
//PreserverWindow,通常指主題換了或者configuration變了狀況下的Acitity快速重啓機制 if (r.mPreserveWindow) { a.mWindowAdded = true; r.mPreserveWindow = false; // Normally the ViewRoot sets up callbacks with the Activity // in addView->ViewRootImpl#setView. If we are instead reusing // the decor view we have to notify the view root that the // callbacks may have changed. ViewRootImpl impl = decor.getViewRootImpl(); if (impl != null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient && !a.mWindowAdded) { a.mWindowAdded = true;
//調用了WindowManagerImpl的addView方法 wm.addView(decor, l); } ... }
重點來看wm.addView()方法,該方法中的decor參數爲Acitity對應的Window中的視圖DecorView,wm爲在建立PhoneWindow是建立的WindowManagerImpl對象,該對象的addView方法實際調用到到是單例對象WindowManagerGlobal的addView方法(前文有提到)。在看addView代碼前,我先來看看WindowManagerGlobal對象成員變量。
private static WindowManagerGlobal sDefaultWindowManager; private static IWindowManager sWindowManagerService; private static IWindowSession sWindowSession; private final Object mLock = new Object(); private final ArrayList<View> mViews = new ArrayList<View>(); private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); private final ArraySet<View> mDyingViews = new ArraySet<View>();
三個成員變量mViews、mRoots和mParams分別是類型爲View、ViewRootImpl和WindowManager.LayoutParams的數組。這裏有這樣的邏輯關係,每一個View都對應着惟一的一個ViewRootImpl和WindowManager.LayoutRarams,便是1:1:1的關係。這三個數組長度始終保持一致,而且在同一個位置上存放的是互相關聯的View、ViewRootImpl和WindowManager.LayoutParams對象。此外還有一個成員變量mDyView,保存的則是已經不須要但還未被系統會收到View。
View與LayoutParams比較好理解,那ViewRootImpl對象的做用是什麼呢?首先WindowManagerImpl是做爲管理類,就像主管同樣,根據Acitity和Window的調用請求,找到合適的作事的人;DecorView自己是FrameworkLayout,本事是一個View,所表示的是一種靜態的結構;因此這裏就須要一個真正作事的人,那就是ViewRootImpl類的工做。總結來說ViewRootImpl的功能以下
1. 完成了繪製過程。在ViewRootImpl類中,實現了perfromMeasure()、performDraw()、performLayout()等繪製相關的方法。
2. 與系統服務進行交互,例如與AcitityManagerSerivice,DisplayService、AudioService等進行通訊,保證了Acitity相關功能等正常運轉。
3. 觸屏事件等分發邏輯的實現
接下來咱們進入WindowManagerGlobal.addView()方法的代碼。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... ViewRootImpl root; View panelParentView = null; synchronized (mLock) {
...
// If this is a panel window, then find the window it is being // attached to for future reference.
// 若是當前添加的是一個子視圖,則還須要找他他的父視圖
//這裏咱們分析的是添加DecorView的邏輯,沒有父視圖,故不會走到這裏,panelParentView爲null
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { final int count = mViews.size(); for (int i = 0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); } } }
root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams);
//保存互相對應的View、ViewRootImpl、WindowManager.LayoutParams到數組中 mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } }
關注代碼中加粗的兩個方法,首先會建立一個ViewRootImpl對象,而後調用ViewRootImpl.setView方法,其中panelParentView在addView參數爲DecorView是爲null。進入ViewRootImpl.setView()代碼。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) {
//初始化成員變量mView、mWindowAttraibutes
//mAttachInfo是View類的一個內部類AttachInfo類的對象
//該類的主要做用就是儲存一組當View attach給它的父Window的時候Activity各類屬性的信息
mView = view; mAttachInfo.mDisplayState = mDisplay.getState(); mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); mViewLayoutDirectionInitial = mView.getRawLayoutDirection(); mFallbackEventHandler.setView(view); mWindowAttributes.copyFrom(attrs); ... //繼續初始化一些變量,包含針對panelParentView不爲null時的父窗口的一些處理
mAdded = true; // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system.
// 這裏調用異步刷新請求,最終會調用performTraversals方法來完成View的繪製
requestLayout(); if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0; try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel); } catch (RemoteException e) { mAdded = false; mView = null; mAttachInfo.mRootView = null; mInputChannel = null; mFallbackEventHandler.setView(null); unscheduleTraversals(); setAccessibilityFocus(null, null); throw new RuntimeException("Adding window failed", e); } finally { if (restore) { attrs.restore(); } } ... } } }
相關變量初始化完成後,便會將mAdded設置爲true,表示ViewRootImpl與setView傳入的View參數已經作好了關聯。以後便會調用requestLayout()方法來請求一次異步刷新,該方法後來又會調用到performTraversals()方法來完成view到繪製工做。注意到這裏雖然完成了繪製的工做,可是咱們建立Activity的源頭是AMS中發起的,咱們從一開始建立Acitivity到相對應的Window、DecorView這一大套對象時,還並未與AMS進程進行反饋。因此以後便會調用mWindowSession.addToDisplay()方法會執行IPC的跨進程通訊,最終調用到AMS中的addWindow方法來在系統進程中執行相關加載Window的操做。