性能优化(七)

1,如何对android应用进行性能分析以及优化?

2,ddms 和 traceView

答:DDMS:

  1. DDMS 【Dalvik Debug Monitor Service】是安卓(android)开发环境中的Dalvik虚拟机调试监控服务。

  2. DDMS能够提供:测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息、Logcat、广播状态信息、模拟电话呼叫、接收SMS、虚拟地理坐标等。

  3. TraceView:

  1. Traceview是安卓(android)平台配备的性能分析的工具。

  2. 可以通过图形化让了解要跟踪的程序的性能,并且能具体到方法(method)。

  • DDMS和TraceView的区别:

  • DDMS是一个程序执行查看器,在里面可以看见线程和堆栈等信息,而TraceView是程序性能分析器。

3,性能优化如何分析systrace?

答:Systrace是Android4.1中新增的性能数据采样和分析工具。它可帮助开发者收集Android关键子系统(如surfaceflinger、WindowManagerService等Framework部分关键模块、服务,View系统等)的运行信息,从而帮助开发者更直观的分析系统瓶颈,改进性能。

Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况等。在Android平台中,它主要由3部分组成:

  • 内核部分:Systrace利用了Linux Kernel中的ftrace功能。所以,如果要使用Systrace的话,必须开启kernel中和ftrace相关的模块。
  • 数据采集部分:Android定义了一个Trace类。应用程序可利用该类把统计信息输出给ftrace。同时,Android还有一个atrace程序,它可以从ftrace中读取统计信息然后交给数据分析工具来处理。
  • 数据分析工具:Android提供一个systrace.py(python脚本文件,位于Android SDK目录/tools/systrace中,其内部将调用atrace程序)用来配置数据采集的方式(如采集数据的标签、输出文件名等)和收集ftrace统计数据并生成一个结果网页文件供用户查看。 从本质上说,Systrace是对Linux Kernel中ftrace的封装。应用进程需要利用Android提供的Trace类来使用Systrace.

关于Systrace的官方介绍和使用可以看这里:Systrace

1.Systrace简单使用

使用Systrace前,要先了解一下Systrace在各个平台上的使用方法,鉴于大家使用Eclipse和Android Studio的居多,所以直接摘抄官网关于这个的使用方法,不过不管是什么工具,流程是一样的:

  • 手机准备好你要进行抓取的界面
  • 点击开始抓取(命令行的话就是开始执行命令)
  • 手机上开始操作
  • 设定好的时间到了之后,会将生成Trace文件,使用Chrome将这个文件打开进行分析

4,用IDE如何分析内存泄漏?

5,Java多线程引发的性能问题,怎么解决?

答:

6,启动页白屏及黑屏解决?

答:

历史原因

当系统启动一个APP时,zygote进程会首先创建一个新的进程去运行这个APP,但是进程的创建是需要时间的,在创建完成之前,界面是呈现假死状态的,这就很尴尬了,因为用户会以为没有点到APP而再次点击,这极大的降低用户体验,Android需要及时做出反馈去避免这段迷之尴尬。于是系统根据你的manifest文件设置的主题颜色的不同来展示一个白屏或者黑屏。而这个黑(白)屏正式的称呼应该是Preview Window,即预览窗口。

好了,现在我们明白了,Preview Window其实是为了提高用户体验而有意设定的。因此,其实如果不是强迫症,它可能并不是一个问题。

但是我猜大部分小伙伴应该是和我一样的强迫症患者:这么丑的黑屏怎么能出现在我的APP上呢???!!!

所以,下面我们就来聊聊这个问题的解决方案。

引言

目前app的设计思路,都会有一个启动页,来进行一些数据的初始化等一些比较耗时的操作,这就会造成启动页短暂的白屏或者黑屏(黑还是白取决于你的默认主题)。那么,如何解决这种问题的?今天介绍一种比较完美的解决方案:layer_list叠加层

