1,如何对android应用进行性能分析以及优化?
2,ddms 和 traceView
答:DDMS:
DDMS 【Dalvik Debug Monitor Service】是安卓(android)开发环境中的Dalvik虚拟机调试监控服务。
DDMS能够提供:测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息、Logcat、广播状态信息、模拟电话呼叫、接收SMS、虚拟地理坐标等。
TraceView:
Traceview是安卓(android)平台配备的性能分析的工具。
可以通过图形化让了解要跟踪的程序的性能,并且能具体到方法(method)。
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,并在其中引用刚才新建的叠加层布局文件,大功告成!这种方式可以做到秒开,无需任何等待。
但是,如果你的启动页不是这种布局,而是类似于下图这种:
微博
上下布局,间隔较大,而且上下之间的布局不固定(根据机型屏幕大小自动适应),这种,该如何处理呢?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系统源码可以了解到系统的处理流程大致如下:
- 发生crash的进程,在创建之初便准备好了defaultUncaughtHandler,用来来处理Uncaught Exception,并输出当前crash基本信息;
- 调用当前进程中的AMP.handleApplicationCrash;经过binder ipc机制,传递到system_server进程;
- 接下来,进入system_server进程,调用binder服务端执行AMS.handleApplicationCrash;
- 从mProcessNames查找到目标进程的ProcessRecord对象;并将进程crash信息输出到 /data/system/dropbox 路径下;
- 执行makeAppCrashingLocked
- 创建当前用户下的crash应用的error receiver,并忽略当前应用的广播;
- 停止当前进程中所有activity中的WMS的冻结屏幕消息,并执行相关一些屏幕相关操作;
- 再执行handleAppCrashLocked方法
- 通过mUiHandler发送消息SHOW_ERROR_MSG,弹出crash对话框;
- 到此,system_server进程执行完成。回到crash进程开始执行杀掉当前进程的操作;
- 当crash进程被杀,通过binder死亡通知,告知system_server进程来执行appDiedLocked();
- 最后,执行清理应用相关的activity/service/ContentProvider/receiver组件信息。
2.2.2 Native Crash日志的生成
当发生Native Crash时,系统会将日志写入/data/tombstones/tombstone_XX日志文件中(其中XX为序号)。同时Crash日志也会保存到dropbox路径下。
阅读Android系统源码可以了解到系统的处理流程大致如下:
- 其中,标红的perform_dump阶段,将crash日志写入/data/tombstones路径下的日志文件中。
- 最后交由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的主要工作流程。
如上图所示:
- Breakpad分成服务端和客户端,两个部分。
- 应用程序,比如百度地图,在编译打包过程中,breakpad的symbol dumper会读取符号表等调试信息,生成基于Breakpad格式的符号表。这份符号表保存在Breakpad的服务端。
- 正常发布的so文件不携带符号表信息。发布的App中集成了Breakpad的client模块。
- 当Native Crash发生时,Breakpad的Client模块会收集日志,并将日志写入minidump中。
- 适当的时机下,minidump日志会回传给服务端。
- 服务端,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(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渲染速度过慢,就会导致如下情况,某些帧显示的画面内容就会与上一帧的画面相同
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
是不会被渲染的,他是透明的.
————-对比结果——————–
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
答:加载位图原理分析
1、BitmapFactory提供了几种解码方式(decodeByteArray(), decodeFile(), decodeResource()等等),以便从多种资源中创建一个Bitmap(位图)对象。可以根据你的图片数据来源选择最合适的解码方式。这些方法视图为构造Bitmap对象分配内存,因此很容易导致OutOfMemory(OOM)异常。每一种解码方式都有额外的特征,你可以通过BitmapFactory.Options类类指定解码方法。
2、尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource直接使用图片路径来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再调用上述方法将其设为ImageView的 source。decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,从而节省了java层的空间。
19,java中的四种引用的区别以及使用场景
20,强引用置为null,会不会被回收?