Android 性能优化篇

1.数据结构优化

        1.1 ArrayList 原理

        查找快,删除,新增元素慢,数组越大,性能越差

        1.2 LinkedList 原理

        查找慢,删除,新增快

        1.3 HashMap原理

        扩容机制规则会浪费25%的内存(扩容因子0.75)

        1.4 Android特有的数据结构

        SparseArray->两个数组,一个存Key,一个存Value;由于存储数据有序,利用二分查找法            性能更高。且数据越多,性能优势越明显;内部还有延迟删除机制。

        局限性:key只能是整数

        注意:如果value为int值,使用SparseIntArray,效率更高

        根据需求使用合理的数据结构,能有效的提高APP性能


2.内存优化

        2.0 内存抖动

        内存波动图呈锯齿状、频繁GC导致卡顿(STW

        2.1 内存泄漏

        程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费。 长生命周期对象持有短生命周期对象强引用,从而导致短生命周期对象无法被回收!

        2.2 内存溢出

        OOM,内存泄漏会导致内存溢出;file文件资源等未释放也会导致内存溢出

        2.3 内存回收的判断方法

        可达性分析法:GC Root

        2.4 四大引用

        强引用,弱引用,软引用,虚引用

        2.5 常见内存泄漏场景

                -- 资源性对象未关闭(文件等)

                -- 注册对象未注销(如广播,EventBus等)

                -- 类的静态变量持有大数据对象(静态变量为GCRoot点)

                -- 单例造成的内存泄漏

                -- 非静态内部类的静态实例

                -- Handler临时性内存泄漏

                -- 集合中的对象只有添加没有删除造成的内存泄漏

                -- WebView造成的内存泄漏(理解泄漏原理)

                -- Thread导致的内存泄漏,Thread生命周期不可控,可能导致引用了短生命周期的

        2.6 内存泄漏检测工具

                -- MAT(Memory Analyzer Tool),一款Eclipse开发的内存检测工具,可检测对象的引用链,和现存的对象个数等。

                -- Android Studio 的 Profiler 可检测实时内存使用情况,并可调用GC,判断是否有内存泄漏现象

                -- LeakCanary

                    原理:注册Activity和Fragment的监听,在OnDestroy时,添加Activity或Fragment到WeakReference(与ReferenceQueque关联)中,接着从ReferenceQueque中查看是否有该对象,如果没有,则放入观察队列,再调用GC,如果还没有释放,则添加到内存泄漏队列,大于5个时,dump heap文件,利用haha分析内存。再用一个View的服务显示出内存泄漏的位置

                -- StrickMode,严苛模式,可设置内存泄漏监听,当发生内存泄漏时,会打印相关的日志


3.启动优化

        3.1 启动时间统计

                adb shell am start -S -W packageName/className

                total time:启动该APP的时间

                wait time:包含暂停上个Activity到启动该APP的时间

                主要看total time

        3.2 启动方式

                -- 冷启动:第一次打开APP

                -- 热启动:Activity在内存中,把存在的Activity带到前台;按home键,再点击该应用

                -- 温启动:退出了APP,但是进程还未被杀掉。再次打开该应用,不用重新创建进程

                启动时间;冷启动 > 温启动 > 热启动

                Google启动计划中,冷启动5秒左右,温启东2秒左右,热启动1.5秒左右

          3.3 检测方法耗时工具

                Android studio Profiler 进行函数调用时间跟踪

                设置APP -> Edit Configurations -> Profilling -> Start this recording on startup: java/kotlin Method Trace

                

再点击profile运行

 

 开始跟踪后,点击Stop结束跟踪

 结束后可选择要观察的时间段,右侧点击Top Down 可看到每个函数执行的额时间,根据此时间结合代码做对应的优化

         3.4 StrickMode

                严苛模式监听不合理的代码,可优化速度

            StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
            .detectDiskReads()
            .detectCustomSlowCalls()
            .detectDiskWrites()
            .penaltyLog()
            .build())

            StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
            .detectLeakedSqlLiteObjects()
            .detectLeakedClosableObjects()
            .detectActivityLeaks()
            .penaltyLog()
            .penaltyDeath()
            .build())
        3.5常见优化方案

        -- Activity可采用异步加载布局文件AsyncLayoutInflater

        -- 合理的使用异步初始化,延迟初始化,懒加载机制

        -- 启动过程避免耗时操作,如数据库I/O操作不要放在主线程执行

        -- 类加载优化,提前异步执行类加载

        -- 合理使用IdleHandler进行延迟初始化

        -- 简化布局,见4,布局和卡顿优化     