如果你的启动页,是一张静态图片,那么这种方式非常适合你。

一般来说,启动页都不会很花哨,标准的都是一个logo+app名字,上下排列。类似于下图:

QQ音乐启动页

如果是这样的,那就是最简单的,找UI切几张logo+文字的图,按照x xx xxx放好,然后drawable文件夹中新建一个layer_splash.xml文件:

?
1
2
3
4
5
6
7
8
9
10
11
<? xml version = "1.0" encoding = "utf-8" ?>
< layer-list xmlns:android = "http://schemas.android.com/apk/res/android" >
  <!-- 整体的背景颜色 -->
  < item android:drawable = "@color/white" /> < item >
  <!-- 中间的图片 -->
  < bitmap
   android:gravity = "center"
   android:src = "@drawable/icon_welcome"
   android:scaleType = "center" />
</ item >
</ layer-list >

然后在你的清单文件中,给启动页Activity单独设置一个主题:SplashAppTheme,并在其中引用刚才新建的叠加层布局文件,大功告成!这种方式可以做到秒开,无需任何等待。

?
1
2
3
4
5
<style name= "SplashAppTheme" parent= "android:Theme" >
   <item name= "android:windowNoTitle" > true </item>
   <item name= "android:windowFullscreen" > true </item>
   <item name= "android:windowBackground" > @drawable /layer_splash</item>
</style>

但是,如果你的启动页不是这种布局,而是类似于下图这种:

微博

上下布局,间隔较大,而且上下之间的布局不固定(根据机型屏幕大小自动适应),这种,该如何处理呢?UI妹纸又要求不能等比例放大显示,说那样很丑吧啦吧啦吧。。。

其实我们可以这样:把上下布局切开,然后就变成了这样:

splash_top

splash_bottom

我们的xml文件这样写:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<? xml version = "1.0" encoding = "utf-8" ?>
< layer-list xmlns:android = "http://schemas.android.com/apk/res/android" >
  <!-- 整体的背景颜色 -->
  < item >
   < color android:color = "@color/white" />
  </ item >
  <!-- 顶部 -->
  < item >
   < bitmap
    android:gravity = "top|center_horizontal"
    android:scaleType = "center"
    android:src = "@drawable/splash_top" />
  </ item >
  <!-- 底部 -->
  < item >
   < bitmap   android:gravity = "bottom|center_horizontal"
    android:scaleType = "center"
    android:src = "@drawable/splash_bottom" />
  </ item >
</ layer-list >

记得top 和 bottom要根据不同尺寸的启动页图片进行切割,这样就完美解决了各种分辨率屏幕的适配问题(包括平板)。

如果你的启动页比较简洁,和我说的这两种情况差不多,你完全可以不给启动页Activity设置布局文件(setContentView()),只使用叠加层作为布局显示。

如果你的app启动页比较花哨或者不规则,你也可以利用这种方式,增加一个比较简洁的app启动图片的叠加层布局,作为启动页的主题进行加载,启动初始化完了再进行页面的变化等操作,也可做到秒启动,不再忍受白屏或者黑屏。

7,启动太慢怎么解决?

答:一个Android应用程序真正启动慢的原因是在应用程序里面做了耗时的操作。把这些耗时的操作找出来并且新线程放到异步线程里面,那么问题就解决了。

利用TraceView逐个修复

Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量、布局、绘制显示在界面上。

在比如在Application里面用到了GSON,将String转化成json,我将这个移动到懒加载里面,是不是又减少了100毫秒呢?

在比如,有些Application中做了支付SDK的初始化,用户又不会一打开App就要支付,放在Application中加载干嘛?

此处我们这里举得例子是优化了139毫秒和100毫秒的,其实真正耗时的任务有的有1秒多,都被我优化完了,所以trace图中看不到了,就举个了这两个例子,还有SharedPreferences也是耗时大户,经过检测保存一个boolean变量耗时120+毫秒以上。

