Android APP 的性能优化如何开展?

Android性能优化:

一、卡顿优化:

为什么卡顿:android使用消息机制来更新UI线程的,UI线程有个looper,其中loop方法中不断取出message,调用绑定的handler在UI线程执行,如果dispatchMessage方法中有耗时操作,就会导致丢帧,绘制超时,UI线程阻塞,从而出现卡顿。Choreographer.FrameCallBack可以获取每一帧的绘制时间,从而计算两帧之间的时间,超过16ms则发生丢帧。

问题检测工具:

blockCanary:检测原理:监控dispatchMessage函数的耗时情况。looper和handler 处理渲染等一系列动作。

sysTrace:命令行工具

traceView:android sdk内置的工具。

GPU过度绘制调试模式:开发者选项

解决方法:

去掉不需要背景色,布局视图数扁平化,减少透明色,即alpha的使用,使用轻量级系统的布局,例如viewstub,behavior系列dialog或者view,

ANR优化

ActivityManagerService(简称AMS)WindowManagerService(简称WMS)会检测App的响应时间,如果App在特定时间无法相应屏幕触摸或键盘输入时间,或者特定事件没有处理完毕,就会出现ANR。无论是四大组件或者进程等只要发生ANR,最终都会调用AMS.appNotResponding()方法。

检测原理:android中所有keyEvent,touchEvent都是有序放在队列中,依次执行,每个被执行事件都被保存在waitQueue中,执行完毕之后从中移除。当用户触发一个事件的时候,会先判断waitQueue中是否为空,为空则立即响应执行,不为空的话,说明还有事件未执行完毕,此时会判断当前时间和上一个事件响应时间是否大于超时时间(一般5s,broadcast是10s),如果超时,则会通过ActivityManagerService弹框提示。

原因:

  • 主线程阻塞或主线程数据读取
  • CPU满负荷,I/O阻塞
  • 内存不足
  • 各大组件ANR(service,broadCast耗时操作)

检测工具和原理:

工具:Log,traces.txt,Java线程调用分析,DDMS分析ANR问题,

Android中所有事件(activity,service生命周期管理)都是通过Looper+messageQUeue+handler来处理的,跟卡顿原理一样,这里就是记录每个message dispatchMessage执行的时间。

解决办法:

尽量避免在主线程(UI线程)中作耗时操作。
 

三、内存优化。 

       1)内存抖动是由于短时间内有大量对象进出新生区导致的,它伴随着频繁的GC

避免:

       尽量避免在循环体内创建对象,应该把对象创建移到循环体外。

      注意自定义View的onDraw()方法会被频繁调用,所以在这里面不应该频繁的创建对象。

      当需要大量使用Bitmap的时候,试着把它们缓存在数组中实现复用。

      对于能够复用的对象,同理可以使用对象池将它们缓存起来。

      2)内存泄漏:一般内存泄漏(traditional memory leak)的原因是:由忘记释放分配的内存导致的。(Cursor忘记关闭等)

                             逻辑内存泄漏(logical memory leak)的原因是:当应用不再需要这个对象,当仍未释放该对象的所有引用。

 详细解释:程序在申请内存后,当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象。:

                  本该回收:不使用该对象

                  不能被回收:其他对象持有不再使用对象的引用//

                 本质原因:(无意识)持有引用者的生命周期—>当引用者需要销毁时候,无法被正确回收

知识点:什么时候会触发GC?

垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。


    1)当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

    2)Java堆内存不足时,GC会被调用。当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

    3)在编译过程中作为一种优化技术,Java 编译器能选择给实例赋 null 值,从而标记实例为可回收。

由于是否进行主GC由JVM根据系统环境决定,而系统环境在不断的变化当中,所以主GC的运行具有不确定性,无法预计它何时必然出现,但可以确定的是对一个长期运行的应用来说,其主GC是反复进行的。

GC Root的引用点是啥:

1、java栈中引用点对象 

2、方法静态引用的对象 

3、方法常量引用的对象 

4、Native中JNI引用的对象

5、Thread——活着的线程

         3)  内存溢出OOM。应用程序所需要的内存,超出了系统为每个应用程序分配的内存(即应用程序产生的内存泄漏较多,累计不释放,却还不断申请)。可以通过AS的menory相关工具查看内存消耗变化。

举例优化点:

       不使用当前activity上下文,使用application上下文,

        动画使用要及时释放。网络文件等流及时关闭。

        广播推出时要取消注册。service执行完要stop。

        EventBus等观察者模式框架手动解除注册。

        集合类引用集合元素对象。释放list

        单例持有其他context时候导致泄漏。单例生命周期同application几乎一致。

        静态类持有其他引用。

        匿名内部类和非静态内部类强持有外部引用,(若外部销毁内部仍持有,导致内存泄漏,举例子一个MyAscnyTask设置为静态引用Application上下文即可)可访问外部变量方法,静态内部类如          果没有传参则不持有引用。自主生命周期。不能访问外部。

        匿名内部类减少使用,再使用静态内部类时候使用弱引用。

LeakCanary检测原理:

1、RefWatcher.watch()创建了一个KeyedWeakReference用于去观察对象。

2、然后,在后台线程中,它会检测引用是否被清除了,并且是否没有触发GC。

3、如果引用仍然没有被清除,那么它将会把堆栈信息保存在文件系统中的.hprof文件里。

