前言
當(dāng)我們打開(kāi)一個(gè)activity需要顯示內(nèi)容的時(shí)候,只需要在onCreate方法中執(zhí)行setContentView方法冶共,一行代碼搞定乾蛤,很簡(jiǎn)單每界,有么有。但是幻捏,有沒(méi)有想過(guò)setConentView方法內(nèi)部盆犁,執(zhí)行了那些操作,Window篡九、DecorView谐岁、ViewRootImpl是怎么回事,本文就來(lái)一步步分析其內(nèi)部工作流程榛臼。源碼基于Android API 21伊佃。
Activity#setContentView
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
...
public Window getWindow() {
return mWindow;
}
可以看到,Activity#setContentView內(nèi)部調(diào)用了mWindow
中的setContentView
方法沛善,那這個(gè)mWindow
是什么呢航揉?可以看出它是Window類(lèi)型的。先來(lái)看看mWindow
是在哪里創(chuàng)建的金刁,通過(guò)源碼可以發(fā)現(xiàn)在Activity#attach中對(duì)其進(jìn)行了賦值操作(注:在Activity啟動(dòng)過(guò)程中帅涂,會(huì)執(zhí)行ActivityThread#performLaunchActivity方法,在這個(gè)方法中會(huì)調(diào)用Activity#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, IVoiceInteractor voiceInteractor) {
...
//在這里創(chuàng)建Activity所屬的Window對(duì)象并賦值給mWindow
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);//Activity實(shí)現(xiàn)了Window的Callback接口媳友,這里給Window注冊(cè)監(jiān)聽(tīng)
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);
}
...
創(chuàng)建Window并初始化DecorView
接著上文看下PolicyManager#makeNewWindow
public static Window makeNewWindow(Context context) {
// this will likely crash somewhere beyond so we log it.
Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
"Call to PolicyManager.makeNewWindow is not supported", null);
return null;
}
發(fā)現(xiàn)PolicyManager#makeNewWindow方法并沒(méi)有具體實(shí)現(xiàn),并且PolicyManager中的方法全在接口IPolicy中聲明了产捞。
public interface IPolicy {
public Window makeNewWindow(Context context);
public LayoutInflater makeNewLayoutInflater(Context context);
public WindowManagerPolicy makeNewWindowManager();
public FallbackEventHandler makeNewFallbackEventHandler(Context context);
}
其真正的實(shí)現(xiàn)是在Policy類(lèi)中醇锚,如下:
public Window makeNewWindow(Context context) {
return new PhoneWindow(context);
}
可以看到,最終是實(shí)例化了PhoneWindow,PhoneWindow是Window的唯一子類(lèi)坯临。
至此焊唬,Window的創(chuàng)建過(guò)程就結(jié)束了,接著看PhoneWindow#setContentView方法看靠。
@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.
//重點(diǎn)在這里赶促,mContentParent為null,執(zhí)行installDecor方法挟炬。
if (mContentParent == null) {
installDecor();//初始化DecorView
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//這個(gè)方法內(nèi)部會(huì)把我們的布局文件的內(nèi)容添加mContentParent中鸥滨。
mLayoutInflater.inflate(layoutResID, mContentParent);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();//執(zhí)行回調(diào)
}
}
這個(gè)方法中主要兩個(gè)工作:1. 如果mContentParent
為null,則執(zhí)行installDecor
方法辟宗,即初始化DecorView;2. 通過(guò)mLayoutInflater.inflate(layoutResID, mContentParent
將我們的布局內(nèi)部添加到mContentParent
吝秕;3. 當(dāng)屏幕的內(nèi)容發(fā)生改變時(shí)泊脐,執(zhí)行回調(diào)方法onContentChanged。那么這個(gè)mContentParent
是指什么呢烁峭?這里先保留疑問(wèn)容客,我們接下來(lái)就會(huì)解釋秕铛。來(lái)看下installDecor
方法做了什么工作。
private void installDecor() {
//如果DecorView為null缩挑,則創(chuàng)建DecorView
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
...
if (mContentParent == null) {
初始化DecorView的布局結(jié)構(gòu)但两,獲取mContentParent并返回。
mContentParent = generateLayout(mDecor);
...
}
...
}
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
...
//獲取一堆主題中設(shè)置的屬性進(jìn)行相關(guān)設(shè)置(好長(zhǎng)一堆~~)
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
if (...) {
...
}
...
// Inflate the window decor.
//根據(jù)不同的窗口特征供置,layoutResource對(duì)應(yīng)不同的布局文件谨湘,用來(lái)填充DecorView(又是很長(zhǎng)一堆~~)。
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!");
}
...
//重點(diǎn)在這里
//把對(duì)應(yīng)的布局文件填充并加載到DecorView芥丧,初始化DecorView的結(jié)構(gòu)
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
//看到contentParent了吧,它對(duì)應(yīng)了DecorView中id為com.android.internal.R.id.content的ViewGroup紧阔。
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
return contentParent;
}
這個(gè)主要做了三件事:1.根據(jù)主題中屬性進(jìn)行一些相關(guān)設(shè)置2.根據(jù)不同的窗口特征,獲取對(duì)應(yīng)的布局文件续担,用來(lái)初始化DecorView的布局結(jié)構(gòu)擅耽。3.獲取contentParent,contentParent對(duì)應(yīng)了DecorView布局文件中id為com.android.internal.R.id.content的ViewGroup物遇,它其實(shí)是一個(gè)FrameLayout乖仇。如圖所示:
再回到上文PhoneWindow#setContentView方法中,當(dāng)獲取到mContentParent
后询兴,還會(huì)執(zhí)行mLayoutInflater.inflate(layoutResID, mContentParent)
將我們寫(xiě)的xml布局文件加載到mContentParent乃沙,至于加載細(xì)節(jié)我們這里就不做分析了。
小結(jié):至此蕉朵,我們已經(jīng)實(shí)例化了Window崔涂,初始化了DecorView布局結(jié)構(gòu),并且把我們布局文件加載到了mContentParent
中始衅。不過(guò)DecorView并沒(méi)有顯示出來(lái)冷蚂,因?yàn)閂iew并不能單獨(dú)存在,必須依附于Window汛闸。
在Activity在啟動(dòng)流程中(Activity的啟動(dòng)流程很復(fù)雜蝙茶,我們這里不做具體分析),當(dāng)執(zhí)行了ActivityThread#performLaunchActivity方法后還會(huì)執(zhí)行ActivityThread#handleResumeActivity方法诸老,在這個(gè)方法中首先會(huì)調(diào)用Activity的onResume
方法隆夯,接著調(diào)用Activity的makeVisible
方法。
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();//獲取WindowManager
wm.addView(mDecor, getWindow().getAttributes());//通過(guò)WindowManager完成Window的添加過(guò)程
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);//讓DecorView可見(jiàn)
}
此方法將完成Window的添加過(guò)程别伏,以及讓DecorView顯示出來(lái)蹄衷,至此Activity界面加載流程就結(jié)束了
將DecorView添加到Window
接著上文,看看DecorView是如何添加到Window中的厘肮。Window其實(shí)是個(gè)抽象的概念愧口,它并不是一個(gè)實(shí)體。我們可以把Window理解成一種抽象的功能集合类茂,每個(gè)Window都關(guān)聯(lián)一個(gè)View和ViewRootImpl耍属。
public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
發(fā)現(xiàn)ViewManager是一個(gè)接口托嚣,WindowManager繼承了ViewManager,也是一個(gè)接口厚骗∈酒簦看它的實(shí)現(xiàn)類(lèi)WindowManagerImpl中的addView
方法。
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
發(fā)現(xiàn)WindowManagerImpl#addView并沒(méi)有實(shí)現(xiàn)具體細(xì)節(jié)领舰,而是交給了WindowManagerGlobal中的addView
去處理夫嗓。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
synchronized (mLock) {
...
//實(shí)例化ViewRootImpl,它是WindowManager和DecorView的紐帶
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);//保存所有Window對(duì)應(yīng)的View
mRoots.add(root);//保存所有Window對(duì)應(yīng)的ViewRootImpl
mParams.add(wparams);//保存所有Window對(duì)應(yīng)的布局參數(shù)
}
// do this last because it fires off messages to start doing things
try {
//重點(diǎn)是這個(gè)方法 開(kāi)始出發(fā)消息做事情
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
我們接著來(lái)看ViewRootImpl#setView方法
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...//省略一大波代碼
// 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.
//內(nèi)部最終會(huì)調(diào)用performTraversals方法開(kāi)啟View的繪制流程提揍。
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//通過(guò)IWindowSession來(lái)完成Window的添加過(guò)程
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, 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();
}
}
...//省略一大波代碼
}
這個(gè)方法很長(zhǎng)啤月,我們只看與本文主流程相關(guān)部分。有兩個(gè)重要的地方劳跃。
- 通過(guò)requestLayout方法開(kāi)啟頂級(jí)View的測(cè)繪谎仲。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "performTraversals");
try {
performTraversals();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
private void performTraversals() {
...
//執(zhí)行測(cè)量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
//執(zhí)行布局
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
//執(zhí)行繪制
performDraw();
}
在performTraversals
方法依次會(huì)執(zhí)行performMeasure
、performLayout
以及performDraw
來(lái)完成對(duì)頂級(jí)View的測(cè)量刨仑、布局郑诺、繪制。performMeasure
方法中會(huì)調(diào)用measure
方法杉武,measure
方法中又會(huì)調(diào)用onMeasure
方法辙诞,在onMeasure
方法中會(huì)完成子View的measure過(guò)程,measure過(guò)程就從父View傳到了子View轻抱。子View又會(huì)重復(fù)父View的動(dòng)作飞涂,如此反復(fù),就完成了整個(gè)View樹(shù)的測(cè)量過(guò)程祈搜。performLayout
以及performDraw
方法與performMeasure
類(lèi)似较店。可以說(shuō)View的三大工作流程是performTraversals
開(kāi)始的。
- 通過(guò)WindowSession完成window的添加過(guò)程容燕。
先來(lái)看下mWindowSession的創(chuàng)建過(guò)程
public ViewRootImpl(Context context, Display display) {
mWindowSession = WindowManagerGlobal.getWindowSession();//獲取WindowSession
}
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
}
return sWindowManagerService;
}
}
mWindowSession
類(lèi)型是IWindowManager梁呈,它是一個(gè)Binder
對(duì)象,可見(jiàn)Window的添加過(guò)程是個(gè)IPC過(guò)程蘸秘。
openSession
方法是在WindowManagerService具體實(shí)現(xiàn)的
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
在WindowManagerService#openSession實(shí)例化了Session并返回官卡。Session是IWindowSession的實(shí)現(xiàn)類(lèi)。addToDisplay
方法也是在Session中具體實(shí)現(xiàn)的醋虏。
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets,
InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outInputChannel);
}
在這個(gè)方法內(nèi)部又調(diào)用了WindowManagerService#addWindow方法寻咒,最終完成了Window的添加過(guò)程。其具體細(xì)節(jié)不再說(shuō)了颈嚼。毛秘。。
希望能對(duì)您有所幫助粘舟,若文中有錯(cuò)誤或表述不當(dāng)?shù)牡胤竭€望指出熔脂,互相交流,共同成長(zhǎng)柑肴!
相關(guān)文章:
Android View 測(cè)量流程(Measure)源碼解析
Android View 布局流程(Layout)源碼解析
Android View 繪制流程(Draw)源碼解析