用两张图告诉你,为什么你的App会卡顿?

Cover

有什么料?

从这篇文章中你能获得这些料:

  • 知道setContentView()之后发生了什么?
  • 知道Android究竟是如何在屏幕上显示我们期望的画面的?
  • 对Android的视图架构有整体把握。
  • 学会从根源处分析画面卡顿的原因。
  • 掌握如何编写一个流畅的App的技巧。
  • 从源码中学习Android的细想。
  • 收获两张自制图,帮助你理解Android的视图架构。

link

从setContentView()说起

public class AnalyzeViewFrameworkActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_analyze_view_framwork);}
}

上面这段代码想必Androider们大都已经不能再熟悉的更多了。但是你知道这样写了之后发生什么了吗?这个布局到底被添加到哪了?我的天,知识点来了!

可能很多同学也知道这个布局是被放到了一个叫做DecorView的父布局里,但是我还是要再说一遍。且看下图✌️

image

这个图可能和伙伴们在书上或者网上常见的不太一样,为什么不太一样呢?因为是我自己画的,哈哈哈...

下面就来看着图捋一捋Android最基本的视图框架。

PhoneWindow

估计很多同学都知道,每一个Activity都拥有一个Window对象的实例。这个实例实际是PhoneWindow类型的。那么PhoneWindow从名字很容易看出,它应该是Window的儿子(即子类)!

知识点:每一个Activity都有一个PhoneWindow对象。

那么,PhoneWindow有什么用呢?它在Activity充当什么角色呢?下面我就姑且把PhoneWindow等同于Window来称呼吧。

image

Window从字面看它是一个窗口,意思和PC上的窗口概念有点像。但也不是那么准确。看图说。可以看到,我们要显示的布局是被放到它的属性mDecor中的,这个mDecor就是DecorView的一个实例。下面会专门撸DecorView,现在先把关注点放到Window上。Window还有一个比较重要的属性mWindowManager,它是WindowManager(这是个接口)的一个实现类的一个实例。我们平时通过getWindowManager()方法获得的东西就是这个mWindowManager。顾名思义,它是Window的管理者,负责管理着窗口及其中显示的内容。它的实际实现类是WindowManagerImpl。可能童鞋们现在正在PhoneWindow中寻找着这个mWindowManager是在哪里实例化的,是不是上下来回滚动着这个类都找不见?STOP!mWindowManager是在它爹那里就实例化好的。下面代码是在Window.java中的。