利用TraceView可以清楚我们每一个方法的耗时时间,极大的帮助了我们做优化工作。

优化思路总结
1、UI渲染优化,去除重复绘制,减少UI重复绘制时间,打开设置中的GPU过度绘制开关,各界面过度绘制不应超过2.5x;也就是打开此调试开关后,界面整体呈现浅色,特别复杂的界面,红色区域也不应该超过全屏幕的四分之一;
2、根据优先级的划分,KoMobileApplication的一些初始化工作能否将任务优先级划分成3,在首页渲染完成后进行加载,比如:PaySDKManager。
3、主线程中的所有SharedPreference能否在非UI线程中进行,SharedPreferences的apply函数需要注意,因为Commit函数会阻塞IO,这个函数虽然执行很快,但是系统会有另外一个线程来负责写操作,当apply频率高的时候,该线程就会比较占用CPU资源。类似的还有统计埋点等,在主线程埋点但异步线程提交,频率高的情况也会出现这样的问题。
4、检查BaseActivity,不恰当的操作会影响所有子Activity的启动。
5、对于首次启动的黑屏问题,对于“黑屏”是否可以设计一个.9图片替换掉,间接减少用户等待时间。
6、对于网络错误界面,友好提示界面,使用ViewStub的方式,减少UI一次性绘制的压力。
7、任务优先级为2,3的,通过下面这种方式进行懒加载的方式
8、Multidex的使用,也是拖慢启动速度的元凶,必须要做优化。后面有空专门写一篇Multidex。
Android应用启动优化:一种DelayLoad的实现和原理
所涉及到的代码我放到了Github上:https://github.com/Gracker/DelayLoadSample

8,怎么保证应用启动不卡顿?

9,App启动崩溃异常捕捉

10,自定义View注意事项

答:注意事项:

1)让MyView支持Warp_content

如果不在onMeasure()方法中对warp_content,也就是AT_MOST模式做处理,就相当于和使用match_parent效果一样,就不能达预期的效果。

解决方法是对在onMeasure()方法中做相应的处理,把需要设定的宽或者高,设置成匹配内容宽高的int值,当然也要考虑内边距padding。

思路如下:判断widthMeasureSpec和heightMeasureSpec是否为AT_MOST,也就是wrap_content,如果想让实际宽度为占满父控件,就把widthSpecSize当做参数,如果不想这样,想要自己定义宽度,就填入具体数值,也可以计算内容的宽度来作为参数。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if(widthSpecMode==MeasureSpec.AT_MOST&&heightMeasureSpec==MeasureSpec.AT_MOST){
    setMeasuredDimension(200, 200);
}else if(widthSpecMode==MeasureSpec.AT_MOST){
    setMeasuredDimension(200, heightSpecSize);
}else if (heightSpecMode==MeasureSpec.AT_MOST) {
    setMeasuredDimension(widthSpecSize, 200);
}

}

2)让MyView支持padding

这是因为直接继承了View的控件,如果不在draw方法中处理padding,那么padding属性是无法起作用的。另外,如果直接继承自ViewGroup的控件,需要在onMeasure()和onLayout()中考虑padding和子元素的margin对其造成的影响,不然将导致padding和子元素的margin失效。(padding是内边距,需要控件自己控制,而margin是外边距,由父控件影响)

3)尽量不要在View中使用Handler,因为View本身就提供了post系列的方法,完全可以替代Handler的作用。

4)View中如有线程或者动画,需要及时停止:

如果有线程或者动画需要停止时,那么onDetachedFromWindow是一个很好的时机。当包含此View的Activity退出或者当前View被remove时,View的onDetachedFromWindow方法会被调用,和此方法对应的是onAttachedToWindow方法会被调用。当View变得不可见时也需要停止线程和动画,如果不及时处理这种问题,有可能会造成内存泄漏。

