android性能优化总结

1,UI优化:这篇文章总结的不错

2,内存泄漏优化

常见的几种形式:

资源对象没关闭造成的内存泄漏:

资源对象没关闭造成的内存泄漏 资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。因为有些资源性对象,比如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。

构造Adapter时,没有使用缓存的convertView:

以构造ListView的BaseAdapter为例,在BaseAdapter中提供了方法: public View getView(int position, ViewconvertView, ViewGroup parent) 来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大

Bitmap对象不在使用时调用recycle()释放内存:

有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不在被使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存 如何解决bitmap带给我们的内存问题 1. 及时销毁,在用完Bitmap时,要及时的recycle掉。 2. 设置一定的采样率,有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存 3. 巧妙的运用软引用,有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。

试着使用关于application的context来替代和activity相关的 context:

不要对activity 的context 长期引用( 一个activity 的引用的生存周期应该和activity 的生命周期相同) · 试着使用关于application的 context 来替代和activity相关的context · 如果一个acitivity 的非静态内部类的生命周期不受控制,那么避免使用它;使用一个静态的内部类并且对其中的activity 使用一个弱引用。解决这个问题的方法是使用一个静态的内部类,并且对它的外部类有一WeakReference,就像在ViewRoot中内部类W所做的就是这么个例子。 · 垃圾回收器不能处理内存泄漏的保障。 - 注册没取消造成的内存泄漏 所以建议:register放到onCreate,且onDestroy里无需做判断,直接unregister。目的是注册/反注册在相反的生命周期中处理(onCreate->onDestroy、onStart->onStop),同时,严格保证一定能反注册。

集合中对象没清理造成的内存泄漏

