Android:应用开发进阶必经之路之性能优化(上)

前言

性能优化在一款产品的迭代过程当中很是重要;程序实现了功能、还原产品原型只能保证程序能用,但若是要让用户更愿意使用,产品得好用。试想一下若是你开发的产品启动慢、页面显示须要长时间转圈加载、页面切换卡顿、黑白屏、用一会机器就发烫、耗内存、OOM、程序切换到后台后占用内存没法释放......,这些问题就像正在玩游戏时弹出提示框这类糟糕的用户体验同样让用户恼火,若是用户不得不使用你的产品,可能还会一直忍受;但若是有不少同类竞品,糟糕的用户体验会大大影响留存率。有时候产品在市场上的表现差,真不能全怪产品和运营,程序体验问题也是很大一部分缘由。 但大部分产品并无足够重视性能问题,随便打开一个应用,即便是大厂出品,也极大可能存在过渡绘制和内存泄露的问题;也有多是开发人员意识到了程序存在性能问题,但完成迭代就够忙的了,哪有时间去作这类不能体现绩效的事情。其实在愈来愈重视体验,同类产品竞争愈来愈激烈的环境下,对于开发人员来说,只完成迭代,把功能作完远远不够,最重要的是把产品作好,让更多人愿意使用。重视性能问题,优化产品的体验,比改几个无关痛痒的bug会有价值得多。 网上可以找到不少关于性能优化颇有价值的参考资料(详见文末),包括腾讯、阿里、魅族、豌豆荚、小米、UC等知名互联网公司都作过关于APP性能优化的分享,若是你专一于应用开发,而且想作一款备受欢迎的产品,性能优化是你进阶路上必须去学习和实践的。java

1、性能问题分类

除了交互、视觉、内容方面的问题外,在用户使用过程当中,给用户形成烦恼的问题均可以归结为是性能问题,好比上文中列出的这些都属于性能问题,按照影响的方面不一样,能够分为以下几大类:android

  • 内存问题: 耗内存、OOM、程序切换到后台后占用内存没法释放(OOM会影响产品的稳定性;耗内存、内存泄露会影响整机的性能;占用内存多预示着留给其它应用的剩余内存空间小);面试

  • 功耗问题: 发烫(耗电);canvas

  • 流畅度问题: 启动慢、页面显示须要长时间转圈加载、页面切换卡顿、黑白屏(卡慢崩会让人烦躁);性能优化

针对上面一系列的性能问题,谷歌官方提供了各类各样的工具来针对性的解决各个方面的问题,也有不少不错的第三方工具值得尝试:bash

  • 内存问题: 提供了Android Studio的静态代码检测功能、Android Monitor;第三方内存泄露分析工具Leakcanary、MAT;架构

  • 功耗问题: 提供了GPU呈现模式、battery-historian、Android Monitor;app

  • 流畅度问题: 提供了Android Studio的静态代码检测功能、Android Monitor、HierarchyViewer、StrictMode、过渡绘制检测工具、TraceView等;ide

除了上面提到的这些性能优化工具外,谷歌还在Youtube上提供了一系列关于Android应用性能优化的短视频Android Performance Patterns,介绍如何优化Android各个方面的性能问题。工具

2、性能优化指标

性能优化的效果仅凭感受很难衡量,一切应该看数听说话,好比流畅度优化,刷新频率每秒越接近60帧越理想,但只要每秒钟超过24帧人眼就没法辨别了,因此仅凭感受是没法区分优化前的30帧和优化后的40帧的区别的。为了说明作性能优化有足够的价值,就有必要经过一系列指标来讲明优化先后的区别。

性能指标的定义应该具备可衡量、可比较的特色,因此每项性能指标能够是数值,也能够是一份报告,好比:

  • 流畅度: FPS,即Frams per Second,一秒内的刷新帧数,越接近60帧越好;

  • 启动时间: 时间,越短越好;

  • 内存泄露: AS静态代码检测结果、MAT检测结果,内存泄露很难用数值定义,但能够经过将优化先后工具检测的结果对比得出结论。没有内存泄露最好;

  • 内存大小: 峰值,峰值越低越好;

  • 功耗: 单位时间内的掉电量,掉电量越少越好;

