標籤:源碼 hierarchy 控制項 介面 framework
問題
在慢慢熟悉android 的過程中,發現一個view 或者layout的初始化,或者構造的流程還是比較清楚的,也就是加到父控制項中,然後就開始了對應的生命週期。但是整個介面的父控制項,或者說系統的第一個view, 是怎麼來的,如何初始化和繪製的呢?
概述
概述:帶著困擾我的問題,在前文的基礎上,繼續分析應用介面和framework的關係,通過分析viewrootimpl 的來源,並結合dialog, popupwindow, 和activity 的 根view的建立流程,回答了問題介面的根view 或者第一個view 是如何初始化,並加入到framework 中的。
分析viewrootimpl 的來源
本文分析是接上篇《源碼分析:LayoutParams的wrap_content, match_parent, 和具體值》,在上文中簡要分析了windowmanager中對介面的處理。
使用各種搜尋方法,可以看到,全部android代碼中只有一處引用ViewRootImpl 這個類,那就是
android.view.WindowManagerImpl.addView(View, LayoutParams, CompatibilityInfoHolder, boolean)
通過分析代碼可以看到,windowmanager在addview() 的過程中,為了管理添加進來的view,使用了三個數組
private View[] mViews; private ViewRootImpl[] mRoots; private WindowManager.LayoutParams[] mParams;
在代碼處理中,通過view 的context 來構造一個對應的ViewRootImpl ,然後把view, rootViewImpl, 和layoutParams 三個變數,存到數組中。並在最後,setView。
root = new ViewRootImpl(view.getContext()); root.mAddNesting = 1; if (cih == null) { root.mCompatibilityInfo = new CompatibilityInfoHolder(); } else { root.mCompatibilityInfo = cih; } view.setLayoutParams(wparams); if (mViews == null) { index = 1; mViews = new View[1]; mRoots = new ViewRootImpl[1]; mParams = new WindowManager.LayoutParams[1]; } else { index = mViews.length + 1; Object[] old = mViews; mViews = new View[index]; System.arraycopy(old, 0, mViews, 0, index-1); old = mRoots; mRoots = new ViewRootImpl[index]; System.arraycopy(old, 0, mRoots, 0, index-1); old = mParams; mParams = new WindowManager.LayoutParams[index]; System.arraycopy(old, 0, mParams, 0, index-1); } index--; mViews[index] = view; mRoots[index] = root; mParams[index] = wparams;
// do this last because it fires off messages to start doing things root.setView(view, wparams, panelParentView);
在setview中,就是前文measure 過程中提出的問題, view是哪裡來的-參見-《盡量理解xml 中LayoutParams的wrap_content, match_parent, 和具體值》
好,現在知道了,rootview ,最終處理的view就是就是從windowmanager 中add進來的。那麼順藤摸瓜,看看到底哪裡調用了WindowManagerImpl.addView()
ViewRootImpl(Context) - android.view.ViewRootImpladdView(View, LayoutParams, CompatibilityInfoHolder, boolean) : void - android.view.WindowManagerImpladdView(View, LayoutParams, CompatibilityInfoHolder) : void - android.view.WindowManagerImpladdView(View, LayoutParams) : void - android.view.WindowManagerImpladdIntruderView() : void - com.android.systemui.statusbar.phone.PhoneStatusBaraddNavigationBar() : void - com.android.systemui.statusbar.phone.PhoneStatusBaraddPanelWindows() : void - com.android.systemui.statusbar.tablet.TabletStatusBar (5 matches)addStartingWindow(IBinder, String, int, CompatibilityInfo, CharSequence, int, int, int) : View - com.android.internal.policy.impl.PhoneWindowManageraddView(View) : void - android.view.WindowManagerImpladvance() : void - com.android.systemui.statusbar.tablet.TabletTickerhandleResumeActivity(IBinder, boolean, boolean) : void - android.app.ActivityThreadhandleShow() : void - android.widget.Toast.TNinvokePopup(LayoutParams) : void - android.widget.PopupWindowmakeVisible() : void - android.app.ActivityonBarViewAttached() : void - com.android.systemui.statusbar.phone.PhoneStatusBaronCreate() : void - com.android.systemui.LoadAverageServiceopenPanel(PanelFeatureState, KeyEvent) : void - com.android.internal.policy.impl.PhoneWindowsetVisible(boolean) : void - android.widget.ZoomButtonsControllershow() : void - android.app.Dialogshow() : void - com.android.internal.policy.impl.KeyguardViewManagershow(int) : void - android.widget.MediaControllershowCompatibilityHelp() : void - com.android.systemui.statusbar.tablet.TabletStatusBarshowSafeModeOverlay() : void - com.android.server.am.ActivityManagerServicestart() : void - com.android.systemui.statusbar.StatusBarstartAnimation(Runnable, int, int, boolean, boolean) : void - com.android.systemui.screenshot.GlobalScreenshotupdateRecentsPanel() : void - com.android.systemui.statusbar.phone.PhoneStatusBarupdateSettings() : void - com.android.internal.policy.impl.PhoneWindowManager
通過查看調用,可以看到很多地方都有調用。大概過一下,就能發現很多熟悉的東西,比如PhoneStatusBar,ActivityThread,PhoneWindow,PopupWindow,Activity,Toast,Dialog 等等。那這裡感覺就比較明顯了,這些熟悉的控制項和類,就是通過windowmanager ,來把自己的view和介面加到系統中了。
分析Dialog 是如何加入到windowmanager 的
柿子先撿軟的捏, 挑個簡單的先。看下show() : void - android.app.Dialog
這不就是經常調用的大名鼎鼎的show() 嘛。 dialog寫完最後調用的show() 方法,沒錯就是它。從這個控制項來看確實就是這樣通過windowmanager, show 一個dialog,就是windowmanager.addview一下啦。 那麼addview的時候,自然就應該是dialog的根view,父控制項嘍。查看一下代碼驗證一下我們的猜測。
/** * Start the dialog and display it on screen. The window is placed in the * application layer and opaque. Note that you should not override this * method to do initialization when the dialog is shown, instead implement * that in {@link #onStart}. */ public void show() { if (mShowing) { if (mDecor != null) { if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR); } mDecor.setVisibility(View.VISIBLE); } return; } mCanceled = false; if (!mCreated) { dispatchOnCreate(null); } onStart(); mDecor = mWindow.getDecorView(); if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) { mActionBar = new ActionBarImpl(this); } WindowManager.LayoutParams l = mWindow.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) { WindowManager.LayoutParams nl = new WindowManager.LayoutParams(); nl.copyFrom(l); nl.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; l = nl; } try { mWindowManager.addView(mDecor, l); mShowing = true; sendShowMessage(); } finally { } }被add進 windowmanager中的是這個mDecor, 來自mWindow.getDecorView();
/** * Retrieve the top-level window decor view (containing the standard * window frame/decorations and the client's content inside of that), which * can be added as a window to the window manager. * * <p><em>Note that calling this function for the first time "locks in" * various window characteristics as described in * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p> * * @return Returns the top-level window decor view. */ public abstract View getDecorView();從注釋中的描述,確實是 top-level window decor view。 印證了前面的猜測。
接下來看下這個mDecor到底是什麼。
找到具體的實現com.android.internal.policy.impl.PhoneWindow.getDecorView()
@Override public final View getDecorView() { if (mDecor == null) { installDecor(); } return mDecor; }調用了installDecor()
private void installDecor() { if (mDecor == null) { mDecor = generateDecor(); mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); }<span style="white-space:pre"></span>//...... }繼續generateDecor();
protected DecorView generateDecor() { return new DecorView(getContext(), -1); }
public DecorView(Context context, int featureId) { super(context); mFeatureId = featureId; }
/** The feature ID of the panel, or -1 if this is the application's DecorView */ private final int mFeatureId;
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker
最後,從類定義中看到,DecorView 是一個 FrameLayout 並實現了RootViewSurfaceTaker
所以,回顧一下,這個 top-level window decor view,被add 進windowmanager 中的就是一個framelayout。
這點通過 hierarchyviewer 分析 UI 時得到印證。
如何使用,請移步官方文檔《Using Hierarchy Viewer》,已經有很多人寫了中文文檔,不再贅述。
好了,到這裡知道了系統dialog 中對應的 頂層 view 是一個framelayout, 對應到framework中 viewrootimpl 中的rootview。
同理,PopupWindow 是怎麼乾的
然後舉一反三,看下PopupWindow 是怎麼搞的。
invokePopup(LayoutParams) : void - android.widget.PopupWindowshowAsDropDown(View, int, int) : void - android.widget.PopupWindowshowAtLocation(IBinder, int, int, int) : void - android.widget.PopupWindow
這個介面showAsDropDown就是PopupWindow 的api嘛,
這裡簡單的列出關鍵代碼,就不一一分析了。
mWindowManager.addView(mPopupView, p);
if (mBackground != null) { final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams(); int height = ViewGroup.LayoutParams.MATCH_PARENT; if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) { height = ViewGroup.LayoutParams.WRAP_CONTENT; } // when a background is available, we embed the content view // within another view that owns the background drawable PopupViewContainer popupViewContainer = new PopupViewContainer(mContext); PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, height ); popupViewContainer.setBackgroundDrawable(mBackground); popupViewContainer.addView(mContentView, listParams); mPopupView = popupViewContainer; } else { mPopupView = mContentView;
public void setContentView(View contentView) { if (isShowing()) { return; } mContentView = contentView; if (mContext == null && mContentView != null) { mContext = mContentView.getContext(); } if (mWindowManager == null && mContentView != null) { mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); } }所以PopupWindow 是把setContentView 的view 加入到了windowmanager 中。
接下來是activity
那最常用的activity 中又是怎樣的呢?
void makeVisible() { if (!mWindowAdded) { ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; } mDecor.setVisibility(View.VISIBLE); }這裡的mDecor 就不像前面的dialog 和popupWindow 中那麼容易分析了。
首先掃了一下activity 這個類,發現沒有賦值語句, 查看一下基類,也沒有。最後查看所有mDecor 的調用。
mDecor - android.app.ActivitydispatchKeyEvent(KeyEvent) : boolean - android.app.ActivityhandleDestroyActivity(IBinder, boolean, int, boolean) : void - android.app.ActivityThread (2 matches)handleResumeActivity(IBinder, boolean, boolean) : void - android.app.ActivityThread (2 matches)handleSendResult(ResultData) : void - android.app.ActivityThreadhandleWindowVisibility(IBinder, boolean) : void - android.app.ActivityThreadmakeVisible() : void - android.app.Activity (2 matches)onWindowAttributesChanged(LayoutParams) : void - android.app.ActivitysetVisible(boolean) : void - android.app.ActivityupdateVisibility(ActivityClientRecord, boolean) : void - android.app.ActivityThread
逐個查看,最後僅在handleResumeActivity() : void - android.app.ActivityThread 中 找到相關賦值。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); 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(); <span style="color:#ff0000;">View decor = r.window.getDecorView();</span> decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); <span style="color:#ff0000;">a.mDecor = decor;</span> l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); }
<span style="white-space:pre"></span>//...... }
這裡代碼涉及較多內容,集中注意力,看decorView ,注意到decor = r.window.getDecorView();。
而ActivityClientRecord r = performResumeActivity(token, clearHide);
也就是說是具體resume 的activity 的 window 的decorView()。
通過前面dialog 部分的分析,我們知道使用phoneWindow 的話,那就是framelayout 的那個 decorView。而目前還沒有其他類型的window,這從window的繼承關係可以看出。
在這裡我們看到,activity 在resume的時候,通過判斷是否要visible ,來把activity 的window 的decorView 加到windowmanager中,那麼在activity的生命週期就應該在對應的狀態中從windowmanager中移除該decorView。
在前面mDecor 的調用中,確實發現了在android.app.ActivityThread.handleDestroyActivity(IBinder, boolean, int, boolean),中。
private void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance) { ActivityClientRecord r = performDestroyActivity(token, finishing, configChanges, getNonConfigInstance); if (r != null) { cleanUpPendingRemoveWindows(r); WindowManager wm = r.activity.getWindowManager(); <span style="color:#ff0000;">View v = r.activity.mDecor;</span> if (v != null) { if (r.activity.mVisibleFromServer) { mNumVisibleActivities--; } IBinder wtoken = v.getWindowToken(); if (r.activity.mWindowAdded) { if (r.onlyLocalRequest) { // Hold off on removing this until the new activity's // window is being added. r.mPendingRemoveWindow = v; r.mPendingRemoveWindowManager = wm; } else { <span style="color:#ff0000;"> wm.removeViewImmediate(v);</span> } }
<span style="white-space:pre"></span>}
<span style="white-space:pre"></span>//...... }
至此,通過看了dialog, popupwindow, 和activity 中的部分源碼, 知道了普通介面(dialog, popupwindow, 和activity )的第一個view, 是怎麼來的,是如何加入到系統中的,也就是windowManager。
一句話結論應用程式層面的介面都是通過windowmanager 加入到framework 中的,ViewRootImpl 是framework對view 的抽象, 介面管理的根節點。
留的尾巴
弄清楚第一個的問題後,接下來爭取比較完整地回顧一下view 的建立,繪製,layout過程。to be continued...
在分析過程中,簡單的接觸到了framework中處理activity的部分流程,handleResumeActivity(),handleDestroyActivity()。另一個問題就是系統framework 是如何管理activity的生命週期的。//TODO
弄清楚第一個的問題後,接下來爭取比較完整地回顧一下view 的建立,繪製,layout過程。to be continued...
在分析過程中,簡單的接觸到了framework中處理activity的部分流程,handleResumeActivity(),handleDestroyActivity()。另一個問題就是系統framework 是如何管理activity的生命週期的。//TODO