我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。 - static的不合理应用导致的内存泄漏 public class ClassName {

private static Context mContext; //省略 } 以上的代码是很危险的,如果将Activity赋值到么mContext的话。那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放. 如何才能有效的避免这种引用的发生呢? 第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。 第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。 第三、使用WeakReference代替强引用。比如可以使用WeakReferencemContextRef;

线程惹的祸

线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控

简单介绍LeakCanary原理

2.1,注意只支持android4.0以上

2.2,ActivityRefWatcher中,注册Activity生命周期监听接口,当Activity onDestroy()被调用时,将当前Activity加入内存泄漏监听队列;

2.3,WeakReference和ReferenceQueue配合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列

2.4,检测方法就很简单了,主动GC,触发WeakReference被GC,同时检测GC前后,ReferenceQueue是否包含被监听对象;如果不包含,则说明该对象没有被GC,一定存在到GC Roots的强引用链,也就是发生了泄漏

3,减小apk的大小优化

3.1,理解apk的结构

在讨论怎样减少应用大小之前,先了解APK的结构是有用的。一个APK文件就是ZIP包,其中包含了组成你的应用的所有文件,比如Java类文件,资源文件,和一个包含被编译资源的文件。 一个APK包含了一下目录: - META-INF/: 包含CERT.SF和CERT.RSA签名文件,也包含了MANIFEST.MF文件。(译注:校验这个APK是否被人改动过) - assets/: 包含了应用的资源,这些资源能够通过AssetManager对象获得。 - res/: 包含了没被被编译到resources.arsc的资源。 - lib/: 包含了针对处理器层面的被编译的代码。这个目录针对每个平台类型都有一个子目录,比如armeabi, armeabi-v7a, arm64-v8a, x86, x86_64和mips。

一个APK也包含了一下文件,其中只有AndroidManifest.XML是强制的: - resources.arsc: 包含了被编译的资源。该文件包含了res/values目录的所有配置的XML内容。打包工具将XML内容编译成二进制形式并压缩。这些内容包含了语言字符串和styles,还包含了那些内容虽然不直接存储在resources.arsc文件中,但是给定了该内容的路径,比如布局文件和图片。 - classes.dex: 包含了能被Dalvik/Art虚拟机理解的DEX文件格式的类。 - AndroidManifest.xml: 包含了主要的Android配置文件。这个文件列出了应用名称、版本、访问权限、引用的库文件。该文件使用二进制XML格式存储。(译注:该文件还能看到应用的minSdkVersion, targetSdkVersion等信息)

3.2,如何减小apk大小

3.2.1,移除不使用的资源

lint是Android Studio中的一个静态代码分析工具,检测在”res/“目录中你的代码没有引用的资源。当lint工具发现了项目中潜在的未使用的资源,它会打印以下类似信息: res/layout/preferences.xml: Warning: The resource R.layout.preferences appears to be unused [UnusedResources] 被引用的库中可能会包含没使用的资源。如果你在build.gradle文件中启用shrinkResources,则Gradle能自动移除这些资源。

为了使用shrinkResources,你必须要启用代码混淆。在构建过程中,首先proguard移除了未使用的代码,然后gradle移除未使用的资源。 在Gradle插件0.7或更高版本,你能申明应用支持的配置。Gradle通过传递resConfigs和defaultConfig给构建系统,构建系统会防止不支持的配置出现在APK中,从而减少APK大小。

3.2.2,最小化第三方库中资源的使用

当开发Android应用时,你经常使用第三方库提升应用的可用性和灵活性。比如,你引用Android Support Library提升旧设备的用户体验,或者使用Google Play服务实现文字自动翻译。

如果一个第三方库原本是为服务器或普通电脑设计,会引入许多不需要的对象和方法。为了只引入应用需要的库中的那部分,你可以编辑库文件(如果库的license允许你这么做)。你也能使用另外的针对手机的实现同样功能的库。 注意:代码混淆能清除库中不被使用的代码,但是他不能移除库的大量内部依赖。

3.2.3,只支持部分屏幕密度

Android支持很多设备集,其中包含了各种不同的屏幕密度。在Android 4.4及更高版本,框架支持不同的密度:ldpi, mdpi, tvdpi, hdpi, xhdpi, xxhdpi和xxxhdpi。尽管Android支持所有这些屏幕密度,但你不需要为每个密度都配置相应的资源。

如果你知道某种特定屏幕密度已经很少有用户使用了,那么你可以考虑是否需要为这个屏幕密度配置资源。如果你不包含针对特定屏幕密度的资源,那么Android会自动缩放原本针对其他密度的已有资源。

如果你的应用只需要缩放的图片,你甚至可以把图片存放在drawable-nodpi目录,从而节省更多空间。我们推荐每个应用都应该至少包含xxhdpi的图片。

3.2.4,减少动画帧数

使用帧动画会大大增加APK的大小。图1显示了目录中构成帧动画的多个PNG文件。每个图片都是动画的一帧。

对于加入动画的每帧,你都增加了APK中图片的个数。图1中,帧动画的帧率是30 FPS。如果帧率降到15 FPS,图片数量将减少一半。

3.2.5,使用drawable对象

一些图片不需要静态的图片资源,框架能在运行时动态地绘制图像。Drawable对象(XML的)只需要占用APK中的一点空间。另外,XML形式的Drawable对象能够产生遵循Material Design设计规范的图像。

3.2.6,重(chong第二声)用资源

你能包含一张图片的很多变种,比如染色、阴影、旋转的版本。但是,我们推荐在运行时复用一张图片来定制化他们。

Android提供了很多方式改变资源的颜色。对于Android 5.0及以上,使用android:tint和tintMode属性。对于更低版本,使用ColorFilter类。

你也能够删除那些只是对另一个资源做旋转的资源。下面的代码片段提供了对一个箭头旋转180度。 arrowexpand" android:fromdegrees="180" android:pivotx="50%" android:pivoty="50%" android:todegrees="180">

3.2.7,通过代码绘制

你也能通过代码绘制图像,从而减少APK大小。代码方式绘制图像不需要任何空间因为你不再需要在APK中存储图像文件。

3.2.8,压缩PNG文件

AAPT工具能够在构建过程中通过无损压缩优化res/drawable/中的图片资源。比如aapt工具能将需要颜色少于256色的PNG变为8位PNG图,这样能够在保证图片质量的同时减少内存使用。

需要注意aapt有以下局限性: - aapt工具不会压缩asset目录的PNG文件。 - 通过aapt的优化,图片文件会使用少于256色。 - aapt工具可能会影响已经被压缩过的PNG文件。为了防止这种情况,你可以在gradle文件中设置cruncherEnabled为false禁用aapt对PNG的压缩。 aaptOptions { cruncherEnabled = false } 译注:建议把cruncherEnabled设为false,然后通过tinypng手工压缩PNG图片。

3.2.9,压缩PNG和JPEG文件

你能使用一些工具(比如pngcrush, pngquant, zopflipng)在不降低图像质量的前提下减少PNG文件大小。所有这些工具都能保留图像质量的情况下减少PNG文件大小。

pngcrush工具特别有效:这个工具通过迭代png过滤器和zlib参数,使用每种过滤器和参数的组合压缩图像,并选择最小的那个作为最后的输出。

对于JPEG文件,能使用packJPG压缩JPEG文件。 译注:guetzli是Google最近推出的JPEG编码器,官方宣称相同图片质量时,比libjpeg生成的图片小20-30%。

3.2.10,使用WebP文件格式

你也能使用WebP文件格式存储图片而不是PNG或者JPEG。WebP格式是有损压缩(像JPEG)且有透明通道(像PNG),且压缩率高于JPEG或PNG。

使用WebP文件格式也有一些缺点。第一,低于Android 3.2的版本不支持WebP,第二,WebP的解码时间比PNG长。

3.2.11,使用向量图

你能使用向量图去创建一个分辨率无关的图标。使用向量图能够显著减少APK大小。在Android中向量图是以VectorDrawable对象形式存在的。使用VectorDrawable对象,一个100B的文件能生成一个屏幕大小的清晰图片。

但是,系统需要很长时间渲染VectorDrawable对象,更大的图片需要更长的时间显示在屏幕上。因此只有小图片才考虑使用向量图。

3.2.12减少Native和Java代码

确保理解任何自动生成的代码。比如,许多protocol buffer工具生成了过多的方法和类,这会让你的应用大小翻倍。

3.2.13,移除调试符号

如果应用在开发中并且仍需要调试,那么我们能理解使用调试符号。使用Android NDK提供的arm-eabi-strip工具,能从Native库中删除不必要的调试符号,之后你再编译release包

3.2.14,避免抽取Native库

在APK中存储未压缩的so文件,并且在Manifest文件的中设置android:extractNativeLibs为false,这会防止在安装时PackageManager将APK中的so文件拷贝到文件系统,避免这种拷贝会让应用在做增量更新时的更新包更小。

3.2.15,维持多个小的APK包

你的APK会包含用户下载了但从未使用的内容,比如地区或语言信息(译注:比如我是中国人,我就不会用到其他语种的资源)。为了给用户创建小的下载包,你能把你的应用拆分成多个APK,这些APK的差别在于一些因素(比如屏幕大小或者GPU纹理支持)。

当一个用户下载了应用,设备根据自身的特性和设置获取正确的APK。这种方式能够让设备不获取设备不需要的资源。比如,如果设备是hdpi的,那么他就不需要xxxhdpi的资源。

其他优化后面记录,这里推荐另一个优化系列,写的很细致

你可能感兴趣的:(android性能优化总结)