从上面各项性能指标的定义能够看出,性能优化效果的评估主要是经过对比得出来的,性能如何只是相对的。只要针对同一个应用的同一项指标,优化后比优化前更优,就说明优化是有效果的。

3、性能优化原则和方法

1优化原则

解决性能问题的过程当中,遵循如下几个原则,有助于提升解决问题的效率:

  • 足够多的测量: 不要凭感受去检测性能问题、评估性能优化的效果,应该保持足够多的测量,数据不会说谎。使用各类性能工具备助于快速定位问题,这比凭感受要靠谱得多;

  • 使用低配置的设备: 一样的程序,在低端配置的设备中,相同的问题会暴露得更为明显;高配的设备不少时候会让你忽略掉性能问题;

  • 权衡利弊: 在可以保证产品稳定、按时交付的前提下去作优化,不能顾此失彼,为了性能优化致使产品迟迟不能交付;

2优化方法

性能优化的指标不少,乍看上去无从下手,但和解bug同样,只要讲方法,事情就变得迎刃而解。对于大多数问题来说,只要遵循了解问题→定位问题→分析问题→解决问题→验证问题的思路,基本上均可以解决:

  • 了解问题: 对于性能问题来说,这个步骤只适用于某些明显的性能问题,不少没法感知的性能问题须要经过工具定位;

  • 定位问题: 经过工具检测、分析,定位在什么地方存在性能问题;若是很难定位,能够采用排除法(屏蔽部分代码,看现象是否仍然存在,若是还存在,则说明被屏蔽的代码没有问题,这样逐渐缩小问题的范围);

  • 分析问题: 找到问题后,分析针对这个问题该如何解决,肯定解决方案;

  • 解决问题: 这个没什么可说的,若是是本身没法解决的问题,借助搜索引擎,你遇到过的问题不少人都遇到过,而且极有可能已经被解决了;

  • 验证问题: 保证每一次优化都有效,没有产生新问题,保证产品的稳定;

4、性能优化工具

本文重点介绍谷歌官方提供的一系列应用性能优化工具以及值得推荐的第三方性能优化工具,这些工具主要集中在以下几个地方:

  • 开发者选项: GPU呈现模式分析、GPU过渡绘制、严格模式、应用无响应ANR等;

  • IDE中: Android Studio,好比静态代码检测工具、Memory Monitor、CPU Monitor、NetWork Monitor、GPU Monitor、Layout Inspector、Analyze APK等;

  • SDK中: sdk\tools,好比DDMS、HierarchyViewer、TraceView等;

  • 第三方性能优化工具: MAT、Leakcanary等;

1开发者选项

首先从不须要依赖任何工具,直接借助手机中的开发者选项进行应用性能检测提及。开发者选项须要进入开发者模式后才能在系统设置中显示,对于大多数设备,能够经过以下方式在手机中开启开发者选项:打开“系统设置”→点击进入“关于手机”→连续点击“版本号”选项直至提示已进入“开发者模式”,就能够在“系统设置”中看到“开发者选项了”,打开“开发者选项”,能够看到不少可以帮助开发者检测应用性能的选项:

  • 调试GPU过渡绘制(Visualize GPU Overdraw): 过渡绘制用于检测你的程序是否存在没必要要的绘制(举个栗子:同一个区域存在多个视图,刷新的时候被遮挡的视图也在绘制),致使显示时的性能问题,它能够帮助开发者解决以下问题:

(1)找出应用中哪些地方存在没必要要的渲染;

(2)帮助开发者发现哪些地方能够减小渲染,提升程序运行效率;

显示过渡绘制区域的步骤以下:“开发者选项”→点击“调试GPU 过渡绘制”→点击“显示过渡绘制区域”,一旦使能,对设备中的任何应用都有效:

