瞭解這一章節,需要先瞭解LayoutInflater這個工具類,我以前分析過:http://www.cnblogs.com/kezhuang/p/6978783.html Window是Activity類中的一個全局變數,Window的作用是輔助Activity(也有可能是其他組件,本章拿Activ ...
瞭解這一章節,需要先瞭解LayoutInflater這個工具類,我以前分析過:http://www.cnblogs.com/kezhuang/p/6978783.html
Window是Activity類中的一個全局變數,Window的作用是輔助Activity(也有可能是其他組件,本章拿Activity為例)組裝界面,大體的流程是這樣
1.Activity告訴Window繪製界面的請求
2.Window會創建好View的結構樹,然後ActivityThread進行View重繪,來顯示在Activity上
我們通過Activity的setContentView來對Window展開分析
//調用Window下邊的setContentView方法來實現,我們來看getWindow()方法 public void setContentView(View view) { getWindow().setContentView(view); initWindowDecorActionBar(); }
Activity中的方法,調用getWindow的setContentView來實現
//就是一個簡單返回,那麼這個mWindow是在哪裡賦值的呢? public Window getWindow() { return mWindow; }
mWindow的實現類是PhoneWindow,只有這一個實現類,實在Activity的一個隱藏方法中創建的對象
//這個attach方法是在activity的內部定義,是在Activity創建的時候會調用,我們可以看到Window的實現類是PhoneWindow, //我們可以去PhoneWindow下查看一下setContentView的具體實現 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) { mWindow = new PhoneWindow(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); //... mWindow.setWindowManager( (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(), (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0); if (mParent != null) { mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); }
這個attach其實是在ActivityThread中被調用,具體我就不細講了。這是屬於Activity啟動流程中的,有興趣的可以自己去翻翻
從上邊可以看出來,mWindow就是PhoneWindow,那麼setContentView就是調用的PhoneWindow的方法了
其實現在這個類叫PhoneWindow已經不合適了。因為我們的安卓系統已經被植入到各種各樣的設備中去了。早已經脫離了手機,應該改名叫DeviceWindow,哈哈
我們接著往下看
//把resId載入成view樹,然後重繪顯示界面 @Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. // 載入的時候該變數是空的,需要調用installDecor來賦值 if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { //已mContentParent為父View,把Activity中傳遞過來的佈局解析到mContentParent中 mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); //最後調用一下回調方法,代表界面發生了改變 if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
這塊先判斷mContentParent是否為空,這個變數是一個ViewGroup類型,位於DecorView中,主要作用是存放用戶的layout(Activity中setContentView傳遞的resId)
首先會通過installDecor來創建一個DecorView出來。然後在創建mContentParent對象出來,最後把Activity傳遞過來的layoutResID載入到mContentParent容器中
到此,一個完整的DecorView樹就組裝完成了。就等待通知ViewRootImpl發起重繪,然後給WindowManagerService發送消息,顯示界面了
我們看下installDecor方法,這個方法比較長,我會省略一些判斷,只保留最核心的地方,我們先提前瞭解幾個一會兒會用到的函數
//第一個就是創建Decor對象的函數,很簡單,就是new一下 protected DecorView generateDecor() { return new DecorView(getContext(), -1); } //第二個就是創建mContentParent對象的函數,這個函數有點複雜,還要判斷系統版本來選擇不同的樣式和佈局 protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. //前邊省略了很多判斷,都是對界面樣式的判斷 int layoutResource; int features = getLocalFeatures(); // System.out.println("Features: 0x" + Integer.toHexString(features)); if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { layoutResource = R.layout.screen_swipe_dismiss; } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_title_icons; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); // System.out.println("Title Icons!"); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { // Special case for a window with only a progress bar (and title). // XXX Need to have a no-title version of embedded windows. layoutResource = R.layout.screen_progress; // System.out.println("Progress!"); } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { // Special case for a window with a custom title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = R.layout.screen_custom_title; } // XXX Remove this once action bar supports these features. removeFeature(FEATURE_ACTION_BAR); } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { // If no other features and not embedded, only need a title. // If the window is floating, we need a dialog layout if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar); } else { layoutResource = R.layout.screen_title; } // System.out.println("Title!"); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); } //上面會選出一個合適的layout來,由下麵這段代碼載入 mDecor.startChanging(); //載入成view tree後,把結果添加到decor中,然後把in賦值給mContentRoot變數存儲 View in = mLayoutInflater.inflate(layoutResource, null); decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); mContentRoot = (ViewGroup) in; //這個findView是從mDecor中查找,找到中間區域,就是用來存放Activity佈局的地方 //也就是我們的mContentParent變數 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); //這不系統會進行重繪 mDecor.finishChanging(); return contentParent; }
這兩個方法需要提前瞭解一下,一個就是創建DecorView的過程,一個就是創建mContentParent的過程,上邊註釋寫的很詳細,看看註釋就好了
//這個方法從方法名字上就可以知道。實在安裝一個叫Decor的東西 //mDecor是我們的整個應用界面,mContentParent就是Activity界面顯示的位置 private void installDecor() { //手機的最終顯示界面,包括狀態欄等 if (mDecor == null) { //new一個DecorView對象出來 mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } //此處這個mContentParent就是承載我們Activity佈局的一個容器 if (mContentParent == null) { //這裡會創建出承載Activity佈局的容器,具體解析看上邊的分析 mContentParent = generateLayout(mDecor); // Set up decor part of UI to ignore fitsSystemWindows if appropriate. mDecor.makeOptionalFitsSystemWindows(); final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById( R.id.decor_content_parent); if(decorContentParent!=null){ Log.i(TAG,"decorContentParent:"+decorContentParent.toString()); }else{ Log.i(TAG,"decorContentParent is null"); } if (decorContentParent != null) { mDecorContentParent = decorContentParent; mDecorContentParent.setWindowCallback(getCallback()); if (mDecorContentParent.getTitle() == null) { mDecorContentParent.setWindowTitle(mTitle); } final int localFeatures = getLocalFeatures(); for (int i = 0; i < FEATURE_MAX; i++) { if ((localFeatures & (1 << i)) != 0) { mDecorContentParent.initFeature(i); } } mDecorContentParent.setUiOptions(mUiOptions); if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) != 0 || (mIconRes != 0 && !mDecorContentParent.hasIcon())) { mDecorContentParent.setIcon(mIconRes); } else if ((mResourcesSetFlags & FLAG_RESOURCE_SET_ICON) == 0 && mIconRes == 0 && !mDecorContentParent.hasIcon()) { mDecorContentParent.setIcon( getContext().getPackageManager().getDefaultActivityIcon()); mResourcesSetFlags |= FLAG_RESOURCE_SET_ICON_FALLBACK; } if ((mResourcesSetFlags & FLAG_RESOURCE_SET_LOGO) != 0 || (mLogoRes != 0 && !mDecorContentParent.hasLogo())) { mDecorContentParent.setLogo(mLogoRes); } // Invalidate if the panel menu hasn't been created before this. // Panel menu invalidation is deferred avoiding application onCreateOptionsMenu // being called in the middle of onCreate or similar. // A pending invalidation will typically be resolved before the posted message // would run normally in order to satisfy instance state restoration. PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false); if (!isDestroyed() && (st == null || st.menu == null) && !mIsStartingWindow) { invalidatePanelMenu(FEATURE_ACTION_BAR); } } else { mTitleView = (TextView)findViewById(R.id.title); if (mTitleView != null) { mTitleView.setLayoutDirection(mDecor.getLayoutDirection()); if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) { View titleContainer = findViewById( R.id.title_container); if (titleContainer != null) { titleContainer.setVisibility(View.GONE); } else { mTitleView.setVisibility(View.GONE); } if (mContentParent instanceof FrameLayout) { ((FrameLayout)mContentParent).setForeground(null); } } else { mTitleView.setText(mTitle); } } } if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) { mDecor.setBackgroundFallback(mBackgroundFallbackResource); } // Only inflate or create a new TransitionManager if the caller hasn't // already set a custom one. if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) { if (mTransitionManager == null) { final int transitionRes = getWindowStyle().getResourceId( R.styleable.Window_windowContentTransitionManager, 0); if (transitionRes != 0) { final TransitionInflater inflater = TransitionInflater.from(getContext()); mTransitionManager = inflater.inflateTransitionManager(transitionRes, mContentParent); } else { mTransitionManager = new TransitionManager(); } } mEnterTransition = getTransition(mEnterTransition, null, R.styleable.Window_windowEnterTransition); mReturnTransition = getTransition(mReturnTransition, USE_DEFAULT_TRANSITION, R.styleable.Window_windowReturnTransition); mExitTransition = getTransition(mExitTransition, null, R.styleable.Window_windowExitTransition); mReenterTransition = getTransition(mReenterTransition, USE_DEFAULT_TRANSITION, R.styleable.Window_windowReenterTransition); mSharedElementEnterTransition = getTransition(mSharedElementEnterTransition, null, R.styleable.Window_windowSharedElementEnterTransition); mSharedElementReturnTransition = getTransition(mSharedElementReturnTransition, USE_DEFAULT_TRANSITION, R.styleable.Window_windowSharedElementReturnTransition); mSharedElementExitTransition = getTransition(mSharedElementExitTransition, null, R.styleable.Window_windowSharedElementExitTransition); mSharedElementReenterTransition = getTransition(mSharedElementReenterTransition, USE_DEFAULT_TRANSITION, R.styleable.Window_windowSharedElementReenterTransition); if (mAllowEnterTransitionOverlap == null) { mAllowEnterTransitionOverlap = getWindowStyle().getBoolean( R.styleable.Window_windowAllowEnterTransitionOverlap, true); } if (mAllowReturnTransitionOverlap == null) { mAllowReturnTransitionOverlap = getWindowStyle().getBoolean( R.styleable.Window_windowAllowReturnTransitionOverlap, true); } if (mBackgroundFadeDurationMillis < 0) { mBackgroundFadeDurationMillis = getWindowStyle().getInteger( R.styleable.Window_windowTransitionBackgroundFadeDuration, DEFAULT_BACKGROUND_FADE_DURATION_MS); } if (mSharedElementsUseOverlay == null) { mSharedElementsUseOverlay = getWindowStyle().getBoolean( R.styleable.Window_windowSharedElementsUseOverlay, true); } } } }
上邊就是對DecorView和mContentParent對象的創建過程
到此setContentView方法執行完畢,最終生產一個 DecorView 樹出來。描述當前界面的佈局層次關係。但是現在還不會重繪界面
這些都是在onCreate方法中完成,Activity的生命周期是到onResume才結束呢
我們Activity的onCreate方法是在ActivityThread中調用,所以執行完onCreate方法後,會回到ActivityThread的調用方法中,ActivityThread還會繼續調用Activity的onResume生命周期方法
但是在調用onResume方法之前呢。ActivityThread在中間做了一步操作,就是繪製onCreate方法中通過setContentView組裝出來的View數,就是Activity對象下的mDecor對象
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); mSomeActivitiesChanged = true; // TODO Push resumeArgs into the activity for consideration ActivityClientRecord r = performResumeActivity(token, clearHide); if (r != null) { final Activity a = r.activity; 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. boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) { try { willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { } } 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; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v( TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } // Get rid of anything left hanging around. cleanUpPendingRemoveWindows(r); // The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { if (r.newConfig != null) { r.tmpConfig.setTo(r.newConfig); if (r.overrideConfig != null) { r.tmpConfig.updateFrom(r.overrideConfig); } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity " + r.activityInfo.name + " with newConfig " + r.tmpConfig); performConfigurationChanged(r.activity, r.tmpConfig); freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig)); r.newConfig = null; } if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward); WindowManager.LayoutParams l = r.window.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != forwardBit) { l.softInputMode = (l.softInputMode & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) | forwardBit; if (r.activity.mVisibleFromClient) { ViewManager wm = a.getWindowManager(); View decor = r.window.getDecorView(); wm.updateViewLayout(decor, l); } } r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } } if (!r.onlyLocalRequest) { r.nextIdle = mNewActivities; mNewActivities = r; if (localLOGV) Slog.v( TAG, "Scheduling idle handler for " + r); Looper.myQueue().addIdleHandler(new Idler()); } r.onlyLocalRequest = false; // Tell the activity manager we have resumed. if (reallyResume) { try { ActivityManagerNative.getDefault().activityResumed(token); } catch (RemoteException ex) { } } } else { // If an exception was thrown when trying to resume, then // just end this activity. try { ActivityManagerNative.getDefault() .finishActivity(token, Activity.RESULT_CANCELED, null, false); } catch (RemoteException ex) { } } }
仔細看我標了顏色並加粗字體的那部分代碼,獲取Activity下的Window對象,在獲取剛剛組裝好的DecorView對象,然後在通過wm就是WindowManager對象的addView方法來創建一個ViewRootImpl對象出來
然後ViewRootImpl對象通過重繪並通知WindowManagerServices來顯示界面,到此Window類就講解完畢了
Window類的作用就是幫助ViewRootImpl組裝View模型。然後ViewRootImpl負責遍歷顯示
如果有錯誤或者疑問,請在評論區指出,我會即使更正,共同學習,共同進步,謝謝支持!