一个质量较高的性能分析专栏
我们先根据这个专栏出发,验证其各种方案的可行性。
性能优化大纲
大神博客
1.性能优化目的
性能优化的目的是:提高应用流畅性、稳定性、节省资源。
①流畅性:运行的更加流畅、不卡顿
②稳定性:稳定运行,使用过程中不出现引用崩溃和无响应(ANR)问题
③节省资源:节省耗费的资源,包括安装包大小、内存占用、耗电量、网络资源(手机流量)等
2.性能指标
流畅性、稳定性、资源节省性
具体操作
优化操作一:流畅性
主要针对三个方向优化:
启动速度、页面显示速度、响应速度
优化方案:
采用异步加载、分部加载、延期加载策略,减少主线程中的操作时间。
具体操作:
1.启动速度
1.减少onCreate的时间。
可以将onCreate的代码放到onResume中执行,不过为了防止重复初始化,需要加标志位。
2.优化布局文件
优化布局层次结构:使用include、merge、ViewStub、ConstraintLayout等。
merge使用主要是的目的是在优化布局,减少布局嵌套,一般与include结合使用
观察布局的工具:Hierarchy Viewer
优化布局的工具:Layoutopt
3.提高Adapter、ViewHolder的效率
4.减少主线程的耗时时间
将耗时过长的操作放入后台线程中执行,只在需要修改UI时通知主线程修改。
2.页面显示速度
优化方案:布局优化 、绘制优化
Android性能优化:手把手带你全面了解 绘制优化
Android性能优化:布局优化 详细解析
主要优化方向:
①降低View.onDraw的复杂度
②避免过度绘制(Overdraw)
具体操作就是:
第一点:不要在我们自定义的View中创建新的局部变量。
避免onDraw中执行大量或者耗时的操作。
第二点:移除默认的Window背景、移除控件中不必要的背景、减少布局文件的嵌套、自定义空间View优化(使用#clipRect、#quickReject)
可以在开发者选项中打开<调试GPU过度绘制>开关,比如蓝色是一层、绿色两层、红色四层等,尽量在两层以内,也就是绿色。(微信整个是红色的...)
第二点实际操作:
①移除默认的Window背景:
在应用程序默认继承的主题中,也就是清单文件中application项中的theme中:
添加一行:
- @null
也就是设置windowBackground为null。
②③可以使用AS自带的Layout Inspector来查看页面布局嵌套情况。(Hierarchy View已被废弃)
④自定义控件View优化:使用clipRect()/quickReject()
Canvas#clipRect:
给Canvas设置一个裁剪区域,只有该区域内才会被绘制,区域之外的都不绘制。
比如抽屉布局可以使用这个。
quickReject:
判断和某个矩形相交,若判断与矩形相交,则可跳过相交的区域,减少过渡绘制。
其他优化方案:
使用OpenGL绘图。(Android最高级的绘图机制,没用过..)
Profile GPU Rendering工具:开发者选项中可打开此开关,用于在屏幕上实时显示GPU渲染每一帧图像花费的时间。
参考资料
总结:
3.响应速度
优化原因:
应用程序出现ANR情况,从而导致应用程序响应速度慢。
ANR原因
①应用在5s内未响应用户的输入事件
②广播接收器在10s内未完成相关的逻辑
③Service在20s内无法处理完成(不是15s哦)
【检测主线程中是否有耗时行为】
【开启StrictMode模式】
在Application的onCreate中开启:
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
然后过滤信息:
`adb logcat | grep StrictMode`
StrictMode详解
博客详解
StrictMode可以用于捕获应用主线程中发生的磁盘I/O、网络访问违规等问题。(并不会检测到耗时任务,比如Thread.sleep)
主要检测两大问题:线程策略(ThreadPolicy)和VM策略(VmPolicy)。
ThreadPolicy线程策略:
1.自定义的耗时调用:使用detectCustomSlowCalls()开启(检测不到耗时操作,需要自己定义)
2.磁盘读取、写入(I/O操作):detectDiskReads()、detectDiskWrites()
3.网络操作:detectNetwork()
VmPolicy虚拟机策略:
1.Activity泄露,使用detectActivityLeaks()开启(都不用LeakCanary)
2.未关闭的Closable对象泄露,使用detectLeadekClosedObjects()开启;
3.泄露的Sqlite对象,对象detectLeakedSqliteObjects
4.网络操作,使用detectNetwork
过滤日志:StrictMode
优化方案:
使用多线程,实际开发中,当一个进程发生了ANR后,系统会在/data/anr目录下创建一个traces.txt文件,通过该文件可定位出ANR的原因。
总结:
4.稳定性
大神博客
主要是:应用崩溃(Crash)、应用无响应(ANR)
ANR上面已经说了,主要看应用崩溃。
应用崩溃Crash很多情况是因为内存溢出,即OOM,故需要避免OOM现象。
因为内存优化很重要,所以我们来
内存优化
1.Android内存管理机制
从上图可以看到,Android内存分配主要就两个工作:
内存分配+内存回收(释放)
内存管理的对象:①进程②对象③变量
Android系统分为3个层次:
Application Framework、ART虚拟机、Linux内核
其中,负责进程内存的角色:Linux内核 、Application Framework
负责对象、变量内存的角色:ART虚拟机
针对进程的内存策略
由ActivityManagerService集中管理所有进程的内存分配。
内存回收策略,分两步:
1.由Application Framework决定回收的进程类型
Android中的进程是托管的;有进程空间紧张时,会按进程优先级从低到高的顺序自动回收进程。
Android进程分为5个优先级:
前台进程、可见进程、服务进程、后台进程、空进程
当系统需回收进程时,最先回收空进程、最后回收前台进程
2.Linux内核真正回收具体进程
1.ActivityManagerService对所有进程进行评分(评分存放在变量adj中)
2.更新评分到Linux内核
3.由Linux内核完成真正的内存回收
内存分配策略
JMM(Java内存模型)中,将内存区域分为五个部分(图片中只有三个,并不太规范):
程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区
①程序计数器:线程私有,记录线程上下文(多线程是通过轮流切换和分配处理器时间片实现的),唯一没有OutOfMomery
②Java虚拟机栈(Stack):线程私有,与线程生命周期相同,线程创建的时候创建。主要存储方法的内容:局部变量、参数、返回值以及运算的中间结果等。
③本地方法栈:存储Native方法内容
④Java堆:线程共享,运行时内存区域,占用内存最大的地方,几乎所有的对象实例都存在这里。分为三个部分:年轻代(Eden、Survivor)、老年代
⑤方法区:线程共享,主要存储已经被java虚拟机加载的类的信息,包括运行时常量池、字段、方法信息、静态变量等。在编译时就已经分配好,存在与程序整个运行期间,不需要回收。
垃圾回收算法:
1.标记-清除算法 2.复制算法 3.标记-整理算法 4.分代收集算法
常见的内存问题
1.内存泄漏
2.内存抖动
3.图片Bitmap相关
4.代码质量&数量
5.日常不正确使用
1.内存泄漏
发生内存泄漏的本质原因:
本该回收的对象因为某些原因而不能被回收、从而继续停留在堆内存中
常见内存泄漏原因:
①集合类
②Static变量修饰的成员变量
③非静态内部类/匿名类
④资源对象使用后未关闭
具体了解
1.集合类导致内存泄漏:
集合类添加元素后,仍引用着集合元素对象,导致该集合中的元素对象无法被回收,从而导致内存泄漏。
Android内存泄漏之集合类
// 通过 循环申请Object 对象 & 将申请的对象逐个放入到集合List
List