Android经过不一样颜色来区分同一个区域绘制的次数,颜色越深,表示过渡绘制的次数越多,过渡绘制越严重。以下图所示,蓝色表示存在一次过渡绘制;深红色表示同一区域存在4次及以上的过渡绘制:

应用没法彻底作到没有过渡绘制,优化是尽可能避免没必要要的过渡绘制,一般状况下保证同一区域过渡绘制少于三次都是合理的,即只要是出现红色(淡红色和深红色)的地方,就是须要优化的地方:

过渡绘制不只仅会影响程序的刷新频率,还会致使程序启动慢、黑白屏、耗内存等问题,由于过渡绘制主要是由于布局复杂致使,android在加载布局文件的时候,其实是读取xml文件并解析,而后根据每一个视图的关系去测量、绘制、显示每个视图;复杂的布局会须要更长的解析、测量、绘制、显示时间,也须要更多的内存(这与是否设置了视图背景有关)。在实际开发过程当中,有以下几种常见的过渡绘制优化方法:

(1) 使用merge标签

merge标签就是为减小布局层次而生的,它经过减小View树的层级来优化布局,merge只能做为xml布局的根标签使用(由于Activity的根布局是FrameLayout,因此只有Activity对应的布局文件根标签为FrameLayout时才适合使用merge标签),若是在代码中Inflate带merge标签的布局时,必须为这个自定义View指定一个父ViewGroup,而且设置attachToRoot为true。merge只可以在xml布局文件中使用,没有对应的java类。下面的实例演示了merge标签的用法,经过“GPU过渡绘制”查看优化先后的效果,能够明显看到经过merge标签解决了过渡绘制的问题;经过Hierarch View观察优化先后的视图树,能够明显看到使用merge标签后的视图层级减小了:

优化前

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="fill_parent"
             android:layout_height="fill_parent"
             android:background="@android:color/white">
    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="center"
        android:src="@drawable/golden_gate"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="24dp"
        android:text="No Merge Layout"
        android:textColor="@android:color/black"/>
</FrameLayout>
复制代码

效果图及过渡绘制显示:

Hierarchy View视图树:

优化后

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
       android:background="@android:color/white">
    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="center"
        android:src="@drawable/golden_gate"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="24dp"
        android:text="Has Merge Layout"
        android:textColor="@android:color/black"/>
</merge>
复制代码

效果图及过渡绘制显示:

Hierarchy View视图树:

(2) 使用ViewStub标签 在开发应用的时候,常常会遇到这样的状况,在程序运行时根据条件来决定显示/隐藏哪一个视图;一般会在布局文件中将其写上去,默认隐藏,而后在代码中根据条件去判断是否显示。这样作的优势是逻辑清晰,但缺点是耗费资源,在布局文件中将某个视图默认设置为invisable或者gone,在Inflate布局文件的时候仍然会被infalte,一样会被实例化、设置属性,但有可能默认被隐藏的视图用户在某一次操做中极可能不会去触发它。为了提升布局文件加载效率和减小额外的资源消耗,强烈建议使用ViewStub标签,ViewStub是一个用于在运行时加载布局资源、不可见、宽高为0的View,在布局文件中使用它只是用于占位,在代码中没有手动加载它时,并不会影响页面的测量、绘制、显示效率,在代码中经过inflate加载ViewStub时,ViewStub会用在布局文件中为其指定的布局文件来代替它自身,经过前面的解释可想而知,ViewStub只可以被inflate一次,一旦加载后ViewStub对象就会被置为空;ViewStub标签有对应的java类ViewStub.java,经过阅读源码能够发现,确实在初始化的时候设置为隐藏、不绘制、宽高为0,而且它复写了View的dispatchDraw和draw方法,这俩方法是空方法,没有调用super的方法,也没有执行本身的代码:

public final class ViewStub extends View {

    ...

