『一篇就够了系列』Android App优化知识点全收藏

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版,欢迎购买。

书籍详情请见:https://blog.csdn.net/ddnosh/article/details/107666187

书籍购买地址:京东 当当 天猫

图片名称


欢迎加入Android开发交流QQ群:
Android开发技术交流

1. 【布局优化】

原理

60fps
cpu和gpu工作协调:CPU处理逻辑运算,GPU处理浮点运算;

检测方法

系统设置–开发者选项–调试GPU过度绘制
无色:没有过度绘制,每个像素绘制了 1 次。
蓝色:每个像素多绘制了 1 次。大片的蓝色可以接受,如果整个窗口是蓝色的,可以尝试优化减少一次 绘制。
绿色:每个像素多绘制了 2 次。
淡红:每个像素多绘制了 3 次。一般来说,这个区域不超过屏幕的 1/4 是可以接受的。
深红:每个像素多绘制了 4 次或者更多。严重影响性能,需要优化,避免深红色区域。

优化方法

减少GPU过度绘制

如非需要,不建议设置activity的背景;

减少CPU工作

布局优化

  1. include、merge、ViewStub
  2. ConstraintLayout 降低层级
  3. RelativeLayout和LinearLayout的选择:
    RelativeLayout层级少,但是子View会调用两次onMeasure;LinearLayout层级多,会增加内存,并且如果使用weight属性也会调用两次onMeasure。在不影响层级深度的情况下,使用LinearLayout和FrameLayout而不是RelativeLayout.
  4. SurfaceView 或 TextureView

工具

  1. uiautomator.bat
    分析xml布局方式;
  2. monitor.bat
    绿: 表示该 View 的此项性能比该 View Tree 中超过 50% 的 View 都要快;例如,代表Measure 的是绿点,意味着这个视图的测量时间快于树中的视图对象的 50%。
    黄: 表示该 View 的此项性能比该 View Tree 中超过 50% 的 View 都要慢;
    红: 表示该 View 的此项性能是 View Tree 中最慢的;

2. 【内存管理】

内存模型

JVM:方法区、虚拟机栈、本地方法栈、堆、程序计数器

内存抖动

内存分配速度大于回收速度。
比如拼接string字符串,换成stringbuffer或stringbuilder。

回收算法

标记清除算法 Mark-Sweep
复制算法 Copying
标记压缩算法 Mark-Compact
分代收集算法

工具

  1. profiler
  2. MemoryAnalyzer
  3. leakcanary

解决内存泄漏的方法

  1. 单例
  2. handler
  3. 静态变量
  4. 匿名内部类
  5. 注册与取消注册
  6. 定时任务
  7. 资源关闭
  8. 属性动画
  9. webview内存泄漏(开个新进程)
  10. 其它注意事项:
    基本数据类型用static final 替换static;

使用for循环还是迭代器Iterator对比:
采用ArrayList对随机访问比较快,而for循环中的get()方法,采用的即是随机访问的方法,因此在ArrayList里,for循环较快

    采用LinkedList则是顺序访问比较快,iterator中的next()方法,采用的即是顺序访问的方法,因此在LinkedList里,使用iterator较快

从数据结构角度分析,for循环适合访问顺序结构,可以根据下标快速获取指定元素.而Iterator 适合访问链式结构,因为迭代器是通过next()和Pre()来定位的.可以访问没有顺序的集合.

    而使用 Iterator 的好处在于可以使用相同方式去遍历集合中元素,而不用考虑集合类的内部实现(只要它实现了 java.lang.Iterable 接口),如果使用 Iterator 来遍历集合中元素,一旦不再使用 List 转而使用 Set 来组织数据,那遍历元素的代码不用做任何修改,如果使用 for 来遍历,那所有遍历此集合的算法都得做相应调整,因为List有序,Set无序,结构不同,他们的访问算法也不一样.(还是说明了一点遍历和集合本身分离了)

3. 【网络优化】

