Android View第一次绘制
Posted danfengw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android View第一次绘制相关的知识,希望对你有一定的参考价值。
疑问
1 android 走生命周期的时候,View究竟是在哪个生命周期进行测量布局绘制的呢?
2 在该生命周期中通知View的绘制流程是怎样的?
解答
1 Android的resume周期开始绘制的,这也是我们为什么在onResume的时候获取不到view的狂高
2 下面来看下具体流程(view的绘制是通过ViewRootImp的scheduleTraversals方法,因此看下怎么从ActivityThread找到ViewRootImp的scheduleTraversals的调用的)
直接来看ActivityThread.java 中的handleResumeActivity中的处理,代码如下
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 (r.mPreserveWindow)
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null)
impl.notifyChildRebuilt();
if (a.mVisibleFromClient)
if (!a.mWindowAdded)
a.mWindowAdded = true;
//wm被addView
wm.addView(decor, l);
else
a.onWindowAttributesChanged(l);
else if (!willBeVisible)
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
上面的wm是ViewManager的getWindow(),而Activity的getWindowManager()最终返回的对象是WindowManager
public WindowManager getWindowManager()
return mWindowManager;
再来看Activity的mWindowManager 是何时创建的呢?请看下面attach方法中的代码,setWindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null)
mWindow.setContainer(mParent.getWindow());
mWindowManager = mWindow.getWindowManager();
这时点击到setWindowManager方法(如下)查看发现,mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
,至此也就是addView方法最终的调用者,实际是WindowManagerImpl对象,因此我们需要去查看WindowManagerImpl的addView方法做了什么操作
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated)
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated;
if (wm == null)
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
现在来看WindowManagerImpl的addView方法做了什么
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params)
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
WindowManagerImpl最终通过mGlobal对象进行的addView,而mGlobal是一个WindowManagerGlobal对象,WindowManagerGlobal中的addView方法中有如下代码,
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
//ViewRootImpl的setView方法
root.setView(view, wparams, panelParentView, userId);
catch (RuntimeException e)
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0)
removeViewLocked(index, true);
throw e;
ViewRootImpl的setView方法中调用了requestLayout();
,与view.assignParent(this);
,关于assignParent()需要关联其他代码,为了避免思路有分歧,这里不介绍它。继续看下面的requestLayout
@Override
public void requestLayout()
if (!mHandlingLayoutInLayoutRequest)
checkThread();
mLayoutRequested = true;
scheduleTraversals();
这里requestLayout 调用到scheduleTraversals()方法,scheduleTraversals方法中通过postSyncBarrier来插入一个消息屏障,其中又通过Choreographer对象发送mTraversalRunnable,
@UnsupportedAppUsage
void scheduleTraversals()
if (!mTraversalScheduled)
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
来看看看TraversalRunnable被调用的时候做了什么?调用了doTraversal()
final class TraversalRunnable implements Runnable
@Override
public void run()
doTraversal();
void doTraversal()
if (mTraversalScheduled)
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile)
Debug.startMethodTracing("ViewAncestor");
performTraversals();
if (mProfile)
Debug.stopMethodTracing();
mProfile = false;
之后在中可以看到调用了performLayout(lp, mWidth, mHeight);
从而进一步调用如下代码:
measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight);
mInLayout = true;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
通过上面的measureHierarchy和host.layout来进行后续的测量与布局。
小结
ActivityThread—》handleResumeActivity—》addView–》WindowManagerImp—》WindowManagerGlobal–》addView—》ViewRootImpl—》setView—》requestLayout()–》scheduleTraversals()–》doTraversal()----》performTraversals()—》测量与布局
流程经过的类:ActivityThread、WindowManagerImp、WindowManagerGlobal、ViewRootImpl
以上是关于Android View第一次绘制的主要内容,如果未能解决你的问题,请参考以下文章