    public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context);

        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ViewStub, defStyleAttr, defStyleRes);
        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
        mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
        a.recycle();

        setVisibility(GONE);
        setWillNotDraw(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(0, 0);
    }

    @Override
    public void draw(Canvas canvas) {

    }

    @Override
    protected void dispatchDraw(Canvas canvas) {

    }

    ...

}
复制代码

下面是ViewStub在Inflate先后的布局及视图树:

XML布局文件

activityviewstublayout.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/activity_view_stub_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <include
        android:id="@+id/inclueId"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"
        android:background="#ff00ff00"
        layout="@layout/inclue_viewstub_layout"/>
    />
    <View
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@android:color/black"/>
    <ViewStub
        android:id="@+id/viewStubId"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="8"
        android:layout="@layout/inclue_viewstub_layout"/>
    <Button
        android:id="@+id/inflateViewStubBtnId"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:text="InflateViewStub"
        android:onClick="onClick"
        />
</LinearLayout>
复制代码

inclueviewstublayout.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/titleTxtViewId"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="ViewStub!!!!"
        android:textColor="@android:color/black"
        android:textSize="24dp"/>
</RelativeLayout>
复制代码
代码
public void onClick(View v){
    switch(v.getId()){
        case R.id.inflateViewStubBtnId:{
            inflateViewStub();
        }
        break;
    }
}

private void inflateViewStub(){
    mViewStub = (ViewStub)findViewById(R.id.viewStubId);
    if(null == mViewStub){
        Toast.makeText(this, "ViewStub is Empty", Toast.LENGTH_LONG).show();
        return;
    }
    mViewStub.inflate();
}
复制代码
Inflate前

界面效果:

Hierachy View:

Inflate后

Hierachy View:

能够看到在ViewStub Inflate前ViewStub不占用布局层级,因此不会消耗程序资源;Inflate后会占用布局层级;在试验的过程当中,点击两次及以上Inflate按钮时,会弹出“ViewStub is Empty”的Toast,说明mViewStub在实例化一次后再次实例化时会失败,由于在Inflate时已经被replace掉,系统找不到这个资源ID。

(3) 使用Space

过渡绘制问题是由于绘制引发的,space标签能够只在布局文件中占位,不绘制,Space标签有对应的java类Space.java,经过阅读源码能够发现,它继承至View.java,而且复写了draw方法,该方法为空,既没有调用父类的draw方法,也没有执行本身的代码,表示该类是没有绘制操做的,但onMeasure方法正常调用,说明是有宽高的。

XML布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:gravity="center"
        android:text="TextView 1"
        android:textColor="@android:color/black"
        android:textSize="28dp"
        android:background="@android:color/darker_gray"/>
    <Space
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@android:color/black"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:gravity="center"
        android:text="TextView 2"
        android:textColor="@android:color/black"
        android:textSize="28dp"
        android:background="@android:color/darker_gray"/>
    <Space
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:gravity="center"
        android:text="TextView 3"
        android:textColor="@android:color/black"
        android:textSize="28dp"
        android:background="@android:color/darker_gray"/>
</LinearLayout>
复制代码
效果图

能够看到在布局中给第一个Space控件设置了黑色背景,但从效果图能够看出Space并无变成黑色,说明没有执行绘制方法。

Hierarchy Vieiw

观察视图树也能够看出Space会占用空间(由于有宽高)。

(4) 去掉没必要要的背景

若是不是经过测量和仔细分析,你很难发现这个不经意的细节会是致使过渡绘制、内存问题的主要缘由,每一个Activity都会在AndroidManifest.xml中设置主题,主题的目的是设置界面的显示风格,但在设置主题的时候一般状况下默认给Window设置了背景,注意是Window而不是Activity,Activity是依附在Window上的,Android系统在刷新整个界面时不只仅是刷新Activity,还会刷新Window。若是默认没有去掉window的背景,而且在布局文件中给Activity设置了背景,就会存在过渡绘制的问题,具体状况能够看下面的实例:

activitybackgroundlayout.xml