4.布局优化

        4.1 Layout Inspector

        查看布局层级,点击眼睛 选择第一项可过滤系统的布局

         4.2 常见的布局优化

                -- 使用 ViewStub 来延迟加载布局。

                -- 使用 include 标签来重用布局。

                -- 使用 merge 标签来优化布局文件。

                -- 使用 ConstraintLayout 来减少布局层级。

                -- 使用 Lint 来检查布局文件中的问题。


5.卡顿优化

        5.1 systrace

        5.2 Profiler的Sys trace

VSync的方波周期就表示帧率,如发现连续的长周期方波,可通过CPU Usage选择执行长 的这一段,查看具体执行情况。可查看sys trace的具体用法

 5.3 Looper的Logging打印每次消息的运行时间

                原理如源码所示,在Looper中的loop方法中,queue.next()后,消息执行前和执行后都有相应日志打印,日志的时间间隔,即为消息执行完的大致时间,可根据此时间判断是否有超时执行情况

 使用方法:创建一个类实现Printer接口,实现println方法即可,统计第一次打印日志到第二次打印日志的时间

class LogMonitor: Printer {
    private var writeFlag = false
    private var time:Long = 0
    private val TAG = "LogMonitor"
    override fun println(x: String?) {
        x?.let {
            Log.e(TAG,it)
        }
        writeFlag =!writeFlag
        if (writeFlag){
            time = System.currentTimeMillis()

        }else{
            var useTime = System.currentTimeMillis() - time
            Log.e(TAG,"use time ${useTime}")
        }
    }
}

在APP中初始化

Looper.getMainLooper().setMessageLogging(LogMonitor())
 5.4 利用Choreographer的回调监听每一帧执行的时间
Choreographer.getInstance().postFrameCallback(object:Choreographer.FrameCallback{
    private var lastTime:Long = 0
    override fun doFrame(frameTimeNanos: Long) {
        if (lastTime == 0L){
            lastTime = frameTimeNanos
            Choreographer.getInstance().postFrameCallback(this)
            return
        }

        var subTime = frameTimeNanos - lastTime
        subTime = subTime/1000000
        if (subTime > 16.6f){
            Log.e(TAG,"lost frame ${subTime / 16.6f}")

        }
        lastTime = frameTimeNanos
        Choreographer.getInstance().postFrameCallback(this)
    }
})

6.电量优化

6.1 Doze低电耗模式

Doze中文是打盹,系统会定期退出打盹一小会时间,让应用完成其延迟的活动。在此维护期间,系统会运行所有待处理的同步,作业和闹钟,并允许应用访问网络。

 随着时间的推移,系统安排的维护期的次数越来越少,这有助于长期处于不活动状态时降低耗电量。

用户可通过移动设备,打开屏幕或连接至充电器唤醒设备,系统就会立即退出低电耗模式,且所有应用都会恢复正常活动。

 6.2 WorkManager

        WorkManager 是适合用于持久性工作的推荐解决方案。如果工作始终要通过应用重启和系统重新启动来调度,便是持久性的工作。由于大多数后台处理操作都是通过持久性工作完成的,因此 WorkManager 是适用于后台处理操作的主要推荐 API。

详情请参考:应用架构:数据层 - 使用 WorkManager 调度任务 - Android 开发者  |  Android Developers

使用:WorkManager 使用入门  |  Android 开发者  |  Android Developers

6.3 Battery Historian2.0

环境搭建:电量优化Battery Historian2.0 配置 - 简书

6.4 Profiler的ENERGY

可查看用电的柱状图

6.5 电量优化总结

·减少操作:应用是否存在可删减的多余操作?是否可以缓存已下载的数据,而不是每次重新下载

·推迟操作:应用是否需要立即执行某项操作?是否可以等到设备充电后或者WiFi连接时再将数据备份到云端

·合并操作:工作是否可以批处理,而不是多次将设备置于活动状态?比如请求的接口,部分接口是否可以合并为一个