5)注意特殊情况下的View滑动冲突

下面是一个画圆的例子:

getPaddingLeft();等等都是获取内边距的方法

getWidth();获取宽度的方法,注意这里是这个控件的宽度,并一定是圆的宽度

canvas.drawCircle(cx, cy, radius, paint);    画圆的方法,第一个参数是距离左边的长度,第二个参数是距离顶部的宽度,

第三个参数是半径,第四个是画笔实例。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    paint.setColor(Color.BLACK);
    final int paddingLeft =getPaddingLeft();
    final int paddingRight =getPaddingRight();
    final int paddingTop =getPaddingTop();
    final int paddingBottom =getPaddingBottom();
    int width=getWidth()-paddingLeft-paddingRight;
    int height=getHeight()-paddingTop-paddingBottom;
    int radius=Math.min(width, height)/2;
    canvas.drawCircle(paddingLeft+width/2,paddingTop+height/2 , radius, paint);
}


11,现在下载速度很慢,试从网络协议的角度分析原因,并优化(提示:网络的5层都可以涉及)

12,Https请求慢的解决办法(提示:DNS,携带数据,直接访问IP)

13,如何保持应用的稳定性

答:1)CRASH问题

Crash问题是最为常见的Android应用稳定性问题之一,具体表现为应用程序异常退出,比如闪退,Force Close等。

对于业务复杂、架构复杂的超级App而言,Crash问题如影随形,无法完全根除。业界中,一般引入Crash率,来评价衡量软件的稳定性。为了达到更低的Crash率,获取更好的稳定性,这对研发人员提出了更高的要求。

下面让我们解开Crash问题的面纱,一探究竟,降服这只怪兽。

2)CRASH技术原理

2.1 CRASH分类

从Android应用开发的角度来看,Crash问题可以分成两种类型:App Crash,Native Crash。

  • App Crash:Java层Crash,由于应用程序的Java层抛出了“未捕获异常(Uncaught Exception)”,从而导致了程序异常退出。
    Java程序中的空指针错误,数组越界等错误,在子线程中刷新UI等错误都会导致异常抛出,造成Crash。
  • Native Crash:Native层(C/C++)Crash,当程序出现异常时,比如空指针,数组越界等。与Java层抛出异常不同,此时系统Kernel会发送相应的signal,从而导致程序异常退出。

          工作中常见的Crash signal类型如下:

 

Signal

Value

Description

SIGSEGV

11

Invalid memory reference.

SIGBUS

7

Access to an undefined portion of a memory object.

SIGFPE

8

Arithmetic operation error, like divide by zero.

SIGILL

4

Illegal instruction, like execute garbage or a privileged instruction

SIGSYS

31

Bad system call.

SIGXCPU

24

CPU time limit exceeded.

SIGXFSZ

25

File size limit exceeded.

2.2 CRASH日志的生成

下面两小节将分别介绍Crash日志生成的原理,有助于后续的问题分析和解决。

2.2.1 App Crash日志的生成

当发生App Crash时,系统会将日志写入/data/system/dropbox 路径下的日志文件中。当然,在logcat的日志中也会有体现。

阅读Android系统源码可以了解到系统的处理流程大致如下:

  1. 发生crash的进程,在创建之初便准备好了defaultUncaughtHandler,用来来处理Uncaught Exception,并输出当前crash基本信息;
  2. 调用当前进程中的AMP.handleApplicationCrash;经过binder ipc机制,传递到system_server进程;
  3. 接下来,进入system_server进程,调用binder服务端执行AMS.handleApplicationCrash;
  4. 从mProcessNames查找到目标进程的ProcessRecord对象;并将进程crash信息输出到  /data/system/dropbox 路径下;
  5. 执行makeAppCrashingLocked
    • 创建当前用户下的crash应用的error receiver,并忽略当前应用的广播;
    • 停止当前进程中所有activity中的WMS的冻结屏幕消息,并执行相关一些屏幕相关操作;
  6. 再执行handleAppCrashLocked方法
  7. 通过mUiHandler发送消息SHOW_ERROR_MSG,弹出crash对话框
  8. 到此,system_server进程执行完成。回到crash进程开始执行杀掉当前进程的操作;
  9. 当crash进程被杀,通过binder死亡通知,告知system_server进程来执行appDiedLocked();
  10. 最后,执行清理应用相关的activity/service/ContentProvider/receiver组件信息。

