知方号

知方号

Android高级进阶之路一Android中View绘制流程浅析

Android高级进阶之路一Android中View绘制流程浅析

前言

一个View,从无到有会走三个流程,也就是老生常谈的measure,layout,draw三流程;

我们都知道Android视图是由一层一层构成的层级结构,直白点说,就是父View包含子View而子View又可以包含子View。所以绘制流程是由最外层的View开始,一步一步向内传递执行。而整个过程又是递归等待的,最外层的View需要等内层所有的View执行完绘制流程才结束,所以便有了”减少布局层级,可以有效提升App性能”这一经典总结。

正文

什么时候开始绘制?

而万物有始才有终,你不惹他,他也不会动手打你。View的绘制流程是什么时候开始的?谁触发的?明白这点后,才去考虑这个过程是怎样执行的。

我们都清楚Activity中onCreate()方法在setContentView()后,View的宽高是获取不到的。同时我们知道Activity在onResume()后才完全可见,并且初次在onResume()方法中也是拿不到View的尺寸的,这样可以推算得出:View的绘制流程是在onResume()方法执行结束后才开始的。那Activity的生命周期方法背后是由谁,又何时调用的?

答:ActivityManagerService 

ActivityManagerService(以下简称AMS))是Androids上层系统中最核心的服务之一,主要负责系统中四大组件的启动、切换、调度及应用程序的管理和调度等工作。具体详细内容参考以下地址:

https://blog.csdn.net/gaugamela/article/details/53067769

相对而言ActivityThread的main方法是应用程序的入口,main()方法里做一些初始化工作,其中包括和AMS建立起通信。

代码语言:txt复制public class ActivityThread{ final ApplicationThread mAppThread = new ApplicationThread(); final Looper mLooper = Looper.myLooper(); final H mH = new H(); final ArrayMap mActivities = new ArrayMap(); public static void main(String[] args) { //初始化lopper Looper.prepareMainLooper(); //初始化ActivityThread ActivityThread thread = new ActivityThread(); //ApplicationThread和AMS建立联系 thread.attach(false); //取消息 Looper.loop(); //loop()方法如果执行结束,未能取到消息,程序抛出异常退出。 throw new RuntimeException("Main thread loop unexpectedly exited"); }}

ActivityThread会管理和用户打交道的Activity,应用所有的Activity都存在ActivityThread中的mActivities集合中,而ActivityThread响应AMS的号召,需要借助ApplicationThread来接受这个诏令,点进去看全都是生命周期方法。接着调用attach()方法让ApplicationThread和AMS建立联系。H类就是一个Handler类,用于发送生命周期改变的消息,通知响应操作。

代码语言:txt复制private class ApplicationThread extends IApplicationThread.Stub { //通知相应的进程执行启动Activity的操作 public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state, PersistableBundle persistentState, List pendingResults, List pendingNewIntents, boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) { sendMessage(H.LAUNCH_ACTIVITY, r); } public final void scheduleResumeActivity(IBinder token, int processState, boolean isForward, Bundle resumeArgs) { sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0, 0, seq); } //.....}

对于AMS我也不太懂在这儿提一下明白是怎么回事就行,以后再慢慢研究。当Activity启动时会先调用到scheduleLaunchActivity()方法,由Handler发送通知消息后执行handleLaunchActivity()->performLaunchActivity()->callActivityOnCreate()->Activity.onCreate()。

代码语言:txt复制private class H extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); //该方法中会执行Activity的onCreate()方法。 handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; //onResume(); case RESUME_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume"); SomeArgs args = (SomeArgs) msg.obj; handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true, args.argi3, "RESUME_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; } }}final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ActivityClientRecord r = mActivities.get(token); //............. //执行onResume()方法 r = performResumeActivity(token, clearHide, reason); if (r != null) { final Activity a = r.activity; 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; if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; //将DecorView添加到Window上 wm.addView(decor, l); } } //说法二:执行makeVisible()来添加View,但也是添加到Window里和上面一样的操作。清楚的小伙伴可以告诉我下。 if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } }}

onResume()时也一样,当Activity的状态发生改变,经过层层调用执行到handleResumeActivity()方法,在方法中先调用Activity.onResume()方法,再执行WindowManager的addView()方法将Activity的根View(DecorView)添加上去,进而开始绘制流程。这就解释了为什么初次在onResume()方法中获取不到View的宽高。对DecorView不太明白的可以参考Activity中setContentView浅析。地址如下所示:

https://blog.csdn.net/sinat_35938012/article/details/81055380

而WindowManager实现类为WindowManagerImpl,WindowManagerImpl中addView()方法又会调用WindowManagerGlobal的addView()方法。参考如下:

https://www.jianshu.com/p/c223b993b1ec

代码语言:txt复制 public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ······ root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); //即将开始流程绘制 root.setView(view, wparams, panelParentView); ·······}

addView()方法中先创建ViewRootImpl对象,随后执行setView()方法将其和DecorView绑定起来,绘制流程也将由ViewRootImpl()来执行。setView()方法中会执行requestLayout()方法。

代码语言:txt复制public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { if (mView == null) { mView = view; // 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. requestLayout(); }}

requestLayout()方法走下去会异步执行performTraversals()方法,View的三大流程都是在该方法中执行的。到这儿我们算是明白View的绘制流程是从哪儿开始的,接下来分析这个过程到底是怎么做的。

代码语言:txt复制private void performTraversals() { //计算DecorView根View的MeasureSpec int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); performLayout(lp, mWidth, mHeight); performDraw();}measure流程

说到measure流程就不得提到一个类,MeausreSpec。使用该类用一个int值就能记录View测量的宽高和宽高的测量模式,大大节约开销。

代码语言:txt复制public static class MeasureSpec { private static final int MODE_SHIFT = 30; //int类型占4个字节,1个字节=8bit(位)。 private static final int MODE_MASK = 0x3

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至lizi9903@foxmail.com举报,一经查实,本站将立刻删除。