public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {...if (wm == null) {wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);//获取了一个WindowManager}mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);//通过这里我们可以知道,上面获取到的wm实际是WindowManagerImpl类型的。}

通过上面的介绍,我们已经知道了Window中有负责承载布局的DecorView,有负责管理的WindowManager(事实上它只是个代理,后面会讲它代理的是谁)。

DecorView

前面提到过,在Activity的onCreate()中通过setContentView()设置的布局实际是被放到DecorView中的。我们在图中找到DecorView。

从图中可以看到,DecorView继承了FrameLayout,并且一般情况下,它会在先添加一个预设的布局。比如DecorCaptionView,它是从上到下放置自己的子布局的,相当于一个LinearLayout。通常它会有一个标题栏,然后有一个容纳内容的mContentRoot,这个布局的类型视情况而定。我们希望显示的布局就是放到了mContentRoot中。

知识点:通过setContentView()设置的布局是被放到DecorView中,DecorView是视图树的最顶层。

WindowManager

前面已经提到过,WindowManager在Window中具有很重要的作用。我们先在图中找到它。这里需要先说明一点,在PhoneWindow中的mWindowManager实际是WindowManagerImpl类型的。WindowManagerImpl自然就是接口WindowManager的一个实现类喽。这一点是我没有在图中反映的。

WindowManager是在Activity执行attach()时被创建的,attach()方法是在onCreate()之前被调用的。关于Activity的创建可以看看我的这篇:【可能是史上最简单的!一张图3分钟让你明白Activity启动流程,不看后悔!http://www.jianshu.com/p/9ecea420eb52】
Activity.java

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,Window window){...mWindow = new PhoneWindow(this, window);//创建Window...mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//注意!这里就是在创建WindowManager。//这个方法在前面已经说过了。if (mParent != null) {mWindow.setContainer(mParent.getWindow());}mWindowManager = mWindow.getWindowManager();}

继续看图。WindowManagerImpl持有了PhoneWindow的引用,因此它可以对PhoneWindow进行管理。同时它还持有一个非常重要的引用mGlobal。这个mGlobal指向一个WindowManagerGlobal类型的单例对象,这个单例每个应用程序只有唯一的一个。在图中,我说明了WindowManagerGlobal维护了本应用程序内所有Window的DecorView,以及与每一个DecorView对应关联的ViewRootImpl。这也就是为什么我前面提到过,WindowManager只是一个代理,实际的管理功能是通过WindowManagerGlobal实现的。我们来看个源码的例子就比较清晰了。开始啦!

image

WimdowManagerImpl.java

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {...mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//实际是通过WindowManagerGlobal实现的。
}

从上面的代码可以看出,WindowManagerImpl确实只是WindowManagerGlobal的一个代理而已。同时,上面这个方法在整个Android的视图框架流程中十分的重要。我们知道,在Activity执行onResume()后界面就要开始渲染了。原因是在onResume()时,会调用WindowManager的addView()方法(实际最后调用的是WindowManagerGlobal的addView()方法),把视图添加到窗口上。结合我的这篇【可能是史上最简单的!一张图3分钟让你明白Activity启动流程,不看后悔!http://www.jianshu.com/p/9ecea420eb52】看,可以帮助你更好的理解Android的视图框架。
ActivityThread.java

final void handleResumeActivity(IBinder token,boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {...ViewManager wm = a.getWindowManager();//获得WindowManager,实际是WindowManagerImpl...wm.addView(decor, l);//添加视图...wm.updateViewLayout(decor, l);//需要刷新的时候会走这里...
}

从上面可以看到,当Activity执行onResume()的时候就会添加视图,或者刷新视图。需要解释一点:WindowManager实现了ViewManager接口。

如图中所说,WindowManagerGlobal调用addView()的时候会把DecorView添加到它维护的数组中去,并且会创建另一个关键且极其重要的ViewRootImpl(这个必须要专门讲一下)类型的对象,并且也会把它存到一个数组中维护。
WindowManagerGlobal.java

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);//设置必要属性view是DecorView,panelParentView是PhoneWindow...
}

可以看出ViewRootImpl是在Activity执行onResume()的时候才被创建的,并且此时才把DecorView传进去让它管理。

知识点:WindowManager是在onCreate()时被创建。它对窗口的管理能力实际是通过WindowManagerGlobal实现的。在onResume()是视图才通过WindowManager被添加到窗口上。

ViewRootImpl

ViewRootImpl能够和系统的WindowManagerService进行交互,并且管理着DecorView的绘制和窗口状态。非常的重要。赶紧在图中找到对应位置吧!

ViewRootImpl并不是一个View,而是负责管理视图的。它配合系统来完成对一个Window内的视图树的管理。从图中也可以看到,它持有了DecorView的引用,并且视图树它是视图树绘制的起点。因此,ViewRootImpl会稍微复杂一点,需要我们更深入的去了解,在图中我标出了它比较重要的组成Surface和Choreographer等都会在后面提到。

到此,我们已经一起把第一张图撸了一遍了,现在童鞋们因该对Android视图框架有了大致的了解。下面将更进一步的去了解Android的绘制机制。

App总是卡顿到底是什么原因?

下面将会详细的讲解为什么我们设置的视图能够被绘制到屏幕上?这中间究竟隐藏着怎样的离奇?看完之后,你自然就能够从根源知道为什么你的App会那么卡,以及开始有思路着手解决这些卡顿。

image

同样用一张图来展示这个过程。由于Android绘制机制确实有点复杂,所以第一眼看到的时候你的内心中可能蹦腾了一万只草泥马