2.2.2 Native Crash日志的生成

当发生Native Crash时,系统会将日志写入/data/tombstones/tombstone_XX日志文件中(其中XX为序号)。同时Crash日志也会保存到dropbox路径下。

阅读Android系统源码可以了解到系统的处理流程大致如下:

  1. 其中,标红的perform_dump阶段,将crash日志写入/data/tombstones路径下的日志文件中。
  2. 最后交由AMS进行handleApplicationCrashInner阶段,也会将Crash信息写入dropbox路径下。

2.2.3 权限的问题

以上2.2.1和2.2.2分别介绍了App Crash和Native Crash发生时,系统如何生成相关日志信息以及日志保存的路径。

工作中会发现,要访问/data/system/dropbox 以及 /data/tombstones路径,都需要root权限。如果手头上没有root过的手机只能尽力在logcat日志中寻找线索了,祝好运吧 ^_^

由于厂商测试中使用的ROM一般都有root权限,所以厂商提供的日志文件大部分都包含dropbox和tombstone路径下的日志文件。

另一方面也要看厂商对系统定制、修改的情况,不排除由于厂商修改了系统策略,日志存储变化的情况。

2.3 CRASH日志的收集

当APP已经发版,大量用户在使用中如果发生了APP Crash,我们如何能尽快发现并第一时间解决呢?这就涉及到如何尽快把发生问题的信息收集上来,具体的说就是把Crash日志从发生问题的手机上采集回来。从上一节,我们知道没有root过的手机,应用程序是无法访问/data/system/dropbox 和 /data/tombstones路径的。那么如何收集Crash日志呢?

2.3.1 App Crash日志的收集

Java中提供了Thread.UncaughtExceptionHandler用来捕获“未捕获异常”。我们可以实现自己的UncaughtExceptionHandler来获取App Crash信息,然后保存在本地文件中。在合适的时机下,将日志信息回传到统计平台。这样我们就能尽快的了解到发出去版本的Crash信息,从而尽快的将问题修复。

可以参考地图中的代码BaiduMapApplication和BMUncaughtExceptionHandler了解细节。

注:经过在真机上验证,实现了自定义的UncaughtExceptionHandler之后,dropbox目录下将无法自动生成App Crash的日志文件了。

2.3.2 Native Crash日志的收集

相对于App Crash,Native Crash的日志收集要复杂一些。由于系统没有提供获取Native Crash的接口,所以无法使用Android自带的任何API来实现这个功能。当前地图选取了Breakpad工具来手机Native层的Crash日志。

Breakpad是由Google提供的开源项目,用于Native层崩溃日志的收集。Chrome,Firefox,Picasa,Google Earth等项目也都在使用它。这里简单介绍一下BreakPad的主要工作流程。

如上图所示:

  1. Breakpad分成服务端和客户端,两个部分。
  2. 应用程序,比如百度地图,在编译打包过程中,breakpad的symbol dumper会读取符号表等调试信息,生成基于Breakpad格式的符号表。这份符号表保存在Breakpad的服务端。
  3. 正常发布的so文件不携带符号表信息。发布的App中集成了Breakpad的client模块。
  4. 当Native Crash发生时,Breakpad的Client模块会收集日志,并将日志写入minidump中。
  5. 适当的时机下,minidump日志会回传给服务端。
  6. 服务端,Breakpad的Processor模块会读取minidump,结合之前保存的符号表生成可读的调用栈信息。

 

