上篇文章中我們了解到了setContentView()
背后所發(fā)生的事情.先用上一篇文章最后總結(jié)的圖片來回顧一下setContentView()
的流程.
但是后來我發(fā)現(xiàn),我們只是知道了
mDecor
是怎樣與mContentParent
聯(lián)系在一起并將我們的activity_main.xml
布局添加到其中.而并不知道mDecor
是如何添加到PhoneWindow
和Activity
上的.所以本篇我們就來探究一下這個(gè)問題.
尋找我們的 mDecor
既然我們在PhoneWindow
里沒有發(fā)現(xiàn)mDecor
是如何添加到window上的.那么我就需要到別的地方去尋找mDecor
的線索,那我們要該從哪入手呢?還記得上一篇我們從Activity的setContentView()
出發(fā)時(shí),最終調(diào)用的是mWindow(PhoneWindow)
的setContentView()
函數(shù),而mDecor
是PhoneWindow
的成員變量,mWindow
是Activity
的成員變量,那么我們可以在Activity
的代碼中尋找一下我們的線索mDecor
.
果然通過在Activity
代碼中進(jìn)行mDecor
的關(guān)鍵字搜索,我們找到了線索.
View mDecor = null;
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
從上面的代碼可以看到Activity
通過WindowManager
的addView()
方法將mDecor
添加到Window
上.但我們發(fā)現(xiàn)這里的mDecor
與PhoneWindow
中的mDecor
好像并不相同.而且在代碼中(Activity
代碼中的mDecor
)與(PhoneWindow
的mDecor
)并沒有明顯的聯(lián)系在一起.難道線索就這么斷了?我決定使用絕招(搜索引擎),果然我找到了另一個(gè)切入點(diǎn).
經(jīng)過一番Internet后,我知道了View的布局繪制是發(fā)生在Activity的創(chuàng)建過程,而Activity的創(chuàng)建是由一個(gè)名為ActivityThread
的類進(jìn)行控制的.下面我們來驗(yàn)證一下,看看能否找到線索.
首先我們找到ActivityThread
的源碼,這里有兩種方法:
- 直接在Internet上閱讀在線的源碼
- 在Android Studio中新建一個(gè)簡單的工程,在
MainActivity
的onCreate()
函數(shù)中下一個(gè)斷點(diǎn),然后運(yùn)行調(diào)試.我們可以在調(diào)試工具的調(diào)用棧中找到ActivityThread
.
找到了ActivityThread
的源碼后,對makeVisible
關(guān)鍵字進(jìn)行搜索,果然找到了調(diào)用的地方,在handleResumeActivity()
中出現(xiàn)了makeVisible()
的調(diào)用,既然出現(xiàn)了相關(guān)的調(diào)用,那我們就看一下handleResumeActivity()
的代碼吧.
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;// 標(biāo)記1開始
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;
} // 標(biāo)注 1結(jié)束
// 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.
// 標(biāo)注 2開始
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();
}
}
// 標(biāo)注 2結(jié)束
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) {
}
}
}
代碼有點(diǎn)長,但我們只需關(guān)注兩個(gè)部分,我已經(jīng)在代碼上做了標(biāo)注.先來看一下標(biāo)注1
的代碼.
// 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;// 標(biāo)記1開始
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
// 關(guān)鍵部分
if (r.window == null && !a.mFinished && willBeVisible) {
// 1
r.window = r.activity.getWindow();
// 2
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 3
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
// 4
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
// 5
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;
} // 標(biāo)注 1結(jié)束
其中這里的r,a
分別是函數(shù)中的局部變量,r
是一個(gè)ActivityClientRecord
類型的對象,通過performResumeActivity()
函數(shù)獲取,代表的是Activity執(zhí)行完onResume()
之后的含有Activity相關(guān)狀態(tài)信息的對象.而a
則是r
所對應(yīng)的Activity.清楚了這兩個(gè)變量的意義后,我們可以對代碼進(jìn)行分析了.首先獲取了willBeVisible
的值,它代表的是activity是否將要顯示在手機(jī)屏幕上.接著來到被標(biāo)注為關(guān)鍵部分
的if
語句塊內(nèi)的代碼.代碼主要做了下面的事情(我已在上面的代碼中標(biāo)出了對應(yīng)的代碼句).
- 將
r.window
賦值為activity的mWindow
(即PhoneWindow
對象) - 獲取
r.window
的DecorView對象(即我們PhoneWindow
的mDecor
對象),并將其設(shè)置為不可見 - 獲取activity的
mWindowManager
對象和r.window
的布局參數(shù) - 將第2點(diǎn)獲取的
DecorView
對象賦值給a.mDecor
((將PhoneWindow
的mDecor
)與(Activity
的mDecor
)關(guān)聯(lián)起來) - 通過第3點(diǎn)獲取的
mWindowManager
對象根據(jù)布局參數(shù)將mDecor
添加到window上.
通過上面的代碼,我們成功的將mDecor
添加到window當(dāng)中,但我們的mDecor
還是不可見的,所以下面將要分析的標(biāo)注2
的代碼就是讓mDecor
變得可見.
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
// 標(biāo)注 2開始
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();
}
}
// 標(biāo)注 2結(jié)束
其實(shí)上面的代碼我們需要關(guān)心的就只有一句,就是r.activity.makeVisible();
這句.這句話調(diào)用了我們之前在Activity
源碼中找到的makeVisible()
函數(shù),將mDecor
設(shè)置為可見.到此,我們知道了mDecor
是怎樣添加到window上的了.
是終點(diǎn)也是起點(diǎn)
作為一個(gè)熱愛技術(shù)的年輕人(碼農(nóng)),怎么可能會(huì)在這里就停止前進(jìn)的腳步呢?所以在上面makeVisible()
函數(shù)中的一句代碼wm.addView(mDecor, getWindow().getAttributes());
展開了新的起點(diǎn) - - - 繼續(xù)探索View的工作原理(希望能有新的發(fā)現(xiàn)).
我毫不猶豫的點(diǎn)進(jìn)了addView()
的源碼,卻發(fā)現(xiàn)這是ViewManager
接口的方法.
/** Interface to let you add and remove child views to an Activity. To get an instance
* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*/
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);
}
這個(gè)接口是用于將View
添加到Activity
或從Activity
移除View
的.既然這樣,那就去找它的實(shí)現(xiàn)類.從下面Activity
的部分源碼中不難發(fā)現(xiàn)addView()
是通過Activity
的mWindowManger
調(diào)用的,而Activity
的mWindowManager
是通過Window
獲取的.
private WindowManager mWindowManager;
private Window mWindow;
/** Retrieve the window manager for showing custom windows. */
public WindowManager getWindowManager() {
return mWindowManager;
}
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) {
........
mWindowManager = mWindow.getWindowManager();
........
}
由此我們可以轉(zhuǎn)到Window
的源碼中尋找mWindowManager
的下落.在Window
的源碼中,很容易就能找到我們的mWindowManager
的相關(guān)代碼了.
private WindowManager mWindowManager;
/**
* Set the window manager for use by this Window to, for example,
* display panels. This is <em>not</em> used for displaying the
* Window itself -- that must be done by the client.
*
* @param wm The window manager for adding new windows.
*/
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);
}
這里的mWindowManager
是通過WindowManagerImpl
的createLocalWindowManager()
函數(shù)進(jìn)行賦值的.所以繼續(xù)轉(zhuǎn)到WindowManagerImpl
的代碼中.在WindowManagerImpl
的代碼中終于發(fā)現(xiàn)了想要的addView()
的代碼了.
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
可以看出WindowManagerImpl
中的addView()
調(diào)用的是mGlobal(WindowManagerGlobal)
的addView()
.看來我們的代碼還沒結(jié)束...自己挖的坑,再深也要走下去,繼續(xù)跳轉(zhuǎn)到WindowManagerGlobal
的addView()
代碼里.看來這次是到達(dá)了真正的目的地了.
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);
}
}
}
// 標(biāo)注
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;
}
}
由于代碼有點(diǎn)長,按照慣例,我們只關(guān)注有用的部分.在上面代碼中,我發(fā)現(xiàn)了一個(gè)setView()
的函數(shù),相信這個(gè)函數(shù)就是我們想要的.那就看下這部分的代碼(從我在注釋中標(biāo)注的代碼開始).代碼中創(chuàng)建了一個(gè)名為root
的ViewRootImpl
對象并調(diào)用了root.setView()
.既然調(diào)用了函數(shù),那按照我的風(fēng)格怎么都要進(jìn)去看看,萬一有意外收獲呢.
下面就是ViewRootImpl
的setView()
的代碼(提示:下面代碼可以跳過):
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
if (mWindowAttributes.packageName == null) {
mWindowAttributes.packageName = mBasePackageName;
}
attrs = mWindowAttributes;
// Keep track of the actual window flags supplied by the client.
mClientWindowLayoutFlags = attrs.flags;
setAccessibilityFocus(null, null);
if (view instanceof RootViewSurfaceTaker) {
mSurfaceHolderCallback =
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
if (mSurfaceHolderCallback != null) {
mSurfaceHolder = new TakenSurfaceHolder();
mSurfaceHolder.setFormat(PixelFormat.UNKNOWN);
}
}
// Compute surface insets required to draw at specified Z value.
// TODO: Use real shadow insets for a constant max Z.
if (!attrs.hasManualSurfaceInsets) {
final int surfaceInset = (int) Math.ceil(view.getZ() * 2);
attrs.surfaceInsets.set(surfaceInset, surfaceInset, surfaceInset, surfaceInset);
}
CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
// If the application owns the surface, don't enable hardware acceleration
if (mSurfaceHolder == null) {
enableHardwareAcceleration(attrs);
}
boolean restore = false;
if (mTranslator != null) {
mSurface.setCompatibilityTranslator(mTranslator);
restore = true;
attrs.backup();
mTranslator.translateWindowLayout(attrs);
}
if (DEBUG_LAYOUT) Log.d(TAG, "WindowLayout in setView:" + attrs);
if (!compatibilityInfo.supportsScreen()) {
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
mLastInCompatMode = true;
}
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
mAttachInfo.mRootView = view;
mAttachInfo.mScalingRequired = mTranslator != null;
mAttachInfo.mApplicationScale =
mTranslator == null ? 1.0f : mTranslator.applicationScale;
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
}
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// 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.
// 標(biāo)注 , 我就是通過光明的入口
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
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 (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(mAttachInfo.mContentInsets);
}
mPendingOverscanInsets.set(0, 0, 0, 0);
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingStableInsets.set(mAttachInfo.mStableInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
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 this type already exists");
case WindowManagerGlobal.ADD_PERMISSION_DENIED:
throw new WindowManager.BadTokenException(
"Unable to add window " + mWindow +
" -- permission denied for this window 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 is not valid");
}
throw new RuntimeException(
"Unable to add window -- unknown error code " + res);
}
if (view instanceof RootViewSurfaceTaker) {
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
view.assignParent(this);
mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;
if (mAccessibilityManager.isEnabled()) {
mAccessibilityInteractionConnectionManager.ensureConnection();
}
if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
// Set up the input pipeline.
CharSequence counterSuffix = attrs.getTitle();
mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
mPendingInputEventQueueLengthCounterName = "aq:pending:" + counterSuffix;
}
}
}
Shit!(尼瑪),怎么感覺越陷越深.如果有這種感覺的同學(xué)那就對了,正所謂不如虎穴,焉得虎子.堅(jiān)持一下,光明就在前方.
在上面的一大串代碼中,我發(fā)現(xiàn)了一句非常熟悉的代碼requestLayout();
(我已在上面的代碼進(jìn)行了標(biāo)注),這不是View在需要重新布局的時(shí)候調(diào)用的函數(shù)嗎,怎么會(huì)在這里出現(xiàn).正所謂Everything for a reason
,我毫不猶豫的點(diǎn)了進(jìn)去.(各位同學(xué)肯定在想:還有完沒完...我只能說:相信我就跟我走下去,你會(huì)有所發(fā)現(xiàn)的).
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
這是停不下來的感覺啊,接著輪到scheduleTraversals()
,我把其中相關(guān)的都貼出來了.
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
這里的scheduleTraversals()
設(shè)置了回調(diào)mTraversalRunnable
,而在回調(diào)里調(diào)用了doTraversal()
.
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
接著來到最終的一個(gè)函數(shù)了,就是上面在doTraversal()
里調(diào)用的performTraversals()
.這個(gè)函數(shù)的代碼可不是一般的長,所以我也不再挑戰(zhàn)大家的極限了,我就直接給出performTraversals()
函數(shù)里的3個(gè)關(guān)鍵的語句.
private void performTraversals() {
.......
// 測量過程
performMeasure();
.......
// 布局過程
performLayout();
.......
// 繪制過程
performDraw();
.......
}
是不是有點(diǎn)熟悉的感覺,這就是View的3大流程,measure,layout,draw
,原來View的3大流程是在這里執(zhí)行的.到這里我們已經(jīng)簡略地走完了View的工作流程了.
總結(jié)
像上次一樣,最后還是用張圖總結(jié)本篇的內(nèi)容,這樣會(huì)使我們頭腦清晰一點(diǎn).
上圖的箭頭表示了函數(shù)之間的調(diào)用,其中的
CallBack:mTraversalRunnable
一處代表的是設(shè)置回調(diào)函數(shù).經(jīng)過本篇的學(xué)習(xí)我們發(fā)現(xiàn)了View的繪制是由
performTraversals()
函數(shù)發(fā)起的.下一篇開始我們就來研究研究繪制View的第一個(gè)流程Mesure
.