前述
一个好的app除了有吸引人的功能, 美丽的交互之外,性能也至关重要,作为一个技术人员,在这里当然只能讲技术了
一般app性能优化主要从一下几个方面入手,
- 启动速度优化
- app卡顿,流畅度优化
- 内存优化
- 代码优化
- apk瘦身优化
- 电量优化
- 稳定性优化
启动速度优化
对于app启动的优化,一般启动的时候设置一个主题/图片防止白屏或者延迟打开
<style name="appTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowBackground">@drawable/splash</item>
<item name="android:windowFullscreen">true</item>
</style>
如果启动页面不是一张图片,而且通过布局显示可以利用Splash加载部分图片先显示出来
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--白色矩形 作为背景色-->
<item>
<shape android:shape="rectangle">
<solid android:color="@android:color/white" />
</shape>
</item>
<!--单独的slogan图片 并且设置下间距-->
<item android:bottom="0dp">
<!--位置设置成靠下-->
<bitmap
android:gravity="bottom"
android:src="@mipmap/bg_load_button" />
</item>
</layer-list>
在5.0后,增加了一个windowDrawsSystemBarBackgrounds属性,用来标志此窗口是否负责绘制系统栏背景,我们把它设成false
,这样当它绘制windowBackground
的时候,就会在NavigationBar
之上。
因为这个属性是5.0以后才有的,所以需要新建values-v21
文件夹,以便5.0以上的机器使用v21的Splash主题。
针对启动速度慢,需要尽可能减少Applicatio和启动页面的onCreate中所要做的事情,比如一些不重要的SDK延迟或者异步加载;部分第三方的初始化(如极光的初始化就很耗时)可以使用到的类异步加载;如果对于设备5.0以下,还要考虑multidex的优化.提高启动页面响应速度,一般点击app进入到首页不能超过3秒,如果超过3秒就应该要做处理了.
对于如何检测方法耗时,除了打印log,adb命令可以使用TraceView去看,后面我们会讲到
卡顿优化
谈到UI流畅度,一般就是不要在主进程去做耗时的操作,提升UI的绘制速度,下面几点需要注意
-
不要在主线程进行网络访问/大文件的IO操作
-
绘制UI时,尽量减少绘制UI层次;减少不必要的view嵌套,减少重复绘制,可以用Hierarchy Viewer工具来检测,后面会详细讲;
-
当我们的布局是用的FrameLayout的时候,我们可以把它改成merge,可以避免自己的帧布局和系统的ContentFrameLayout帧布局重叠造成重复计算(measure和layout)
参考链接
-
提高显示速度,使用ViewStub:当加载的时候才会占用。不加载的时候就是隐藏的,仅仅占用位置。
-
在view层级相同的情况下,尽量使用 LinerLayout而不是RelativeLayout;因为RelativeLayout在测量的时候会测量二次,而LinerLayout测量一次,可以看下它们的源码;
-
删除控件中无用的属性;
-
布局复用.比如listView 布局复用
-
尽量避免过度绘制(overdraw)比如:背景经常容易造成过度绘制。由于我们布局设置了背景,同时用到的MaterialDesign的主题会默认给一个背景。这时应该把主题添加的背景去掉;还有移除 XML 中非必须的背景
-
自定义View优化。使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。也是避免过度绘制.
-
合理的刷新机制,尽量减少刷新次数,尽量避免后台有高的 CPU 线程运行,缩小刷新区域。
-
采用开销较低的布局:例如,您可能会发现,TableLayout 作为具有许多位置依赖项的更复杂的布局,可以提供相同的功能。在 Android 的 N 版本中,ConstraintLayout 类提供了与 RelativeLayout 类似的功能,但开销要低得多。
性能问题并不容易复现,也不好定位,下面介绍几个非常有用的工具分析卡顿
-
1.Profile GPU Rendering
image.png
在手机开发者模式下,有一个卡顿检测工具叫做:Profile GPU Rendering
下面用一张图来解析各个颜色对应的意思(特别需要注意的我红色框了出来)
image.png
总之,保持动画流畅的关键就在于让这些垂直的柱状条尽可能地保持在绿线下面,任何时候超过绿线,你就有可能丢失一帧的内容. -
2.Debug GPU overDraw过度绘制检测
image.png
在手机开发者模式下,有一个过度绘制检测工具叫做:Debug GPU overDraw,看图:
各个颜色代表的意义
Android 将按如下方式为界面元素着色,以确定过度绘制的次数:
真彩色:没有过度绘制
蓝色:过度绘制 1 次
绿色:过度绘制 2 次
粉色:过度绘制 3 次
红色:过度绘制 4 次或更多次
总之,尽量避免过度绘制,常见的就是设置了多个背景颜色造成
内存优化
在开发的过程,如果方法不当的话,很容易造成内存泄漏,接下来,来说一下哪些情景容易出现内存泄漏。
内存泄漏出现的情景
-
单例中引用的上下文Context,引用了Activity中的Context, 这样会造成内存泄漏,要引用Application中的Context;()参考文章
-
尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。
-
资源性对象未关闭。比如Cursor、File文件等,往往都用了一些缓冲,在不使用时,应该及时关闭它们。参考文章
-
注册对象未注销。比如事件注册后未注销,会导致观察者列表中维持着对象的引用。类的静态变量持有大数据对象。例如EventBus或者RxJava
-
非静态内部类的静态实例。参考文章
public class LayoutPerActivity extends Activity {
private static TestModule mTestModule = null;
@override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getWindow().setBackgroundDrawable(null);
setContentView(R.layout.activity_layout_per);
if(null == mTestModule) {
mTestModule = new TestModule(this);
}
}
class TestModule{
private Context mContext = null;
public TestModule(Context ctx) {
mContext = ctx;
}
}
}
-
Handler临时性内存泄漏。如果Handler是非静态的,容易导致 Activity 或 Service 不会被回收,或者可以使用弱引用持有对Activity的引用。
-
容器中的对象没清理造成的内存泄漏。
-
WebView。WebView 存在着内存泄漏的问题,在应用中只要使用一次 WebView,内存就不会被释放掉。参考资料
内存优化的方案
- 对象引用。强引用、软引用、弱引用、虚引用四种引用类型,根据业务需求合理使用不同,选择不同的引用类型。
- 减少不必要的内存开销。注意自动装箱,增加内存复用,比如有效利用系统自带的资源、视图复用、对象池、Bitmap对象的复用。
- 使用最优的数据类型。比如针对数据类容器结构,可以使用ArrayMap数据结构,避免使用枚举类型,使用缓存Lrucache等等。
- 图片内存优化。可以设置位图规格,根据采样因子做压缩,用一些图片缓存方式对图片进行管理等等。
除此之外,内存泄漏可监控, 推荐square公司开源的Leakcanary可以在发生内存泄漏时告警,并且生成 leak tarce 分析泄漏位置,同时可以提供 Dump 文件进行分析。
内存分析工具
以下介绍几种内存分析工具
-
Memory Monitor
Memory Monitor 是一款使用非常简单的图形化工具,可以很好地监控系统或应用的内存使用情况.
主要有以下功能:
(1).显示可用和已用内存,并且以时间为维度实时反应内存分配和回收情况。
(2).快速判断应用程序的运行缓慢是否由于过度的内存回收导致。
(3).快速判断应用是否由于内存不足导致程序崩溃。
- Allocation Tracker
Memory Monitor 和 Heap Viewer 都可以很直观且实时地监控内存使用情况,还能发现内存问题,但发现内存问题后不能再进一步找到原因,或者发现一块异常内存,但不能区别是否正常,同时在发现问题后,也不能定位到具体的类和方法。这时就需要使用另一个内存分析工具 Allocation Tracker,进行更详细的分析, Allocation Tracker 可以分配跟踪记录应用程序的内存分配,并列出了它们的调用堆栈,可以查看所有对象内存分配的周期。
- Memory Analyzer Tool(MAT)
MAT 是一个快速,功能丰富的 Java Heap 分析工具,通过分析 Java 进程的内存快照 HPROF 分析,从众多的对象中分析,快速计算出在内存中对象占用的大小,查看哪些对象不能被垃圾收集器回收,并可以通过视图直观地查看可能造成这种结果的对象。
代码优化
关于代码优化,有这么一个很好用的工具Android lint
在顶部菜单栏找到Analyze选项,在弹框中选中Inspect Code,或者对项目根双击弹出菜单弹框(windows下右键项目根目录),找到Analyze选项再选中Inspect Code选项
image.png
Correctness:不够完美的编码,比如硬编码、使用过时 API 等
Performance:对性能有影响的编码,比如:静态引用,循环引用等
Internationalization:国际化,直接使用汉字,没有使用资源引用等
Security:不安全的编码,比如在 WebView 中允许使用 JavaScriptInterface
Remove All Unused Resources的选项,先全部清理,考虑反射机制,不能删除资源
Handler Reference leaks,当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)。而Handler通常会伴随着一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕(例如图片下载完毕)之后,通过消息机制通知Handler,然后Handler把图片更新到界面。然而,如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,如果你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。这个问题也是较常见的可能导致内存泄漏的问题,解决方法一般是通过弱引用持有对Activity的引用
Android代码优化——Layout Inspector
当您的布局在运行时构建而不是完全在XML布局中定义时,这尤其有用
Layout Inspector主要用分析布局的层级结构,减少不必要的层级,避免overdraw, 达到渲染优化的效果。Layout Inspector虽然界面不如HierarchyView直观,但是提供的信息也足够详细,分析布局层级绝对够用了。
Android代码优化---traceview使用
Debug.startMethodTracing(“hello”); Debug.stopMethodTracing();
耗电优化
在移动设备中,电池的重要性不言而喻,没有电什么都干不成。对于操作系统和设备开发商来说,耗电优化一致没有停止,去追求更长的待机时间,而对于一款应用来说,并不是可以忽略电量使用问题,特别是那些被归为“电池杀手”的应用,最终的结果是被卸载。因此,应用开发者在实现需求的同时,需要尽量减少电量的消耗。 耗电的原因其实很多,这里我就讲一下几种优化方案,优化方案的反面就是他的原因了,几种优化方案如下:
-
合理的使用wack_lock锁,wake_lock锁主要是相对系统的休眠(这里就是为了省电,才做休)而言的,意思就是我的程序给CPU加了这个锁那系统就不会休眠了,这样做的目的是为了全力配合我们程序的运行。有的情况如果不这么做就会出现一些问题,比如微信等及时通讯的心跳包会在熄屏不久后停止网络访问等问题。所以微信里面是有大量使用到了wake_lock锁。这里有一篇关于wake_lock的使用,请查阅;
-
使用jobScheduler2,集中处理一些网络请求,有些不用很及时的处理可以放在充电的时候处理,比如,图片的处理,APP下载更新等等,这里有一篇关于jobScheduler的使用,请查阅;
-
计算优化,避开浮点运算等。
-
数据在网络上传输时,尽量压缩数据后再传输,建议用FlatBuffer序列化技术,这个比json效率高很多倍,不了解FlatBuffer,建议找资料学习一下,后面有时间的话,也会专门写关于FlatBuffer的文章.
andriod耗电分析所用到的工具
在 Android5.0 以前,在应用中测试电量消耗比较麻烦,也不准确,5.0 之后专门引入了一个获取设备上电量消耗信息的 API:Battery Historian。Battery Historian 是是一款图形化数据分析工具,直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况;
安装包大小优化
随着功能不断增加,APP的包肯定不会断的变大,但应用的安装包越大,用户下载的门槛越高,特别是在移动网络情况下,用户在下载应用时,对安装包大小的要求更高,因此,减小安装包大小可以让更多用户愿意下载和体验产品。所以,我们还是要想办法去如何去优化,尽量减小app的安排包.
APP包优化方案
-
res资源优化
(1)只尽量使用一套图片,使用高分辨率的图片。
(2)UI设计在ps安装TinyPNG插件,对图片进行无损压缩。
(3)svg图片:一些图片的描述,牺牲CPU的计算能力的,节省空间。使用的原则:简单的图标。
(4)图片使用WebP(developers.google.com/speed/webp/)的格式(Facebook、腾讯、淘宝在用。)缺点:加载相比于PNG要慢很多。 但是配置比较高。工具: isparta.github.io/
(5)使用tintcolor(android - Change drawable color programmatically)实现按钮反选效果。 -
代码优化
(1)实现功能模块的逻辑简化
(2)Lint工具检查无用文件将无用的资源列在“UnusedResources: Unused resources”,删除。
(3)移除无用的依赖库。 -
lib资源优化
(1)动态下载的资源。
(2)一些模块的插件化动态添加。
(3)so文件的剪裁和压缩。 -
assets资源优化
(1)音频文件最好使用有损压缩的格式,比如采用opus、mp3等格式,但是最好不要使用无损压缩的音乐格式
(2)对ttf字体文件压缩,可以采用FontCreator工具只提取出你需要的文字。比如在做日期显示时,其实只需要数字字体,但是使用原有的字体库可能需要10MB大小,如果只是把你需要的字体提取出来生成的字体文件只有10KB -
代码混淆。
使用proGuard 代码混淆器工具,它包括压缩、优化、混淆等功能。 -
7z极限压缩
具体请参考微信的安接包压缩,实现实现原理,有时间再分析;
稳定性优化
Android 应用的稳定性定义很宽泛,影响稳定性的原因很多,比如内存使用不合理、代码异常场景考虑不周全、代码逻辑不合理等,都会对应用的稳定性造成影响。其中最常见的两个场景是:Crash 和 ANR,这两个错误将会使得程序无法使用,比较常用的解决方式如下:
-
提高代码质量。比如开发期间的代码审核,看些代码设计逻辑,业务合理性等。
-
代码静态扫描工具。常见工具有Android Lint、Findbugs、Checkstyle、PMD等等。
-
Crash监控。把一些崩溃的信息,异常信息及时地记录下来,以便后续分析解决。
-
Crash上传机制。在Crash后,尽量先保存日志到本地,然后等下一次网络正常时再上传日志信息。
总结
其实app性能优化,不是一二天可以完成的,不断的提高开发的质量,发现了问题,就要及时的解决,不能推三拉四,这次就总结到这里,本人也是小白,希望能能够和大家一起学习.
网友评论