更多细节可以参考地图代码中的模块 /subModules/nacrashcollector 和 地图Crash日志平台。

3. 测试手段

3.1 Monkey测试

稳定性测试一般通过自动化手段完成。Android SDK提供了“monkey”这个自动化测试工具。它可以运行在模拟器里或实际设备中,向系统发送随机的用户事件流,如按键输入、触摸屏输入、手势输入、Sensor 事件等, 实现对应用程序的压力测试。

执行方法:

1)  adb shell 连接到手机,进入命令行。

2)  执行命令

monkey --pct-touch 45 --pct-motion 20 --pct-majornav 10 --pct-appswitch 15 --pct-anyevent 10 --ignore-crashes --ignore-timeouts --ignore-security-exceptions –p com.baidu.BaiduMap --monitor-native-crashes --throttle 2000 -v 490000                                 

3)  可以把Monkey日志重定向到sdcard上,每次测试结束之后进行检查。

3.2 QA自动化测试平台


14,RecycleView和ListView的性能对比

答:ListView:

1.在布局文件中使用ListView,并为其定义一个id,方便我们之后的调用,宽高与父控件相同

2.准备数据,将数据添加到ArrayAdapter适配器当中

3.在Activity的java文件中使用findviewbyid找到ListView实例,为其设置Adapter

4.实现ListView的item项点击事件,直接使用Listview定义的setitemonClick方法就行了


RecycleView使用步骤:

1.在布局文件中使用RecyclerView,定义id,宽高与空间相同

2.准备RecyclerView的子项item布局,之后要在适配器类中使用,这里就不在赘述

3.定义个泛型类,如之前在ListView中所说的T类,适配器的泛型

3.定义一个适配器T类,使其继承RecyclerView.Adapter<T.ViewHolder>(ViewHolder是T类中的一个内部类)


15,ListView的优化

16,RecycleView优化

17,View渲染

答:深入Android渲染机制

1.知识储备

  • CPU: 中央处理器,它集成了运算,缓冲,控制等单元,包括绘图功能.CPU将对象处理为多维图形,纹理(Bitmaps、Drawables等都是一起打包到统一的纹理).

  • GPU:一个类似于CPU的专门用来处理Graphics的处理器, 作用用来帮助加快格栅化操作,当然,也有相应的缓存数据(例如缓存已经光栅化过的bitmap等)机制。

  • OpenGL ES是手持嵌入式设备的3DAPI,跨平台的、功能完善的2D和3D图形应用程序接口API,有一套固定渲染管线流程. 附相关OpenGL渲染流程资料

  • DisplayList 在Android把XML布局文件转换成GPU能够识别并绘制的对象。这个操作是在DisplayList的帮助下完成的。DisplayList持有所有将要交给GPU绘制到屏幕上的数据信息。

  • 格栅化 是 将图片等矢量资源,转化为一格格像素点的像素图,显示到屏幕上,过程图如下.

格栅化操作

  • 垂直同步VSYNC:让显卡的运算和显示器刷新率一致以稳定输出的画面质量。它告知GPU在载入新帧之前,要等待屏幕绘制完成前一帧。下面的三张图分别是GPU和硬件同步所发生的情况,Refresh Rate:屏幕一秒内刷新屏幕的次数,由硬件决定,例如60Hz.而Frame Rate:GPU一秒绘制操作的帧数,单位是30fps,正常情况过程图如下.

正常情况


2.渲染机制分析

渲染流程线

UI对象—->CPU处理为多维图形,纹理 —–通过OpeGL ES接口调用GPU—-> GPU对图进行光栅化(Frame Rate ) —->硬件时钟(Refresh Rate)—-垂直同步—->投射到屏幕

图片名称

渲染时间线

Android系统每隔16ms发出VSYNC信号(1000ms/60=16.66ms),触发对UI进行渲染, 如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps,这意味着计算渲染的大多数操作都必须在16ms内完成。