(这里为了演示在布局文件中为每一个视图设置了背景,在真实状况中没有必要为每一个视图都设置):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/activity_background_layout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginLeft="20dp"
        android:layout_marginRight="20dp">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="10dp"
            android:background="@android:color/white"
            android:text="@string/more_text"/>
    </ScrollView>
</RelativeLayout>
复制代码

使用该布局的Activity对应的主题以下:

<style name="AppTheme" parent="@android:style/Theme.Light.NoTitleBar">
</style>
复制代码

经过过渡绘制工具检测,存在过渡绘制:

下面是去掉window背景后的效果(布局文件不变,主题变更以下):

<style name="AppTheme" parent="@android:style/Theme.Light.NoTitleBar">
    <item name="android:windowBackground">@null</item>
</style>
复制代码

效果图:

说明:

一、在主题中去掉Window的背景时要注意,去掉以后必须从新运行程序检查一下,避免有些Activity并无设置背景致使界面背景为黑色;

二、有的程序为了不冷启动时界面黑屏/白屏的问题,在主题中为window设置了一张图片,而后在布局文件中为Activity也设置了背景,这样既会致使过渡绘制问题,还会致使内存问题(同一个页面两张全屏的图片,双倍内存);因此这种解决方式并不妥,若是是启动速度问题,直接优化启动速度比这种方式靠谱。

(5) 其余

一、经过Canvas的clipRect方法控制每一个视图每次刷新的区域,这样能够避免刷新没必要要的区域,从而规避过渡绘制的问题;

二、如对一个View作Alpha转化,须要先将View绘制出来,而后作Alpha转化,最后将转换后的效果绘制在界面上。通俗点说,作Alpha转化就须要对当前View绘制两遍,可想而知,绘制效率会大打折扣,耗时会翻倍;

三、多和产品、UI沟通,避免过渡设计,过渡绘制最好的解决方案是界面的布局自己就不复杂,这样程序实现出来的界面就会少不少过渡绘制,优化也会简单不少。

总结一下过渡绘制的检测和解决方案:经过“开发者选项”中的“显示过渡绘制”和Android提供的工具“HierarchyViewer”,以每一个界面为单位,能够彻底检测出每一个界面的过渡绘制问题;由于致使过渡绘制的缘由不一,因此也会有多种对应的解决方案:

一、merge标签能够解决相同布局嵌套致使的过渡绘制问题;

二、ViewStub标签能够解决动态加载页面布局,避免默认加载没必要要布局的问题;

三、Space标签能够解决只占位、不刷新的视图问题;

四、去掉Window背景能够解决全部界面的过渡绘制问题;

五、clipRect能够解决只刷新固定区域的问题;

六、没必要要的alpha值设置能够解决同一视图被屡次绘制的问题;

七、最重要的是产品设计合理,多和产品、UI沟通,避免无心义的工做。

GPU呈现模式分析(Profiling GPU Rendering)

从Android 4.1开始,在“开发者选项”中提供了GPU呈现模式分析的选项,GPU呈现模式是一个方便你快速观察UI渲染效率的工具,主要做用是实时查看每一帧的渲染效率,定位哪里存在渲染的性能问题;经过以下方式能够打开GPU呈现模式分析:“系统设置”→“开发者选项”→“GPU呈现模式分析”→在弹出的窗口中选择“在屏幕上显示成条形图(On screen as bars)”。

打开GPU呈现模式后,你能够在机器的任何界面看到以下图所示的条形图,顶部通知栏、当前活动程序(主窗口)、底部导航栏都会有对应的呈现模式条形图,用于观察通知栏、当前活动界面、导航栏的渲染效率。

随着界面的刷新,界面上会滚动显示锤子的柱状图来表示每帧画面说须要的渲染时间,柱状图越高表示花费的渲染时间越长。中间有一根绿色的横线,表明每帧的最长渲染时间:16ms,咱们须要确保每一帧花费的总时间都低于这条横线,这样才可以避免出现卡顿的问题。