4、HeapAnalyzerService被开启在一个独立的进程中,并且HeapAnalyzer使用了HAHA开源库解析了指定时刻的堆栈快照文件heap dump。

5、从heap dump中,HeapAnalyzer根据一个独特的引用key找到了KeyedWeakReference,并且定位了泄露的引用。

6、HeapAnalyzer为了确定是否有泄露,计算了到GC Roots的最短强引用路径,然后建立了导致泄露的链式引用。

7、这个结果被传回到app进程中的DisplayLeakService,然后一个泄露通知便展现出来了。

一句话描述:

在一个Activity执行完onDestroy()之后,将它放入WeakReference中,然后将这个WeakReference类型的Activity对象与ReferenceQueque关联。这时再从ReferenceQueque中查看是否有没有该对象,如果没有,执行gc,再次查看,还是没有的话则判断发生内存泄露了。最后用HAHA这个开源库去分析dump之后的heap内存。

四、布局优化

需要优化情况:

       UI线程即主线程中做耗时操作导致阻塞。

       Layout层数过多,绘制过度,16ms内未完成渲染

       同一时间内动画执行次数过多,CPU、GPU负载过重

       View过度绘制,只需要部分元素更新,却刷新所有元素。导致CPU/GPU负载

       View频繁触发onmeasure/onlayout/耗时过多频繁渲染

       内存频繁触发GC过多,内存创建频繁,阻塞渲染

       冗余资源及逻辑等导致加载和执行缓慢

       打开手机的显示GPU视图查看。4/3/2/1图层显示颜色不一样。GPU呈现模式分析。查看渲染消耗

建议方案:

1、使用include和merge减少复用,减少层级。

2、使用轻量级ViewStub不占用内存。减少使用RelativeLayout。测绘耗时。

3、复杂页面上使用ConstraintLayout。

4、自定义绘制View在onDraw中避免耗时任务。

五、速度优化(线程优化和网络优化)

线程优化:

避免在主线程做耗时操作。

Asynctask:AsyncTask 是 Android 对线程池 Executor 的封装,多线程,适合执行耗时操作,完成后或者操作中对主线程进行更新。适合下载apk文件等

HandlerThread:一个Android 已封装好的轻量级异步类,实现工作线程 & 主线程(UI线程)之间的通信,即:将工作线程的执行结果传递给主线程,从而在主线程中执行相关的UI操作。继承Thread,单线程+异步队列(Handler)的场景,比如IO读写操作数据库、文件,应用启动优化,从数据库读取数据展示view中,应用在IntenService中。使用:先创建HandlerThread,并调用start,因为start才有looper对象。其中有WorkHandler,用来工作发送message,还有UIHandler用来处理主线程更新,创建WorkHandler需要接收HandlerThread的looper对象,处理message。处理是需要UI更新则使用UIHandler。 

Thread:适合单个耗时任务。

Handler:实现子线程与主线程通信。Looper/MessageQueue/Message

ThreadPool:分解任务分发不同线程同时处理。管理和合理分配线程,有效利用,采用线程池管理,重用内部线程,

IntentService:继承service,适合执行由Ui触发的耗时的后台任务。并可以把这些任务执行的情况通过一定的机制反馈给UI。线程任务 需 按顺序在后台执行。

同service比较:

1、Service 也是在主线程中运行,所以一些耗时操作,依然需要单独开启线程去执行。

IntentService内部维护一个HandlerThread,用来执行业务操作,执行完成,会自动调用stopSelf,内存友好,

如何使用:

创建一个service继承intentService,在onHandleIntent中实现业务逻辑代码。

调用用startService(*),在manifest中注册。

android 8.0以上不能使用,startService,可用startForegroundService。或者用JobScheduler替代(5.0以上)

JobScheduler允许开发者创建在后台执行的job,当预置的条件被满足时,这些Job将会在后台被执行。JobScheduler会把一系列的job收集起来一起执行。

网络优化:

1、请求图片webP、缩略图等等

2、后端接口合理设计,返回数据控制、请求刷新策略

六、电量优化

以上优化减少CPU/GPU消耗也是优化电量使用。

可做网络状态判断,决定请求动作

避免无效的频繁网络请求

后台任务尽可能少唤起CPU

合理使用广播和服务

七、启动优化

在启动页面设置主题windowbackground,设置一个界面而非白屏黑屏。给用户进入app的感受。

避免在application做大量的耗时操作,沉重的初始化。

避免I/O操作/反序列化/网络操作/布局嵌套。

jetpack的APP startUp。实现Initializer接口,提供了一个 ContentProvider 来运行所有依赖项的初始化,避免每个第三方库单独使用 ContentProvider 进行初始化,从而提高了应用的程序的启动速度。

八、代码/包/优化

避免创建不必要的对象,首选静态,优化使用static final,避免使用浮点数。

代码瘦身。lint检查useless resouce 和 unused code,

图片压缩,能绘制的尽量绘制。

重用资源,上下箭头通过动画实现。

同一图像不同颜色通过tint和tintMode实现5.0以下用ColorFilter。

使用webP文件格式。AS提供转换

使用矢量图形。nine-patch。svg

代码混淆使用Proguard工具。压缩优化混淆

组件化。不涉及业务逻辑的功能模块放在jcenter上面管理,按需下载。

插件化:加载需要的apk

ndk支持cpu架构指令集类型

猜你喜欢

转载自blog.csdn.net/guoyingmiss/article/details/110536101
今日推荐