7.网络优化

7.1.HTTPDNS

HTTPDNS是面向多端应用(移动端APP,PC客户端应用)的域名解析服务,解析请求基于HTTP(S)协议,有效解决了传统域名解析容易被劫持、解析不准确、更新不及时、服务不稳定等问题。

阿里HTTPDNS入口:HTTPDNS_域名解析_域名防劫持_开发与运维-阿里云

7.2.常见的网络优化方案

        -- 连接优化:keep-alive = true 保持长链接,复用socket,减少重新建立连接的开销

                HTTP1只能复用一个,HTTP2可多路复用,OKHTTP3支持HTTP2

        -- 数据压缩:可以用protobuf数据格式传输数据;开启gzip数据压缩 content-encoding = gzip

        -- 使用webp代替png/jpg

        -- 不同网络的不同图片下发:如原图(300*300)

                2/3g使用低清晰度图片:100*100

                4G判断信号强度,强:300*300,中:200*200;弱:100*100

                WiFi:300*300

        -- HTTP开启缓存:如首页数据加入缓存

DoraemonKit模拟弱网环境工具


8.APK瘦身

        -- 开启代码混淆 minifyEnabled = true;开启后不止是混淆了代码,未使用的代码不会打进包里

        -- 开启资源混淆 shrinkResources = true;开启后,未用的资源不会打进包里

        -- Lint 检测未用资源

        -- productFlavors 分渠道打包

        -- 开启split abi 拆分机制

                splits {
                    abi {
                        enable true
                        reset()
                        include 'arm64-v8a','armeabi-v7a'
                        universalApk true
                     }
                }

        -- 小图标使用矢量图,阿里矢量图库 iconfont-阿里巴巴矢量图标库

        -- 添加lite版的依赖;如protobuf-lite

        -- 分模块引入依赖

        -- 插件化加载不常用的模块

        -- 资源混淆;AndResGuard


9.ANR问题分析

9.1.ANR类型
        9.1.1.KeyDispatchTimeout(常见)

        input事件在5S内没有处理完成发生了ANR

        logcat日志关键字:Input event dispatching timed out

        9.1.2.BroadcastTimeout

        前台Broadcast:onReceiver在10s内没有处理完成发生ANR

        后台Broadcast: onReceiver在60s内没有处理完成发生ANR

        logcat日志关键字:Timeout of broadcast BroadcastRecord

        9.1.3.Service Timeout

        前台Service:onCreate,onStart, onBind 等生命周期在20s内没有处理完成发生ANR

        后台Service:同上在200s内没有处理完成发生ANR

        logcat日志关键字:Timeout executing service

        9.1.4.ContentProvider Timeout

        ContentProvider在10s内没有处理完成发生ANR

        logcat日志关键字:timeout publishing content providers

9.2.常见出现ANR的场景

        -- 主线程频繁进行耗时的IO操作,如数据库写入

        -- 多线程操作的死锁,主线程被block

        -- 主线程被Binder对端block

        -- System Server中WatchDog出现ANR

        -- service Binder的连接达到上限无法和System server 通信

        -- 系统资源耗尽(管道、CPU、IO)

9.3.ANR问题解决方法

/data/anr/trace_*.txt ANR日志路径

先 Cmd line:包名,找到我们的应用

首先看是否是资源耗尽、IO阻塞导致的ANR

死锁导致的可搜索:held by关键字

应用层的ANR可能在main调用链里面指定出现ANR的位置

9.4.自己实现ANR监控方案
        9.4.1.FileObserver:

        监控某个目录/文件,状态发生改变,创建,删除文件

        监控 data/anr,是否有创建,有的话就表示有ANR发生,可上传trace文件,但不一定是我们          的APP发生了ANR。

        6.0之后就被禁止了,可通过手机厂商更改配置打开监听

        9.4.2.WatchDog方案

        WatchDog是个开源的框架,GitHub地址:https://github.com/SalomonBrys/ANR-WatchDog
        原理:启动一个异步线程,在while循环中,使用主线程的Handler发送一个消息,线程休眠            指定的时间5s,当线程唤醒之后,如果发送的消息还没被主线程执行,即认为主线程发生了           卡顿。

        优点兼容性好,非侵入式

        缺点:检测并不准确


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值