Android View绘制流程源码浅析
来源:互联网 发布:数据库分区和分表 编辑:程序博客网 时间:2024/06/05 03:23
Android中View的绘制是一个面试的必答题,网上他人的博文也很多,本文旨在分析出大致流程。
废话不说,read the fucking source code!
先从ActivityThread主线程启动Activity说起,当Activity初始化 Window和将布局添加到
PhoneWindow的内部类DecorView类之后,ActivityThread类会调用handleResumeActivity方法将顶层视图DecorView添加到PhoneWindow窗口,来看看handlerResumeActivity方法的实现:
2974 final void handleResumeActivity(IBinder token,2975 boolean clearHide, boolean isForward, boolean reallyResume) {2976 // If we are getting ready to gc after going to the background, well2977 // we are back active so skip it.2978 unscheduleGcIdler();2979 mSomeActivitiesChanged = true;29802981 // TODO Push resumeArgs into the activity for consideration2982 ActivityClientRecord r = performResumeActivity(token, clearHide);29832984 if (r != null) {2985 final Activity a = r.activity;29862987 if (localLOGV) Slog.v(2988 TAG, "Resume " + r + " started activity: " +2989 a.mStartedActivity + ", hideForNow: " + r.hideForNow2990 + ", finished: " + a.mFinished);29912992 final int forwardBit = isForward ?2993 WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;29942995 // If the window hasn't yet been added to the window manager,2996 // and this guy didn't finish itself or start another activity,2997 // then go ahead and add the window.2998 boolean willBeVisible = !a.mStartedActivity;2999 if (!willBeVisible) {3000 try {3001 willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(3002 a.getActivityToken());3003 } catch (RemoteException e) {3004 }3005 }3006 if (r.window == null && !a.mFinished && willBeVisible) {3007 r.window = r.activity.getWindow();3008 View decor = r.window.getDecorView();3009 decor.setVisibility(View.INVISIBLE);3010 ViewManager wm = a.getWindowManager();3011 WindowManager.LayoutParams l = r.window.getAttributes();3012 a.mDecor = decor;3013 l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;3014 l.softInputMode |= forwardBit;3015 if (a.mVisibleFromClient) {3016 a.mWindowAdded = true;3017 wm.addView(decor, l);3018 }30193020 // If the window has already been added, but during resume3021 // we started another activity, then don't yet make the3022 // window visible.3023 } else if (!willBeVisible) {3024 if (localLOGV) Slog.v(3025 TAG, "Launch " + r + " mStartedActivity set");3026 r.hideForNow = true;3027 }30283029 // Get rid of anything left hanging around.3030 cleanUpPendingRemoveWindows(r);30313032 // The window is now visible if it has been added, we are not3033 // simply finishing, and we are not starting another activity.3034 if (!r.activity.mFinished && willBeVisible3035 && r.activity.mDecor != null && !r.hideForNow) {3036 if (r.newConfig != null) {3037 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "3038 + r.activityInfo.name + " with newConfig " + r.newConfig);3039 performConfigurationChanged(r.activity, r.newConfig);3040 freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.newConfig));3041 r.newConfig = null;3042 }3043 if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="3044 + isForward);3045 WindowManager.LayoutParams l = r.window.getAttributes();3046 if ((l.softInputMode3047 & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)3048 != forwardBit) {3049 l.softInputMode = (l.softInputMode3050 & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))3051 | forwardBit;3052 if (r.activity.mVisibleFromClient) {3053 ViewManager wm = a.getWindowManager();3054 View decor = r.window.getDecorView();3055 wm.updateViewLayout(decor, l);3056 }3057 }3058 r.activity.mVisibleFromServer = true;3059 mNumVisibleActivities++;3060 if (r.activity.mVisibleFromClient) {3061 r.activity.makeVisible();3062 }3063 }30643065 if (!r.onlyLocalRequest) {3066 r.nextIdle = mNewActivities;3067 mNewActivities = r;3068 if (localLOGV) Slog.v(3069 TAG, "Scheduling idle handler for " + r);3070 Looper.myQueue().addIdleHandler(new Idler());3071 }3072 r.onlyLocalRequest = false;30733074 // Tell the activity manager we have resumed.3075 if (reallyResume) {3076 try {3077 ActivityManagerNative.getDefault().activityResumed(token);3078 } catch (RemoteException ex) {3079 }3080 }30813082 } else {3083 // If an exception was thrown when trying to resume, then3084 // just end this activity.3085 try {3086 ActivityManagerNative.getDefault()3087 .finishActivity(token, Activity.RESULT_CANCELED, null, false);3088 } catch (RemoteException ex) {3089 }3090 }3091 }
接着看82行也就是源码3055行(基于android5.0) wm.addView(decor, l);
ViewManager wm= a.getWindowManager();这里有个向上转型,wm的具体对象Activity的WindowManagerImpl对象
接着看 WindowManagerImpl的addView方法
@Override public void addView(View view, ViewGroup.LayoutParams params) { mGlobal.addView(view, params, mDisplay, mParentWindow); }
这里的mGlobal是个单实例的WindowManagerGlobal类。进入WindowManagerGlobal类看addView()方法。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ............ ViewRootImpl root; View panelParentView = null; ............ //获得ViewRootImpl对象root root = new ViewRootImpl(view.getContext(), display); ........... // do this last because it fires off messages to start doing things try { //将传进来的参数DecorView设置到root中 root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { ........... } }
创建了一个ViewRootImpl对象root,然后调用ViewRootImpl类中的setView成员方法()。继续跟踪代码进入ViewRootImpl类
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { //将顶层视图DecorView赋值给全局的mView mView = view; ............. //标记已添加DecorView mAdded = true; ............. requestLayout(); ............. } }接着看ViewRootImpl的内部方法调用
@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); try { performTraversals(); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } } }............
最后DecorView的绘制会进入到ViewRootImpl类中的performTraversals()成员方法。
现在我们主要来分析下 ViewRootImpl类中的performTraversals()方法
private void performTraversals() { // cache mView since it is used so much below... //mView就是DecorView根布局 final View host = mView; //mAdded赋值为true,因此条件不成立 if (host == null || !mAdded) return; //是否正在遍历 mIsInTraversal = true; //是否马上绘制View mWillDrawSoon = true; ............. //顶层视图DecorView所需要窗口的宽度和高度 int desiredWindowWidth; int desiredWindowHeight; ..................... //在构造方法中mFirst已经设置为true,表示是否是第一次绘制DecorView if (mFirst) { mFullRedrawNeeded = true; mLayoutRequested = true; //如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要窗口的宽度和高度就是除了状态栏 if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL || lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) { // NOTE -- system code, won't try to do compat mode. Point size = new Point(); mDisplay.getRealSize(size); desiredWindowWidth = size.x; desiredWindowHeight = size.y; } else {//否则顶层视图DecorView所需要窗口的宽度和高度就是整个屏幕的宽高 DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics(); desiredWindowWidth = packageMetrics.widthPixels; desiredWindowHeight = packageMetrics.heightPixels; } }............//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); // Ask host how big it wants to be //执行测量操作 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);........................//执行布局操作 performLayout(lp, desiredWindowWidth, desiredWindowHeight);.......................//执行绘制操作performDraw();}
先看ViewRootImpl#performMeasure
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); } }
ViewRootImpl的mView就是DecorView对象
回过头看DecorView的两个参数怎么来的
ViewRootImpl#getRootMeasureSpec
private static int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
继续回溯第二个参数在performTraversals函数中找到
WindowManager.LayoutParams lp = mWindowAttributes;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; ......
再向上找setView在handleResumeActivity中被调用,参数l来源
WindowManager.LayoutParams l = r.window.getAttributes();
继续挖,Window#getAttributes
public final WindowManager.LayoutParams getAttributes() { return mWindowAttributes; }<pre name="code" class="java"> // The current window attributes. private final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();好吧,有个默认的值,答案就要解开了
android.view.WindowManager
public LayoutParams() { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; format = PixelFormat.OPAQUE; }到此为止,终于解开了getRootMeasureSpec传入的第二个参数,默认为MATCH_PARENT。getRootMeasureSpec传入参数后这个函数走的是MATCH_PARENT,使用MeasureSpec.makeMeasureSpec方法组装一个MeasureSpec,MeasureSpec的specMode等于EXACTLY,specSize等于windowSize,也就是为何根视图总是全屏的原因。
0 0
- Android View绘制流程源码浅析
- Android-View绘制流程浅析
- Android View绘制流程与源码解析
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- android View 绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- Android线程之间的通讯机制Handler
- hdu2150 Pipe (判断线段是否相交)
- android学习笔记-接口回调简析
- Web前端开发之定义全局的上下文路径
- [TwistedFate]初级数据持久化
- Android View绘制流程源码浅析
- Course(水题)
- 【springmvc+mybatis项目实战】杰信商贸-32.出口报运修改+报运货物批量展现-批量修改控件1
- 学习路上之初识MongoDB
- 【springmvc+mybatis项目实战】杰信商贸-33.出口报运修改+报运货物批量展现-批量修改控件2
- having 和group by
- CCSpriteBatchNode 是批处理节点
- 银行家算法的实现
- object_recognition