前言
前一段時間寫過兩篇關于View的文章 Activity中的Window的setContentView 和 遇見LayoutInflater&Factory 。分析了Activity設置頁面布局到頁面View元素進行布局到底經歷了一個怎么樣的過程?
- Activity的attach中生成PhoneWindow對象;
- setContentView中初始化DecorView(ViewGroup);
- 在LayoutInflater進行對布局文件的解析之后更加解析的數據
- 根據解析出的數據執行View的構造函數進行View的構造,同時生成ViewTree。
為什么接下來繼續寫這篇文章呢?是因為我在掘金上看到一篇子線程更新View的文章之后,發現自己對View還不是很了,以這個問題為方向看了View相關的源碼。發現網絡上有些文章對于ViewRootImpl的分析還是有些問題或者疑惑的,所以自己整理過的知識點分享給大家,希望能對大家有幫助。(源碼cm12.1
)
View的介紹
最開始學習View的時候最先分析的是它的布局(LinearLayout、FrameLayout、TableLayout、RelativeLayout、AbsoluteLayout),然后是它的三大方法(measure、layout、draw)。
繪制&加載View-----onMeasure()
- MeasureSpec.EXACTLY是精確尺寸, 當我們將控件的layout_width或layout_height指定為具體數值時如andorid:layout_width="50dip",或者為FILL_PARENT是,都是控件大小已經確定的情況,都是精確尺寸。
- MeasureSpec.AT_MOST是最大尺寸,當控件的layout_width或layout_height指定為WRAP_CONTENT時 ,控件大小一般隨著控件的子空間或內容進行變化,此時控件尺寸只要不超過父控件允許的最大尺寸即可。因此,此時的mode是AT_MOST,size給出了父控件允許的最大尺寸。
- MeasureSpec.UNSPECIFIED是未指定尺寸,這種情況不多,一般都是父控件是AdapterView,通過measure方法傳入的模式。
ViewGroup.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec)
protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec)
protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed)
繪制&加載View-----onLayout()
- onLayout方法:是ViewGroup中子View的布局方法。放置子View很簡單,只需在重寫onLayout方法,然后獲取子View的實例,調用子View的layout方法實現布局。在實際開發中,一般要配合onMeasure測量方法一起使用。View的放置都是根據一個矩形空間放置的。
- layout方法:是View的放置方法,在View類實現。調用該方法需要傳入放置View的矩形空間左上角left、top值和右下角right、bottom值。
繪制&加載View-----onDraw()
public void draw(Canvas canvas)
protected void onDraw(Canvas canvas)
protected void dispatchDraw(Canvas canvas)(View,ViewGroup)
protected boolean drawChild(Canvas canvas, View child, long drawingTime) (ViewGroup)
View的解析與生成
View的解析和生成之前在下邊的這兩篇文章中已經講述
View如何在頁面進行展示的,View樹是如何生成的。
Activity中的Window的setContentView
View對象的生成,屬性值的初始化。
遇見LayoutInflater&Factory
在這兩篇文章中用到了一些Android中相關的類:
- Activity:一個Activity是一個應用程序組件,提供一個屏幕,用戶可以用來交互為了完成某項任務,例如撥號、拍照、發送email……。
- View:作為所有圖形的基類。
- ViewGroup:對View繼承擴展為視圖容器類。
- Window:它概括了Android窗口的基本屬性和基本功能。(抽象類)
- PhoneWindow:Window的子類。
- DecorView:界面的根View,PhoneWindow的內部類。
- ViewRootImpl:ViewRoot是GUI管理系統與GUI呈現系統之間的橋梁。
- WindowManangerService:簡稱WMS,它的作用是管理所有應用程序中的窗口,并用于管理用戶與這些窗口發生的的各種交互。
ViewRootImpl簡介
The top of a view hierarchy, implementing the needed protocol between View and the WindowManager. This is for the most part an internal implementation detail of {@link WindowManagerGlobal}.
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代碼省略**********/
}
ViewRootImpl是View中的最高層級,屬于所有View的根(但ViewRootImpl不是View,只是實現了ViewParent接口
),實現了View和WindowManager之間的通信協議,實現的具體細節在WindowManagerGlobal這個類當中。
public interface ViewManager{
//添加View
public void addView(View view, ViewGroup.LayoutParams params);
//更新View
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
//移除View
public void removeView(View view);
}
上面提到了WindowManager
對View
的添加、更新、刪除操作,那么聯系這兩者之間的Window
呢?
Activity中的Window的setContentView 閱讀這篇文章我們知道Activity中有Window對象,一個Window對象對應著一個View(
DecorView
),ViewRootImpl
就是對這個View進行操作的。
我們知道界面所有的元素都是有View構成的,界面上的每一個像素點也都是由View繪制的。Window只是一個抽象的概念,把界面抽象為一個窗口對象,也可以抽象為一個View。
ViewRootImpl與其他類之間的關系
在了解ViewRootImpl與其他類的關系之前,我們先看一副圖和一段代碼:
public final class WindowManagerGlobal {
/*******部分代碼省略**********/
//所有Window對象中的View
private final ArrayList<View> mViews = new ArrayList<View>();
//所有Window對象中的View所對應的ViewRootImpl
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//所有Window對象中的View所對應的布局參數
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();
/*******部分代碼省略**********/
}
WindowManagerGlobal
在其內部存儲著ViewRootImpl
和View
實例的映射關系(順序存儲)。WindowManager
繼承與ViewManger
,從ViewManager
這個類名來看就是用來對View
類進行管理的,從ViewManager
接口中的添加、更新、刪除View的方法也可以看出來WindowManager
對View
的管理。
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, 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);
}
/*******部分代碼省略**********/
}
-
WindowManagerImpl
為WindowManager
的實現類。WindowManagerImpl
內部方法實現都是由代理類WindowManagerGlobal
完成,而WindowManagerGlobal
是一個單例,也就是一個進程中只有一個WindowManagerGlobal
對象服務于所有頁面的View。
ViewRootImpl的初始化
在Activity的onResume之后,當前Activity的Window對象中的View會被添加在WindowManager中。
public final class ActivityThread {
/*******部分代碼省略**********/
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
/*******部分代碼省略**********/
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
/*******部分代碼省略**********/
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;
//window的類型:一個應用窗口類型(所有的應用窗口類型都展現在最頂部)。
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//將decor添加在WindowManager中
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;
}
/*******部分代碼省略**********/
} 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) {
}
}
}
}
創建Window所對應的ViewRootImpl,并將Window所對應的View、ViewRootImpl、LayoutParams順序添加在WindowManager中。
public final class WindowManagerGlobal {
/*******部分代碼省略**********/
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 and we're running on L or above (or in the
// system context), assume we want hardware acceleration.
final Context context = view.getContext();
if (context != null
&& context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
//聲明ViwRootImpl
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);
}
}
}
//創建ViwRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//將Window所對應的View、ViewRootImpl、LayoutParams順序添加在WindowManager中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
//把將Window所對應的View設置給創建的ViewRootImpl
//通過ViewRootImpl來更新界面并完成Window的添加過程。
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;
}
}
}
ViewRootImpl綁定Window所對應的View
ViewRootImpl綁定Window所對應的View,并對該View進行測量、布局、繪制等。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代碼省略**********/
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
//ViewRootImpl成員變量view進行復制,以后操作的都是mView。
mView = view;
/*******部分代碼省略**********/
//Window在添加完之前先進行一次布局,確保以后能再接受系統其它事件之后重新布局。
//對View完成異步刷新,執行View的繪制方法。
requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//將該Window添加到屏幕。
//mWindowSession實現了IWindowSession接口,它是Session的客戶端Binder對象.
//addToDisplay是一次AIDL的跨進程通信,通知WindowManagerService添加IWindow
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 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();
}
}
/*******部分代碼省略**********/
//設置當前View的mParent
view.assignParent(this);
/*******部分代碼省略**********/
}
}
}
}
ViewRootImpl對mView進行操作
對View的操作包括文章最開始講述的測量、布局、繪制,其過程主要是在ViewRootImpl的performTraversals方法中。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代碼省略**********/
//請求對界面進行布局
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
//校驗所在線程,mThread是在ViewRootImpl初始化的時候執行mThread = Thread.currentThread()進行賦值的,也就是初始化ViewRootImpl所在的線程。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
//安排任務
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
//做任務
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;
}
}
}
//執行任務(分別調用mView的measure、layout、draw)
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
/*******部分代碼省略**********/
//想要展示窗口的寬高
int desiredWindowWidth;
int desiredWindowHeight;
//開始進行布局準備
if (mFirst || windowShouldResize || insetsChanged ||
viewVisibilityChanged || params != null) {
/*******部分代碼省略**********/
if (!mStopped) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
/*******部分代碼省略**********/
if (measureAgain) {
//View的測量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
/*******部分代碼省略**********/
}
final boolean didLayout = layoutRequested /*&& !mStopped*/ ;
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
//View的布局
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
/*******部分代碼省略**********/
}
/*******部分代碼省略**********/
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
/*******部分代碼省略**********/
//View的繪制
performDraw();
}
} else {
if (viewVisibility == View.VISIBLE) {
// 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;
}
}
View的測量
ViewRootImpl調用performMeasure執行Window對應的View的測量。
- ViewRootImpl的performMeasure;
- DecorView(FrameLayout)的measure;
- DecorView(FrameLayout)的onMeasure;
- DecorView(FrameLayout)所有子View的measure;
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代碼省略**********/
//View的測量
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//mView在Activity中為DecorView(FrameLayout)
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
View的布局
ViewRootImpl調用performLayout執行Window對應的View的布局。
- ViewRootImpl的performLayout;
- DecorView(FrameLayout)的layout方法;
- DecorView(FrameLayout)的onLayout方法;
- DecorView(FrameLayout)的layoutChildren方法;
- DecorView(FrameLayout)的所有子View的Layout;
在這期間可能View會自己觸發布布局請求,所以在此過程會在此調用ViewRootImpl的requestLayout重新進行測量、布局、繪制。
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代碼省略**********/
//請求對ViewRootImpl進行布局
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
//View的布局
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
/*******部分代碼省略**********/
final View host = mView;
/*******部分代碼省略**********/
try {
//調用View的Layout方法進行布局
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
//在ViewRootImpl進行布局的期間,Window內的View自己進行requestLayout
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
// requestLayout() was called during layout.
// If no layout-request flags are set on the requesting views, there is no problem.
// If some requests are still pending, then we need to clear those flags and do
// a full request/measure/layout pass to handle this situation.
ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
false);
if (validLayoutRequesters != null) {
// Set this flag to indicate that any further requests are happening during
// the second pass, which may result in posting those requests to the next
// frame instead
mHandlingLayoutInLayoutRequest = true;
// Process fresh layout requests, then measure and layout
int numValidRequests = validLayoutRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = validLayoutRequesters.get(i);
Log.w("View", "requestLayout() improperly called by " + view +
" during layout: running second layout pass");
//請求對該View布局,最終回調到ViewRootImpl的requestLayout進行重新測量、布局、繪制
view.requestLayout();
}
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mHandlingLayoutInLayoutRequest = false;
// Check the valid requests again, this time without checking/clearing the
// layout flags, since requests happening during the second pass get noop'd
validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);
if (validLayoutRequesters != null) {
final ArrayList<View> finalRequesters = validLayoutRequesters;
// Post second-pass requests to the next frame
getRunQueue().post(new Runnable() {
@Override
public void run() {
int numValidRequests = finalRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = finalRequesters.get(i);
/*******部分代碼省略**********/
//請求對該View布局,最終回調到ViewRootImpl的requestLayout進行重新測量、布局、繪制
view.requestLayout();
}
}
});
}
}
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
}
View的繪制
ViewRootImpl調用performDraw執行Window對應的View的布局。
- ViewRootImpl的performDraw;
- ViewRootImpl的draw;
- ViewRootImpl的drawSoftware;
- DecorView(FrameLayout)的draw方法;
- DecorView(FrameLayout)的dispatchDraw方法;
- DecorView(FrameLayout)的drawChild方法;
- DecorView(FrameLayout)的所有子View的draw方法;
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
/*******部分代碼省略**********/
//View的繪制
private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
}
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
/*******部分代碼省略**********/
}
//進行繪制
private void draw(boolean fullRedrawNeeded) {
/*******部分代碼省略**********/
//View上添加的Observer進行繪制事件的分發
mAttachInfo.mTreeObserver.dispatchOnDraw();
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
/*******部分代碼省略**********/
//調用Window對應的ViewRootImpl的invalidate方法
mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, this);
} else {
/*******部分代碼省略**********/
//繪制Window
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
if (animating) {
mFullRedrawNeeded = true;
scheduleTraversals();
}
}
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
scheduleTraversals();
}
}
/**
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
/*******部分代碼省略**********/
try {
/*******部分代碼省略**********/
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
//View繪制
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
/*******部分代碼省略**********/
}
return true;
}
}
異步線程更新視圖
異步線程中到底是否能對視圖進行更新呢?我們先看看TextView.setText
方法的代碼執行流程圖。
從上圖看在頁面進行視圖更新的時候會觸發 checkThread
,校驗當前線程是否是 ViewRootImpl
被創建時所在的線程。而 ViewRootImpl
的創建是在 Activity
的 onResume
生命周期之后。
需要注意的是不是所有的 TextView.setText
都會觸發 checkThread
。 比如 TextView.setText
之后對當前 TextView
的 layout
不會進行任何改變,那么這次的 TextView.setText
就可以在異步線程執行。
操作UI的時候只有這些方法被調用的時候才會執行 ViewRootImpl.checkThread
,進行任務執行線程的校驗。
//異常信息,提示當前執行任務的線程與創建ViewRootImpl的線程不一樣~!
new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
異步線程執行UI操作
我們知道了異步線程不建議操作UI的最終原因是:
發生了對任務執行線程的校驗,而且當前執行任務的線程與創建
ViewRootImpl
的線程不一樣;
所以只要避免上面的執行邏輯,還是可以在異步線程操作UI的。
- 我們在不觸發
ViewRootImpl.checkThread
的情況下再異步線程執行UI操作。 - 我們在
Activity.onResume
之前在異步線程進行視圖更新,因為這個時候不會發生線程校驗。(PS:我們知道Activity
所綁定的ViewRootImpl
的初始化是在主線程中,所以我們一般不會在非主線程進行UI操作
。) - 我們可以再異步線程初始化
ViewRootImpl
同時在該線程進行視圖更新(eg:Dialog
異步線程的展現,具體參考:Dialog、Toast的Window和ViewRootImpl)。
PS:我們學習Android源碼并通過了解其內部實現,我們可以通過技術手段去掉、增加或修改部分代碼邏輯。以期望能做出更好的產品、做更細節的優化。但是想這種界面繪制只能發生在主線程規則我們還是必須要遵守的。兩個線程不能同時draw,否則屏幕會花;不能同時寫一塊內存,否則內存會花;不能同時寫一份文件,否則文件會花。同一時刻只有一個線程可以做ui,那么當兩個線程互斥幾率較大時,或者保證互斥的代碼復雜時,選擇其中一個長期持有其他發消息就是典型的解決方案。所以普遍的要求ui只能單線程。
Activity、Dialog、Toast的ViewRootImpl的不同
文章前面內容都是站在Activity的角度來進行代碼解析的,因此我們不再對Dialog和Toast與Activity做具體分析,主要來看看它們與Activity有什么不同之處。詳見:Dialog、Toast的Window和ViewRootImpl。
總結
通過對ViewRootImpl的更細節的分析,我們再看自定義View的布局時的一些方法會更加清楚(知其然且知其所以然)。同時也解開了Android中異步線程更新View
的謎底。
文章到這里就全部講述完啦,若有其他需要交流的可以留言哦~!~!