Activity是Android開發(fā)者寫第一行代碼起就開始接觸到的。而在onCreate方法中調(diào)用setContentView(R.layout.main_activity),恐怕也是絕大多數(shù)開發(fā)者的頭等任務(wù)。然后我們可以調(diào)用findViewById(R.id.xxx)來(lái)獲取布局中的某一個(gè)View。通過給View設(shè)置點(diǎn)擊事件的監(jiān)聽來(lái)響應(yīng)用戶的操作。就這么簡(jiǎn)單我們和Android的View過了一段幸福的時(shí)光,直到有一天,在某個(gè)編碼過后的深夜,在剛要合上電腦的一剎那,View低沉卻又如心頭一擊的一句話,打破了很長(zhǎng)時(shí)間以來(lái)看似平淡幸福的生活局面,“喂,我們?cè)谝黄疬@么久了,可是你真的了解我嗎?”。是啊,我了解View嗎,它從哪里來(lái)?它和Activity的關(guān)系是什么?為什么有時(shí)和它相處,它的脾氣好像總是有點(diǎn)怪,比如在onCreate方法中獲取View的width,一直為0?還有onMeasure onLayout onDraw方法是從什么時(shí)候開始調(diào)用的?好吧是時(shí)候帶你一起來(lái)認(rèn)識(shí)認(rèn)識(shí)你覺得熟悉,卻又不是那么熟悉的View。
在開始本文前,還是按照慣例,拋出幾個(gè)問題讓讀者思考,讓讀者帶著思考來(lái)閱讀文章,一來(lái)可以讓讀者更有針對(duì)性,二來(lái)也可以反映出本文的大概內(nèi)容,讓讀者一眼就能看到文章內(nèi)容的大概
setContentView一般都是在onCreate中調(diào)用,可以在onResume中調(diào)用嗎?
Activity的ContentView是什么時(shí)候在Activity上顯示給用戶看的
Window、Activity、View他們?nèi)咧g的關(guān)系是什么
WindowManager是什么,它和View之間的關(guān)系是什么
WindowManagerService在整個(gè)View體系中充當(dāng)什么角色
ViewRootImpl是什么,它是什么時(shí)候創(chuàng)建的,它與View之間的關(guān)系是什么
View的measure、onMeasure、layout、onLayout、draw、onDraw是什么意思,它們之間有什么關(guān)系
View的MeasureSpec是什么,它是怎么控制View和子View的測(cè)量的
View的requestLayout和invalidate區(qū)別是什么
上面9個(gè)問題,你可能平時(shí)或多或少都有思考過。如果這些問題你都能答上來(lái),那么恭喜你,你對(duì)View的掌握可以說(shuō)是通透了。如果不是,那么請(qǐng)跟我一起探尋View的秘密
1. Activity.setContentView中發(fā)生了什么
1.1 Activity.setContentView
首先我們來(lái)看一下源碼,因?yàn)橹挥袃尚?,所以直接貼代碼吧
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
1.2. Activity.getWindow
public Window getWindow() {
return mWindow;
}
1.3. Activity.attach
好了,就不跟你貼mWindow的定義的源碼了,請(qǐng)你直接翻閱源碼,mWindow是Window對(duì)象。具體來(lái)說(shuō)是PhoneWindow對(duì)象。接下來(lái)我們看下mWindow在什么時(shí)候初始化的,查詢一番,發(fā)現(xiàn)是在Activity的attach方法中,被賦值的,如果你對(duì)attach方法感覺到陌生又好奇,可以參考這篇文章。在這里我也對(duì)attach方法做一個(gè)簡(jiǎn)單的講解,省得讀者被中斷。簡(jiǎn)單來(lái)說(shuō)Activity并不是一個(gè)真正的Context對(duì)象,Activity只是有名無(wú)實(shí),真正的Context對(duì)象是通過調(diào)用Activity的
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)
方法賦值給Activity的。同時(shí)在該方法中,初始化了mWindow對(duì)象
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);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...省略一些代碼
//調(diào)用WindowManagerImpl.createLocalWindowManager返回WindowManagerImpl
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
//如果mParent不為空
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
}
1.4. PhoneWindow和WindowManger
從上面Activity.attach方法中,我們看到mWindow = new PhoneWindow(this,window)、mWindow.setWindowManager()和mWindowManager = mWindow.getWindowManager()這幾行比較重要的代碼。首先我們來(lái)看下PhoneWindow的源碼,看下PhoneWindow.setContentView做了什么。關(guān)于WindowManager我們?cè)诮榻B完了PhoneWindow后我們?cè)賮?lái)講解
1.5. PhoneWindow.setContentView
@Override
public void setContentView(int layoutResID) {
//如果mContentParent==null初始化DecorView
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 {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
乍一看這個(gè)方法看起來(lái)也很簡(jiǎn)單啊,只是mContentParent這是個(gè)什么?好吧,先留下這個(gè)問題,接著看installDecor方法
1.6 PhoneWindow.installDecor
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//return new DecorView
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
//如果mContentParent==null 初始化mContentParent
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...省略代碼
}
}
}
好吧這里還引出一個(gè)DecorView mDecor對(duì)象。我們看下DecorView的源碼可以看到它是FrameLayout的子類。關(guān)于mDecor和mContentParent對(duì)象我們可以這樣認(rèn)為。mDecor對(duì)象代表的PhoneWindow的根View。而mContentParent對(duì)象是mDecor對(duì)象的子View。而mContentParent是Activity.setContentView的contentView的父View。由此我們可以知道。Activity的contentView是依附在PhoneWindow的DecorView上的。Activity的ActionBar之類的控件也正是PhoneWindow代為操勞的。
好吧,至此我們應(yīng)該明白一個(gè)道理,Activity里所有用戶肉眼能夠看到的組件,其實(shí)都是依附在PhoneWindow上。你可以把PhoneWindow簡(jiǎn)單認(rèn)為是Activity的ContentView。那么PhoneWindow是如何被添加到Activity上被用戶看到的呢?答案是通過WindowManager。
1.7 WindowManager和WindowMangerService
WindowMangerService是一個(gè)系統(tǒng)級(jí)的服務(wù),在開機(jī)的時(shí)候,系統(tǒng)會(huì)啟動(dòng)該服務(wù)。至于WindowManagerService的實(shí)現(xiàn)原理不是本文的任務(wù)。WindowManager和WindowMangerService的關(guān)系 與ActivityManager和ActivityManagerService是一樣的。WMS是系統(tǒng)級(jí)的服務(wù),它是真正將View添加到手機(jī)屏幕上展示給用戶看的。而WindowManger你可以簡(jiǎn)單認(rèn)為是WMS運(yùn)行在應(yīng)用程序端的遠(yuǎn)程代理對(duì)象(真正的代理對(duì)象是WindoManagerGlobal的sWindowManagerService對(duì)象)。因?yàn)槲覀兊膽?yīng)用程序是無(wú)法直接直接訪問WMS等系統(tǒng)服務(wù),需要通過AIDL來(lái)實(shí)現(xiàn)跨進(jìn)程通信。WindowManager和WMS正是AIDL中的遠(yuǎn)程代理對(duì)象和本地服務(wù)對(duì)象(不懂AIDL的同學(xué)需要去補(bǔ)補(bǔ)課喲,這里默認(rèn)大家都懂了)。通過WindowManager.addView(View view, ViewGroup.LayoutParams params)可以把View展示給用戶
1.8 Window.setWindowManager
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);
}
1.9 WindowManagerImpl.createLocalWindowManager
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
原來(lái)是直接new WindowManagerImpl對(duì)象啊。好吧Activity的mWindowManager原來(lái)是WindowManagerImpl對(duì)象,好吧我們來(lái)看下WindowManagerImpl源碼
1.10 WindowManagerImpl
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
}
原來(lái)WindowManagerImpl addView是借助WindowManagerGlobal mGlobal來(lái)實(shí)現(xiàn)的。有經(jīng)驗(yàn)的老司機(jī)一眼就能看出WindowManagerGlobal實(shí)現(xiàn)了單例模式
1.11 WindowManagerGlobal源碼
public final class WindowManagerGlobal {
private static final String TAG = "WindowManager";
//WindowManagerGlobal單例對(duì)象
private static WindowManagerGlobal sDefaultWindowManager;
//WindowManagerService在客戶端的遠(yuǎn)程代理對(duì)象
private static IWindowManager sWindowManagerService;
//單個(gè)App客戶端與WindowManagerService對(duì)應(yīng)的一個(gè)IWindowSession
private static IWindowSession sWindowSession;
private final Object mLock = new Object();
//App進(jìn)程中所有Activity的DecorView
private final ArrayList<View> mViews = new ArrayList<View>();
//App進(jìn)程中所有Activity的DecorView對(duì)應(yīng)的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
//要結(jié)束生命的Views
private final ArraySet<View> mDyingViews = new ArraySet<View>();
private Runnable mSystemPropertyUpdater;
private WindowManagerGlobal() {
}
public static void initialize() {
getWindowManagerService();
}
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
}
/**獲取WindowMangerService在APP進(jìn)程端的遠(yuǎn)程對(duì)象**/
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
sWindowManagerService = getWindowManagerService();
ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
/**
* 1.如果view已經(jīng)被add了,報(bào)錯(cuò)
* 2.創(chuàng)建ViewRootImpl,調(diào)用ViewRootImpl.setView
**/
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
// If there's no parent, then hardware acceleration for this view is
// set from the application's hardware acceleration setting.
final Context context = view.getContext();
if (context != null
&& (context.getApplicationInfo().flags
& ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
// Start watching for system property changes.
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
SystemProperties.addChangeCallback(mSystemPropertyUpdater);
}
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
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);
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.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (view != null) {
InputMethodManager imm = InputMethodManager.getInstance();
if (imm != null) {
imm.windowDismissed(mViews.get(index).getWindowToken());
}
}
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
void doRemoveView(ViewRootImpl root) {
synchronized (mLock) {
final int index = mRoots.indexOf(root);
if (index >= 0) {
mRoots.remove(index);
mParams.remove(index);
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
}
if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
doTrimForeground();
}
}
private int findViewLocked(View view, boolean required) {
final int index = mViews.indexOf(view);
if (required && index < 0) {
throw new IllegalArgumentException("View=" + view + " not attached to window manager");
}
return index;
}
}
總結(jié)WindowManagerGlobal功能如下
獲取WMS的遠(yuǎn)程代理對(duì)象
獲取WMS的IWindowSession對(duì)象
創(chuàng)建ViewRootImpl對(duì)象
通過ViewRootImpl對(duì)象來(lái)addView removeView updateViewLayout
好吧,至此我們引出了View體系中至關(guān)重要的ViewRootImpl對(duì)象,我們放在一講講解,我們回頭再想想,在Activity的onCreate中調(diào)用setContentView,生成的PhoneWindow的DecorView對(duì)象什么時(shí)候被WindowManger調(diào)用addView的呢
2. ActivityThread.handleResumeActivity 中將PhoneWindow的DecorView add到WindowManger上
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
//調(diào)用Activity的onResume方法
r = performResumeActivity(token, clearHide, reason);
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;
//如果不可見說(shuō)明在啟動(dòng)另一個(gè)Activity
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
//PhoneWindow 在Activity的attach中初始化的
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
//剛開始的時(shí)候 設(shè)置成不可見
decor.setVisibility(View.INVISIBLE);
//返回的是WindowManagerImpl
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
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();
// 第一次應(yīng)該是為null
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
//這里會(huì)把decor加載到WindowManagerImpl中
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, false /* force */);
// 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) {
performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
+ r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
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) {
throw ex.rethrowFromSystemServer();
}
}
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
從源碼中我們看到在handleResumeActivity中通過wm.add(decor,l)把Activity的DecorView加載到WindowManger上的。從ActivityThread中處理Activity的生命周期順序是 onCreate -> onResume -> wm.add(decor,l)。所以這里能解釋不少問題了,1.可以在onResume中調(diào)用setContentView 2.在onCreate中調(diào)用View.getWidth等方方法是返回不了正確值的。
3. ViewRootImpl.setView
3.1 ViewRootImpl.setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
//如果mView==null 表示是新增加的
if (mView == null) {
mView = view;
...省略
requestLayout();
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();
}
}
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
mAdded = false;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
+ " is exiting");
case WindowManagerGlobal.ADD_DUPLICATE_ADD:
throw new WindowManager.BadTokenException(
"Unable to add window -- window " + mWindow
+ " has already been added");
case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
// Silently ignore -- we would have just removed it
// right away, anyway.
return;
case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- another window of type "
+ mWindowAttributes.type + " already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException("Unable to add window "
+ mWindow + " -- permission denied for window type "
+ mWindowAttributes.type);
case WindowManagerGlobal.ADD_INVALID_DISPLAY:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified display can not be found");
case WindowManagerGlobal.ADD_INVALID_TYPE:
throw new WindowManager.InvalidDisplayException("Unable to add window "
+ mWindow + " -- the specified window type "
+ mWindowAttributes.type + " is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
}
}
}
上面我保留了兩行比較重要的方法requestLayout()和 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel);
requestLayout()主要是讓View經(jīng)歷measure layout draw 三個(gè)階段,mWindowSession.addToDisplay應(yīng)該是交給WMS去請(qǐng)求其他服務(wù)渲染界面。我們主要來(lái)看下requestLayout()
我們來(lái)跟蹤下requestLayout源碼
3.2 ViewRootImpl.requestLayout
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
//注意這個(gè)變量
mLayoutRequested = true;
scheduleTraversals();
}
}
注意mLayoutRequested = true,只有mLayoutRequested為true,measure layout draw三個(gè)階段才會(huì)全部經(jīng)歷,否則只會(huì)經(jīng)歷draw階段,這也就是View的reqeustLayout和invalidate的區(qū)別,requestLayout會(huì)經(jīng)歷完整的三個(gè)階段
3.3 ViewRootImpl.scheduleTraversals()
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
從源碼中可以看出交由mChoreographer對(duì)象去執(zhí)行mTraversalRunnable對(duì)象
3.4 ViewRootImpl的TraversalRunnable對(duì)象
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
最終調(diào)用了ViewRootImpl.doTraversal()
3.5 ViewRootImpl.doTraversal()
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
跟蹤performTraversals()代碼
3.6 ViewRootImpl.performTraversals()
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
host.debug();
}
if (host == null || !mAdded)
return;
//正在遍歷
mIsInTraversal = true;
//馬上要畫
mWillDrawSoon = true;
//windowSize是否改變
boolean windowSizeMayChange = false;
boolean newSurface = false;
boolean surfaceChanged = false;
WindowManager.LayoutParams lp = mWindowAttributes;
//預(yù)期的window的寬度
int desiredWindowWidth;
int desiredWindowHeight;
//當(dāng)前View是否可見
final int viewVisibility = getHostVisibility();
//view的可見性是否改變(1.不是第一次而且和上次的可見性不一樣)
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded);
//用戶肉眼看到的View的可見性是否改變
final boolean viewUserVisibilityChanged = !mFirst &&
((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
...省略代碼
Rect frame = mWinFrame;
if (mFirst) {
//如果是第一次
mFullRedrawNeeded = true;
//需要請(qǐng)求layout
mLayoutRequested = true;
//如果是狀態(tài)欄或者輸入法 需要使用真實(shí)的size
if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
Configuration config = mContext.getResources().getConfiguration();
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
...省略代碼
//如果是第一次 會(huì)調(diào)用View的onAttachedToWindow
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
//Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
if (viewVisibilityChanged) {//如果hostView的可見性變了 說(shuō)明window的可見性變了
mAttachInfo.mWindowVisibility = viewVisibility;
host.dispatchWindowVisibilityChanged(viewVisibility);
if (viewUserVisibilityChanged) {//如果肉眼可見性變了
host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
}
if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
endDragResizing();
destroyHardwareResources();
}
if (viewVisibility == View.GONE) {
// After making a window gone, we will count it as being
// shown for the first time the next time it gets focus.
mHasHadWindowFocus = false;
}
}
// Non-visible windows can't hold accessibility focus.
if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
//如果window不可見把Accessibility clear掉
host.clearAccessibilityFocus();
}
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);
boolean insetsChanged = false;
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
//如果是requestLayout
if (layoutRequested) {
final Resources res = mView.getContext().getResources();
if (mFirst) {
// make sure touch mode code executes by setting cached value
// to opposite of the added touch mode.
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
insetsChanged = true;
}
if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
insetsChanged = true;
}
if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
insetsChanged = true;
}
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
insetsChanged = true;
}
if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
insetsChanged = true;
}
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowSizeMayChange = true;
if (shouldUseDisplaySize(lp)) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
Configuration config = res.getConfiguration();
desiredWindowWidth = dipToPx(config.screenWidthDp);
desiredWindowHeight = dipToPx(config.screenHeightDp);
}
}
}
//1.開始測(cè)量
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
}
...省略代碼
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
//2 開始layout
performLayout(lp, mWidth, mHeight);
... 省略代碼
}
... 省略代碼
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
//3. 開始draw
performDraw();
} else {
if (isViewVisible) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}
總結(jié)這個(gè)函數(shù)的功能
- 測(cè)量View的大小
- 擺放View的位置(layout)
- 畫View(draw)
3.7 ViewRootImpl的performMeasure、performLayout、performDraw
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
...省略代碼
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...省略代碼
mInLayout = false;
}
由上可知會(huì)分別調(diào)用View的measure、layout、draw方法
4. View和ViewGroup相關(guān)
4.1 ViewGroup.measureChildWithMargins
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
4.2 ViewGroup.getChildMeasureSpec
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}