面试必备】深入分析App卡顿原因及优化建议

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

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

    从setContentView()说起
public class AnalyzeViewFrameworkActivity extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_analyze_view_framwork);
  }
}

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

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


这个图可能和伙伴们在书上或者网上常见的不太一样,为什么不太一样呢?因为是我自己画的,哈哈哈...
下面就来看着图捋一捋Android最基本的视图框架。
PhoneWindow
估计很多同学都知道,每一个Activity都拥有一个Window对象的实例。这个实例实际是PhoneWindow类型的。那么PhoneWindow从名字很容易看出,它应该是Window的儿子(即子类)!
知识点:每一个Activity都有一个PhoneWindow对象
那么,PhoneWindow有什么用呢?它在Activity充当什么角色呢?下面我就姑且把PhoneWindow等同于Window来称呼吧。

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.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实现的。我们来看个源码的例子就比较清晰了。开始啦!

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()方法),把视图添加到窗口上
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会那么卡,以及开始有思路着手解决这些卡顿。

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

原文地址:https://blog.51cto.com/14295695/2407445

时间: 2024-08-01 06:12:46

面试必备】深入分析App卡顿原因及优化建议的相关文章

Android中app卡顿原因分析示例

在知乎回答了一个“为什么微博的app在iPhone比Android上流畅”的问题.后面部分是一个典型的动画卡顿的性能分析过程,因此帖在这里.有编程问题可以在这里交流.知乎链接. ========================================================= 我来说下我所知道的事情.我不知道iOS为什么流畅,但我知道一些Android为什么不流畅的原因. 首先,就题主所说的问题,我用iPad和小米Pad对比了一下微博滑动滚屏这件事情(2014年8月10日目前微博

android中app卡顿优化问题

所谓app卡顿原因就是在运行时出现了丢帧,还可能是UI线程被阻塞.首先来一下丢帧现象,android每16ms会对界面进行一次渲染,如果app的绘制.计算等超过了16ms那么只能等下一个16ms才能进行渲染,这就发生了丢帧现象. 手机卡顿出现的原因:1,布局过于复杂:xml布局文件可能存在深层嵌套或者组件过多: 2,重复绘制:一个界面的某一点可能在同一时间进行了多次绘制: 3,内存抖动:系统内存是有限的,系统经常会将不活跃的进程置入外存中就是常说的虚拟内存,当调用它时再把它从外存转入内存,内存外

Android app优化之导致app 卡顿慢的直接原因

大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计师的角度,他们希望App能够有更多的动画,图片等时尚元素来实现流畅的用户体验.但是Android系统很有可能无法及时完成那些复杂的界面渲染操作.Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps,这意味着程序的大多数操作都必须在16ms内完成()时间超出16ms越多,丢的帧就越多,可以大概估计一下Android 5秒没响应抛

Android App 卡顿分析

极力推荐Android 开发大总结文章:欢迎收藏 程序员Android 力荐 ,Android 开发者需要的必备技能 Android App 反应卡顿,从技术上将就是UI 渲染慢. UI渲染是从您的应用程序生成一个框架并将其显示在屏幕上的行为. 为了确保用户与您的应用程序的交互顺利,您的应用程序应该在16ms内渲染帧数达到每秒60帧(为什么60fps?). 如果您的应用程序因UI渲染速度缓慢而受到影响,那么系统将被迫跳过帧,用户将感觉到您的应用程序中出现卡顿. 我们把这个叫做jank. 本篇文章

直播卡顿原因详解及优化

随着视频直播的发展,很多直播团队可能会遇到视频直播卡顿,频繁出现缓冲标志或者直播画面一卡一卡等情况.究竟是哪些原因造成了视频直播观看的卡顿情况呢? 又拍直播云结合实践经验,从设备.视频流.网络这三方面进行解剖分析造成直播卡顿的问题及其解决方法. 视频直播卡顿原因 造成直播视频卡顿的原因主要有设备.视频流.网络这三方面的问题. 问题排查及解决方法 设备 高清视频往往会给硬件带来解码压力,由于解码造成的卡顿尤为明显.同时如果PC端Flash Player或移动端播放软件版本过低,可能也会造成解码问题

iOS开发之记一次App卡顿Bug的解决历程(踩了一个StoryBoard的坑)

虽然今天是周末,但是还是要学习的不是.写这篇博客的目的呢是记录一下自己在上次项目迭代中踩的坑,不过这个坑已经填上了.虽然坑不大,但是踩上去肯定能崴脚.其实还是那句话,在没人给你指路的情况下,踩的坑多了,慢慢的就成长了.为了填今天要讲的这个坑,午觉都没睡呢.当然今天博客的内容并不高深,而且出现的几率还是蛮大的,所以喽就记录一下.也许你已经踩过,或者你已经将此坑填上,但是今天是我踩了一脚呢,没办法,还是记录一下吧. 解决历程用一个字描述就是:“删”. 一.描述这个“坑” 首先呢,我们先来看一下这个B

Android App卡顿慢优化之多线程优化

本博客涉及的内容有:多线程并发的性能问题,介绍了AsyncTask,HandlerThread,IntentService与ThreadPool分别适合的使用场景以及各自的使用注意事项,这是一篇了解Android多线程编程不可多得的基础文章,清楚的了解这些Android系统提供的多线程基础组件之间的差异以及优缺点,才能够在项目实战中做出最恰当的选择. 1)Threading Performance(线程性能问题) 在程序开发的实践当中,为了让程序表现得更加流畅,我们肯定会需要使用到多线程来提升程

app卡顿问题检测--KMCGeigerCounter

介绍: KMCGeigerCounter是一个iOS帧速计算器,像盖革计数器那样,当动画丢失一帧时它就记录一次.掉帧通常是不可见的,但是很难区分55fps和60fps之间的不同,而KMCGeigerCounter可以让你观测到掉落5帧的情况.可以通过这个来检测app的卡顿程度 使用: KMCGeigerCounter的使用非常简单: [KMCGeigerCounter sharedGeigerCounter].enabled = YES; 说明: 卡顿:         流畅:        

Windows资源管理器打开文件夹卡顿原因及解决办法

全新安装的 Win8 打开文件夹居然会卡顿,特别是打开EXE程序比较多的文件夹,通过资源监视器查看,幕后凶手就是 Windows Defender 杀毒软件. MSE是微软提供防毒功能,而Windows Defender则是为用户提供反间谍功能,Win8已经把这两个功能合二为一了.用过MSE的用户就知道它有卡EXE的毛病,在Win8中也是这样,当你打开EXE程序.压缩包比较多的文件夹,它就一直扫描啊扫描-..所以MSE很忙-.. 解决方法: 先用Windows Defender全盘扫描,然后可以