解决方案

  1. API接口设计:多个api得到的数据合并一个api得到;
  2. GZip压缩request和response;
  3. Protocol Buffer代替json;
  4. 获取图片的URL告知服务器所需图片的宽高;
    比如根据不同网络条件返回不同大小图片;采用webp;
  5. 大文件下载断点续传;
  6. 增量包:bsdiff和bspatch;
  7. 网络请求的数据进行缓存;
    比如将网络请求得到的数据放入数据库,每次加载页面先从数据库获得,等获取到网络数据后更新UI和数据库;
  8. JobScheduler在wifi情况下进行下载任务,比如广告等;
    5.0后用JobScheduler,8.0后用WorkManager。
  9. webview加载,涉及到前端html页面优化;

专项:弱网优化

先缓存请求,等网络情况好时再通过JobScheduler发送请求。比如点赞。
通过OkHttpClient配置cache,当手机没有联网的时候,就可以直接从缓存中加载数据。还可以设置读、写、连接超时。

4. 【应用瘦身】

  1. AndResGuard资源文件瘦身;
  2. lint去除无用资源:Analyze > Run Inspection By Name > unused resources;
  3. proguard:开启 minifyEnabled 混淆代码,可以压缩文件,使用 shrinkResources 去除无用资源;
  4. 图片:
    使用svg格式(xml格式);
    tinypng压缩;
    webp格式;
  5. 插件化
  6. 只保留一个cpu架构的so文件:armeabi-v7a
  7. 去除多语言;
  8. 去除第三方库,比如rxjava等;

5. 【启动优化】

1. 启动黑白屏

1. 方法1:自定义背景图 + 全屏
黑白屏是因为系统在创建进程的时候
我们首先想到的方案是用一张图片代替黑白颜色,并且为了更好的效果可以设置为全屏显示。
首先定义一个BaseTheme:
··· xml

<style name="BaseTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

···
然后定义一个启动页的style,继承自BaseTheme。

<style name="SplashTheme" parent="AppTheme">
        <item name="android:windowFullscreen">true</item> // 全屏
        <item name="android:windowBackground">@mipmap/splash</item> //图片
</style>

这个方案的缺点是从全屏到非全屏页面(如MainActivity)会有一个抖动的过程。
2. 方法2:透明 + 禁用窗口预览动画

<style name="SplashTheme" parent="AppTheme">
        <item name="android:windowDisablePreview">true</item>  //禁用窗口预览动画
 		<item name="android:windowBackground">@null</item> //不设置背景
</style>

这种方案适用于启动页无需耗时的场景,如果启动需要一段时间的话,这段时间对用户来说会以为点击了桌面图标后会出现没有反应,体验不好。
3. 方法3(推荐):自定义背景图+透明状态栏
综合方法1和方法2的缺点,我们提出一种方案,那就是通过自定义背景图片+将系统状态栏透明化的方案。

    <style name="SplashTheme" parent="AppTheme">
        <item name="android:windowTranslucentStatus">false</item> //<!-- 将状态栏透明化,设置为true,状态栏有阴影,false则无阴影 -->
        <item name="android:statusBarColor">@android:color/transparent</item> //设置状态栏颜色为透明色
        <item name="android:windowBackground">@mipmap/splash</item>
    </style>

这种方案既不会产生抖动问题,也不会点击桌面图标后出现的无反应场景。

2. 启动耗时检测

启动分为冷启动和热启动。
冷启动就是进程被杀,或者第一次启动;
热启动就是Activity被回收,但是Application仍存在,无需重新创建,对应的进程还在。
1. 命令行
命令行:adb shell am start -S -R 3 -W com.androidwind.androidquick.sample/.SplashActivity
-S表示每次启动前先强行停止,-R表示重复测试次数。
运行结果如下:

Stopping: com.androidwind.androidquick.sample
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.androidwind.androidquick.sample/.SplashActivity }
Status: ok
Activity: com.androidwind.androidquick.sample/.SplashActivity
ThisTime: 2851
TotalTime: 2851
WaitTime: 2894
Complete