从图中能够看出,每一条柱状线包含三种颜色,但从Android 6.0开始,你看到的每条柱状线已不止三种颜色:

每种颜色表明每一帧渲染过程当中须要完成的某一件事情,由于6.0以前的三种颜色不大可以清晰地帮助咱们定位性能问题的具体缘由,因此从6.0开始,将每一帧的渲染过程拆分红了8个步骤,每一个步骤一种颜色,每种颜色的意义以下:

(1)Swap Buffers: 表示处理任务的时间,也能够说是CPU等待GPU完成任务的时间,线条越高,表示GPU作的事情越多;

(2)Command Issue: 表示执行任务的时间,这部分主要是Android进行2D渲染显示列表的时间,为了将内容绘制到屏幕上,Android须要使用Open GL ES的API接口来绘制显示列表,红色线条越高表示须要绘制的视图更多;

(3)Sync & Upload: 表示的是准备当前界面上有待绘制的图片所耗费的时间,为了减小该段区域的执行时间,咱们能够减小屏幕上的图片数量或者是缩小图片的大小;

(4)Draw: 表示测量和绘制视图列表所须要的时间,蓝色线条越高表示每一帧须要更新不少视图,或者View的onDraw方法中作了耗时操做;

(5)Measure/Layout: 表示布局的onMeasure与onLayout所花费的时间,一旦时间过长,就须要仔细检查本身的布局是否是存在严重的性能问题;

(6)Animation: 表示计算执行动画所须要花费的时间,包含的动画有ObjectAnimator,ViewPropertyAnimator,Transition等等。一旦这里的执行时间过长,就须要检查是否是使用了非官方的动画工具或者是检查动画执行的过程当中是否是触发了读写操做等等;

(7)Input Handling: 表示系统处理输入事件所耗费的时间,粗略等于对事件处理方法所执行的时间。一旦执行时间过长,意味着在处理用户的输入事件的地方执行了复杂的操做;

(8)Misc Time/Vsync Delay: 表示在主线程执行了太多的任务,致使UI渲染跟不上vSync的信号而出现掉帧的状况;出现该线条的时候,能够在Log中看到这样的日志:

I/Choreographer(*): Skipped XXX frames! The application may be doing too much work on its main thread
复制代码

经过GPU呈现模式能够清晰地检测出致使渲染问题的具体缘由,但不能定位是哪一行代码出了问题,从上面的描述可知,减小过渡绘制能够很好地提高GPU呈现模式的表现力;若是要跟踪具体哪一行代码致使了渲染的性能问题,须要借助各类性能检测工具。好比经过TraceView跟踪是否存在耗时操做;经过“显示过渡绘制”跟踪是否存在过渡绘制等。

启动严格模式

当当前界面在主线程中存在耗时操做时,会闪烁屏幕,但只会提示你存在耗时操做,不会告诉你具体的地方;若是要精肯定位具体哪里耗时,应该在代码中添加StrictMode检查,在log中会报详细的耗时信息:

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites()
        .detectNetwork()
        .penaltyLog().build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects()
        .detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());
复制代码

关于严苛模式的详细说明,能够参考官网的这篇文章,很详细:StrictMode。

显示全部“应用无响应”(Show all ANRs)

当任何一个应用(包括后台应用)无响应时会弹出“App Not Responding”对话框,主要用于识别程序之间是否存在干扰;

更多关于开发者选项的信息,能够查阅这篇文章:All about your phone's developer options。

篇幅缘由,未完待续,敬请期待............

最后送福利了,如今关注我而且加入群聊能够获取包含源码解析,自定义View,动画实现,架构分享等。 内容难度适中,篇幅精炼,天天只需花上十几分钟阅读便可。你们能够跟我一块儿探讨,欢迎加群探讨,有flutter—性能优化—移动架构—资深UI工程师—NDK相关专业人员和视频教学资料,还有更多面试题等你来拿~ Android开发交流群:1018342383

下一篇:Android:应用开发进阶必经之路之性能优化(下)

相关文章
相关标签/搜索