正常情况

这里写图片描述

渲染超时,计算渲染时间超过16ms

当这一帧画面渲染时间超过16ms的时候,垂直同步机制会让显示器硬件 等待GPU完成栅格化渲染操作, 
这样会让这一帧画面,多停留了16ms,甚至更多.这样就这造成了 用户看起来 画面停顿

当GPU渲染速度过慢,就会导致如下情况,某些帧显示的画面内容就会与上一帧的画面相同

GPU超时情况


3.渲染时会出现的问题

GPU过度绘制

GPU的绘制过程,就跟刷墙一样,一层层的进行,16ms刷一次.这样就会造成,图层覆盖的现象,即无用的图层还被绘制在底层,造成不必要的浪费.

这里写图片描述

过度绘制查看工具

在手机端的开发者选项里,有OverDraw监测工具,调试GPU过度绘制工具
其中颜色代表渲染的图层情况,分别代表1层,2层,3层,4层覆盖.

这里写图片描述

我的魅族手机的Monitor GPU Rendering

这里写图片描述

计算渲染的耗时

任何时候View中的绘制内容发生变化时,都会重新执行创建DisplayList,渲染DisplayList,更新到屏幕上等一 系列操作。这个流程的表现性能取决于你的View的复杂程度,View的状态变化以及渲染管道的执行性能。

举个例子,当View的大小发生改变,DisplayList就会重新创建,然后再渲染,而当View发生位移,则DisplayList不会重新创建,而是执行重新渲染的操作.

当你的View过于复杂,操作又过于复杂,就会计算渲染时间超过16ms,产生卡顿问题

渲染耗时呈现工具

工具中,不同手机呈现方式可能会有差别.分别关于StatusBar,NavBar,激活的程序Activity区域的GPU Rending信息。激活的程序Activity区域的GPU Rending信息。

界面上会滚动显示垂直的柱状图来表示每帧画面所需要渲染的时间,柱状图越高表示花费的渲染时间越长。

中间有一根绿色的横线,代表16ms,我们需要确保每一帧花费的总时间都低于这条横线,这样才能够避免出现卡顿的问题。

这里写图片描述

每一条柱状线都包含三部分, 
蓝色代表测量绘制Display List的时间, 
红色代表OpenGL渲染Display List所需要的时间, 
黄色代表CPU等待GPU处理的时间。

这里写图片描述


4. 如何优化

有人会说这些小地方,不值得优化.但是当你用的是低配机器,内存到饱和,CPU运算到达饱和,就像一个界面要做很多交互,绘制,加载图片,请求网络.后,一个小问题就会导致页面卡顿(就像我手机的淘宝客户端…),OOM,项目崩溃.

是的,这就是力量~

Android系统已经对它优化

在Android里面那些由主题所提供的资源,例如Bitmaps,Drawables都是一起打包到统一的Texture纹理当中,然后再传递到 GPU里面,这意味着每次你需要使用这些资源的时候,都是直接从纹理里面进行获取渲染的。

我们要做的优化

扁平化处理,防止过度绘制OverDraw

1.每一个layout的最外层父容器 是否需要?

这里写图片描述

2.布局层级优化

进行检测时,可能会让多种检测工具冲突,用Android Device Monitor的时候,最好关闭相关手机上的开发者检测工具开关. 
查看自己的布局,深的层级,是否可以做优化. 
渲染比较耗时(颜色就能看出来),想办法能否减少层级以及优化每一个View的渲染时间.

Hierarchy Viewer工具

他是查看耗时情况,和布局树的深度的工具.

这里写图片描述

3.图片选择

Android的界面能用png最好是用png了,因为32位的png颜色过渡平滑且支持透明。jpg是像素化压缩过的图片,质量已经下降了,再拿来做9path的按钮和平铺拉伸的控件必然惨不忍睹,要尽量避免。