缺点:
1. 某个Activity可能需要经过多个Activity跳转后才能到达,需要叠加这些Activity启动的时间;
2. 统计时间不准确,比如只计算了Activity的启动和初始化时间,并未统计等待时间;
3. 统计的Activity需要设置或者android:exported=“true”;

2. 写一个计时工具TimeUtils
主要区分热启动和冷启动的耗时算法。
冷启动 = application启动时间 + 热启动时间;
热启动指Activity的启动时间。
3. 第三方工具nimbledroid
https://nimbledroid.com/

3. 优化方案

App加载过程比较复杂,我们客户端开发中能够控制的优化点是Application和Activity。

懒加载

  1. Application:SDK
    尽量将第三方的SDK进行延迟初始化,用到的时候再去加载。
  2. Application:static变量
    比如一些static对象,如果初始化时间较长,而启动时暂未用到,可以考虑放到后续第一次使用的时候初始化。
  3. Activity:viewstub
    ViewStub在使用到的时候才会加载。
    总之目的就是减少View层级,减少View绘制时间。
    比如使用ConstraintLayout。
  4. Activity:Fragment
    lazyfragment

开启子线程处理

Thread、ThreadPool、AsyncTask、IntentService、HandlerThread、RxJava等。
注意子线程不能阻塞主线程,比如sharedpreferences加载大容量sd卡文件则会造成主线程阻塞。Thread的join和FutureTask的get也会阻塞主线程。

6. 【电量优化】

工具

Battery-Historian

耗电定位

CPU 处理
定位
网络
图像

优化

优化的途径主要体现在以下几个方面:

  1. 尽可能降低 CPU、GPU 的功耗。尽量少用 定时器。优化 I/O 操作。ART替换Dalvik,减少CPU工作,减少空间,节省电量

不要频繁写入小数据,而是积攒到一定数量再写入
读写大量的数据可以使用 Dispatch_io ,GCD 内部已经做了优化。
数据量比较大时,建议使用数据库

  1. 网络方面的优化
    减少压缩网络数据 (XML -> JSON -> ProtoBuf),如果可能建议使用 ProtoBuf。
    如果请求的返回数据相同,可以使用 NSCache 进行缓存
    使用断点续传,避免因网络失败后要重新下载。
    网络不可用的时候,不尝试进行网络请求
    长时间的网络请求,要提供可以取消的操作
    采取批量传输。下载视频流的时候,尽量一大块一大块的进行下载,广告可以一次下载多个

定位层面的优化

如果只是需要快速确定用户位置,最好用 CLLocationManager 的 requestLocation 方法。定位完成后,会自动让定位硬件断电
如果不是导航应用,尽量不要实时更新位置,定位完毕就关掉定位服务
尽量降低定位精度,比如尽量不要使用精度最高的 kCLLocationAccuracyBest
需要后台定位时,尽量设置 pausesLocationUpdatesAutomatically 为 YES,如果用户不太可能移动的时候系统会自动暂停位置更新
尽量不要使用 startMonitoringSignificantLocationChanges,优先考虑 startMonitoringForRegion:

硬件检测优化

用户移动、摇晃、倾斜设备时,会产生动作(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不需要检测的场合,应该及时关闭这些硬件

其它

获取电量报告、分析;
wakelock尽量不用;
JobScheduler 替换 Service,在连接上wifi或者充电时运行这些任务。

7.【图片优化】

  1. ARGB_8888系统默认,inSampleSize采样率;
  2. 图片尽量放到高密度目录中,这样方法倍数是1以下;
  3. png带有A通道,可以用tinypng等工具压缩后再使用;
  4. 使用webp;
  5. 使用.9图片;
  6. 使用图片框架,如Glide;

8. 【代码优化】

  1. 代码重构;
  2. 架构优化:组件化、插件化;

欢迎关注我的技术公众号:国民程序员,我们的目标:输出干货

  1. 每天分享原创技术文章
  2. 海量免费技术资料和视频学习资源
  3. 分享赚钱门道,带领程序员走向财务自由
图片名称

猜你喜欢

转载自blog.csdn.net/ddnosh/article/details/108468843