Android 应用性能优化(5)---用两张图告诉你,为什么你的App会卡顿?相关推荐

  1. 用两张图告诉你,为什么你的App会卡顿?

    有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? 知道Android究竟是如何在屏幕上显示我们期望的画面的? 对Android的视图架构有整体把握. 学会 ...

  2. 两张图告诉你tnsnames.ora 的配置详解

    图一: 图二: 是否解答了你的困惑呢?

  3. 两张图告诉你什么是box-sizing:border-box

    文章目录 什么是box-sizing:border-box 什么是box-sizing:border-box 当没有使用box-sizing:border-box时的盒子模型: 当使用了box-siz ...

  4. 两张图告诉你刺激战场最全年兽刷新位置:另附年兽对抗技巧

    刺激战场年兽都在哪?怎么才能不被年兽打到?刺激战场春节模式已经上线一段时间了,很多玩家还在不知道年兽刷新位置都在哪,网上发的年兽标记图大部分也是不全的.今天就给大家分享一下刺激战场刷新年兽的全位置地图 ...

  5. android 应用性能优化1

    1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉大家你一总结.我一总结的都说到了很多优化注意事项,但是看过这些文章后大多数存在一个问题就是只 ...

  6. Android UI性能优化 检测应用中的UI卡顿

    本文已在我的公众号hongyangAndroid首发. 转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/58626355 本文出自 ...

  7. Android应用性能优化最佳实践.

    移动开发 Android应用性能优化最佳实践 罗彧成 著 图书在版编目(CIP)数据 Android应用性能优化最佳实践 / 罗彧成著. -北京:机械工业出版社,2017.1 (移动开发) ISBN ...

  8. Android应用性能优化——学习心得

    Android应用性能优化--学习心得 Android应用性能优化这门课分为内存优化.视图优化.电量优化.Bitmap优化.其他优化等五大部分,下面这对这五大部分的学习能容做一下总结: 一. 内存优化 ...

  9. Android UI性能优化详解

    此文来自于MrPeak杂货铺,由于没法转载,只能贴这了,妄作者见谅:http://mrpeak.cn/android/2016/01/11/android-performance-ui 设计师,开发人 ...

最新文章

  1. 匹配算法:局部结构保留
  2. ocr中文 java_java 中文ocr
  3. 跳出数据计算拯救人工智能之打败机器学习方法详解二
  4. 前端定时器 setInterval 和 setTimeout
  5. ssm jsp跳转jsp_去掉Shiro默认login.jsp跳转
  6. synchronized 原理知多少
  7. 开发插件:分享10个非常实用IDEA插件,值得看一看!
  8. Sublime Text 3插件安装方法
  9. 20个使用柔和的色调的优秀网站设计示例
  10. TCP的带宽估计和丢包恢复
  11. L1-047 装睡 (10 分)—团体程序设计天梯赛
  12. (转)亚马逊创始人贝佐斯:前进路上,善良远比聪明重要
  13. 史上最详细的F450四轴装机实例(无名官方推荐的)
  14. C#获取枚举的key-value格式
  15. VUE博客模板----FXBLOG
  16. History of Object Recognition (物体识别)
  17. 《剪花布条》:从花布条中尽可能剪出几块小饰条
  18. LEAD广告联盟,真的能够年入百万吗?
  19. [ERROR] Slave I/O: error connecting to master
  20. 职场丨女生:啊啊啊,明天面试穿什么?【带图】

热门文章

  1. 第六章——串并行通信与接口技术
  2. Linux netfilter源码分析(2)
  3. linux下的多线程
  4. android guide 中文,Android API Guide:Toast 中文翻译
  5. gis地图和普通地图的区别_GIS之如何添加WMTS地图
  6. redhat升级linux内核,用rpm方式升级RHEL6.1内核
  7. MySQL基础思维导图
  8. 自然语言处理 matlab,程序员罗杰
  9. SpringCloud 阶段总结
  10. hbase meta中分区信息错误的记录