对于颜色繁杂的,照片墙纸之类的图片(应用的启动画面喜欢搞这种),那用jpg是最好不过了,这种图片压缩前压缩后肉眼分辨几乎不计,如果保存成png体积将是jpg的几倍甚至几十倍,严重浪费体积。

4.清理不必要的背景

这里写图片描述

5.当背景无法避免,尽量用Color.TRANSPARENT

因为透明色Color.TRANSPARENT是不会被渲染的,他是透明的.



//优化前
//优化前: 当图片不为空,ImageView加载图片,然后统一设置背景
Bean bean=list.get(i);
 if (bean.img == 0) {
            Picasso.with(getContext()).load(bean.img).into(holder.imageView);
        }
        chat_author_avatar.setBackgroundColor(bean.backPic);

 

//优化后
//优化后:当图片不为空,ImageView加载图片,并设置背景为TRANSPARENT;
//当图片为空,ImageView加载TRANSPARENT,然后设置背景为无照片背景
Bean bean=list.get(i);
 if (bean.img == 0) { Picasso.with(getContext()).load(android.R.color.transparent).into(holder.imageView); holder.imageView.setBackgroundColor(bean.backPic); } else { Picasso.with(getContext()).load(bean.img).into(holder.imageView); holder.imageView.setBackgroundColor(Color.TRANSPARENT); } 

 

————-对比结果——————– 
这里写图片描述

6.优化自定义View的计算

View中的方法OnMeasure,OnLayout,OnDraw.在我们自定义View起到了决定作用,我们要学会研究其中的优化方法.

学会裁剪掉View的覆盖部分,增加cpu的计算量,来优化GPU的渲染


  /**
     * Intersect the current clip with the specified rectangle, which is
     * expressed in local coordinates.
     *
     * @param left   The left side of the rectangle to intersect with the
     *               current clip
     * @param top    The top of the rectangle to intersect with the current clip
     * @param right  The right side of the rectangle to intersect with the
     *               current clip
     * @param bottom The bottom of the rectangle to intersect with the current * clip * @return true if the resulting clip is non-empty */ public boolean clipRect(float left, float top, float right, float bottom) { return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); } 

 


5.总结

性能优化其实不仅仅是一种技术,而是一种思想,你只听过它的高大上,却不知道它其实就是各个细节处的深入研究和处理.

当然,有的时候也需要自己进行权衡效果和性能,根据需求进行选择.

还有,Android Device Monitor 是个好东西~简直就是性能优化大本营,性能优化的工具基本都在其中.

所以在平时的开发过程中,养成良好的思考习惯,是第一步~

写代码的时候要想: 
1.你的代码是不是多余? 
2.你的对象有没有必要在循环中创建? 
3.你的计算方法是不是最优?

画界面的时候要想: 
1.布局是否有背景? 
2.是否可以删掉多余的布局? 
3.自定义View是否进行了裁剪处理? 
4.布局是否扁平化,移除非必需的UI组?

最后,Android Device Monitor 是个好东西~ 性能优化的工具基本都在其中.

18,Bitmap如何处理大图,如一张30M的大图,如何预防OOM

答:加载位图原理分析

1BitmapFactory提供了几种解码方式(decodeByteArray(), decodeFile(), decodeResource()等等),以便从多种资源中创建一个Bitmap(位图)对象。可以根据你的图片数据来源选择最合适的解码方式。这些方法视图为构造Bitmap对象分配内存,因此很容易导致OutOfMemory(OOM)异常。每一种解码方式都有额外的特征,你可以通过BitmapFactory.Options类类指定解码方法。

2、尽量不要使用setImageBitmapsetImageResourceBitmapFactory.decodeResource直接使用图片路径来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再调用上述方法将其设为ImageView sourcedecodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。

19,java中的四种引用的区别以及使用场景

20,强引用置为null,会不会被回收?





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值