更多网络优化,可参考: Android网络
GL10.GL_MAX_TEXTURE_SIZE
AlarmManager
提供的闹钟服务来做,保证在系统休眠的时候cpu可以得到休眠,在需要唤醒时可以唤醒(持有cpu唤醒锁)使用具有缓存策略的输入流
原 | 建议替换为 |
---|---|
InputStream |
BufferedInputStream |
Reader |
BufferedReader |
如果已知大概需要多大,就直接给初始大小,减少扩容时额外开销。
ArrayList
: 里面就一数组,内存小,有序取值快,扩容效率低LinkedList
: 里面就一双向链表,内存大,随机插入删除快,扩容效率高。HashSet
: 里面就一个HashMap
,用key对外存储,目的就是不允许重复元素。ConcurrentHashMap
: 线程安全,采用细分锁,锁颗粒更小,并发性能更优Collections.synchronizedMap
: 线程安全,采用当前对象作为锁,颗粒较大,并发性能较差。SparseArray
、SparseBooleanArray
、SparseIntArray
: 针对Key为Int
、Boolean
进行了优化,采用二分法查找,简单数组存储。相比HashMap
而言,HashMap
每添加一个数据,大约会需要申请额外的32字节的数据,因此Sparsexxx
在内存方面的开销会小很多。String
,并且这个方法的返回值始终都是被用来append
到一个StringBuffer
上,就改为传入StringBuffer
直接append
上去,避免创建一个短生命周期的临时对象;2) 如果使用的字符串是截取自某一个字符串,就直接从那个字符串上面substring
,不要拷贝一份,因为通过substring
虽然创建了新的String
对象,但是共享了里面的char
数组中的char
对象,减少了这块对象的创建;量使用多个一维数组,其性能高于多维数组;int
数组性能远大于Integer
数组性能;static
,这样调用时可以提升15%~20%的速度,因为不需要切换对象状态。static final
,这样可以让Class
首次初始化时,不需要调用<clinit>
来创建static
方法,而是在编译时就直接将常量替换代码中使用的位置。get/set
访问成员变量,虽然这在语言的开发中是一个好的习惯,但是Android虚拟机中,对方法的调用开销远大于对变量的直接访问。在没有JIT的情况下,直接的变量访问比调用方法快3倍,在JIT下,直接的变量访问更是比调用方法快7倍!方法/变量
时,考虑将这些外部类的私有方法/变量
改用包可见的方式。首先在编写代码的时候,通过内部类访问外部类的私有方法/变量
是合法的,但是在编译的时候为了满足这个会将需要被内部类访问的私有方法/变量
封装一层包可见的方法,实现让内部类访问这些私有的方法/变量
,根据前面我们有提到说方法的调用开销大于变量的调用,因此这样使得性能变差,所以我们在编码的时候可以考虑直接将需要被内部类调用的外部类私有方法/变量
,改为包可见。float
。在很多现代设备中,double
的性能与float
的性能几乎没有差别,但是从大小上面double
是float
的两倍的大小。String.indexOf()
相关的API,Dalvik将会替换为内部方法;System.arraycopy()
方法在Nexus One手机上,会比我们上层写的类似方法的执行速度快9倍。Map map
),相比直接访问对象类(如HashMap map
),会慢6%,但是在存在JIT的设备中,两者的速度差不多。但是内存占用方面面向接口变成会消耗更多内存,因此如果你的面向接口编程不是十分的必要的情况下可以考虑不用。尽量使用
Iterable
而不是通过长度判断来进行遍历。
// 这种性能是最差的,JIT也无法对其优化。 public void zero() { int sum = 0; for (int i = 0; i < mArray.length; ++i) { sum += mArray[i].mSplat; } } // 相对zero()来说,这种写法会更快些,在存在JIT的情况下速度几乎和two()速度一样快。 public void one() { int sum = 0; // 1) 通过本地化变量,减少查询,在不存在JIT的手机下,优化较明显。 Foo[] localArray = mArray; // 2) 获取队列长度,减少每次遍历访问变量的长度,有效优化。 int len = localArray.length; for (int i = 0; i < len; ++i) { sum += localArray[i].mSplat; } } // 在无JIT的设备中,是最快的遍历方式,在存在JIT的设备中,与one()差不多快。 public void two() { int sum = 0; for (Foo a : mArray) { sum += a.mSplat; } }
建多索引的原则: 哪个字段可以最快的减少查询结果,就把该字段放在最前面
BETWEEN
、LIKE
、OR
CASE WHEN
"?", parameter
)代替字符串拼接(底层有特殊优化与缓存)Android JVM相关知识,可参看: ART、Dalvik
Android JNI、NDK相关知识,可参看: NDK
JNI不一定显得更快,有些会更慢。
特点: 不用在虚拟机的框子下写代码
一些重要的参数之类,也可以考虑放在Native层,保证安全性。参考: Android应用程序通用自动脱壳方法研究
360 17个进程: 360手机卫士 Android开发 InfoQ视频 总结 ,但是考虑到多进程的消耗,我们更需要关注多个组件复用同一进程。
在没有做任何操作的空进程而言,其大约需要额外暂用1.4MB的内存。
最后,多进程存在的两个问题: 1. 由于进程间通讯或者首次调起进程的消耗等,带来的cpu、i/o等的资源竞争。2. 也许对于部分同事来说,会还有可读性问题吧,毕竟多了层IPC绕了点。
相关深入优化,可参看Android绘制布局相关
对于卡顿相关排查推荐参看: Android性能优化案例研究(上)与Android性能优化案例研究(下)
可以参考Falcon Pro作者的推荐: Falcon Pro 3如何完成独立开发演讲分析
RxJava (响应式编程,代码更加简洁,异步处理更快快捷、异常处理更加彻底、数据管道理念)
相关了解可以参看: RxJava
Okhttp: 默认gzip、缓存、安全等
Retrofit: 非常好用的REST Client,结合RxJava简单API实现、类型安全,简单快捷
Realm: 效率极高(Falcon Pro 3的作者Joaquim用了该库以后,所有数据库操作都放到了UI线程)(基于TightDB,底层C++闭源,Java层开源,简单使用,性能远高于SQLite等)
Fabric: 全面的信息(新版本还支持JNI Crash获取和上报)、稳定的数据、及时的通知、强大的反混淆(其实在混淆后有上传mapping)
LeakCanary: 自动化泄漏检测与分析 ( 可以看看这个LeakCanary使用总结与Leakcanary Square的一款Android/Java内存泄漏检测工具)
根据设备可用内存的不同,每个设备给应用限定的Heap大小是有限的,当达到对应限定值还申请空间时,就会收到
OutOfMemoryError
的异常。
Android根据不同的进程优先级,对不同进程进行回收来满足内存的供求,可以参照这篇文章: Android中线程、进程与组件的关系。
在后台进程的LRU队列中,除了LRU为主要的规则以外,系统也会根据杀死一个后台进程所获得的内存是否更多作为一定的参考依据,因此后台进程为了保活,尽量少的内存,尽可能的释放内存也是十分必要的。
Service
的存活周期(可以考虑直接使用执行完任务直接关闭自己的IntentService
),也就是说在Service没有任何任务的时候,尽可能的将其关闭,以减少系统资源的浪费。ActivityManager
中的getMemoryClass()
获知当前设备允许每个应用大概可以有多少兆的内存使用(如果在AndroidManifest
设置了largeHeap=true
,使用getLargeMemoryClass()
获知),并且让应用中的内存始终低于这个值,避免OOM。Enum
枚举需要大于两倍的内存空间来存储相同的数据。class
(或者匿名类)大约占用500字节。onTrimMemory()
回调处理监听
onTrimMemory()
的回调,根据不同的内存等级,做相应的释放以此让系统资源更好的利用,以及自己的进程可以更好的保活。
TRIM_MEMORY_RUNNING_MODERATE
: 当前应用还在运行不会被杀,但是设备可运行的内存较低,系统正在从后台进程的LRU列表中杀死进程其他进程。TRIM_MEMORY_RUNNING_LOW
: 当前应用还在运行不会被杀,但是设备可运行内存很低了,会直接影响当前应用的性能,当前应用也需要考虑释放一些无用资源。TRIM_MEMORY_RUNNING_CRITICAL
: 当前应用还在运行中,但是系统已经杀死了后台进程LRU队列中绝大多数的进程了,当前应用需要考虑释放所有不重要的资源,否则很可能系统就会开始清理服务进程,可见进程等。也就说,如果内存依然不足以支撑,当前应用的服务也很有可能会被清理掉。TRIM_MEMORY_UI_HIDDEN
当回调回来的时候,说明应用的UI对用户不可见的,此时释放UI使用的一些资源。这个不同于onStop()
,onStop()
的回调,有可能仅仅是当前应用中进入了另外一个Activity
。
TRIM_MEMORY_BACKGROUND
: 系统已经处于低可用内存的情况,并且当前进程处于后台进程LRU队列队头附近,因此还是比较安全的,但是系统可能已经开始从LRU队列中清理进程了,此时当前应用需要释放部分资源,以保证尽量的保活。TRIM_MEMORY_MODERATE
: 系统处于低可用内存的情况,并且当前进程处于后台进程LRU队列中间的位置,如果内存进一步紧缺,当前进程就有可能被清理掉,需要进一步释放资源。TRIM_MEMORY_COMPLETE
: 系统处于低可用内存的情况,并且当前进程处于后天进程LRU队列队首的位置,如果内存进一步紧缺,下一个清理的就是当前进程,需要释放尽可能的资源来保活当前进程。在API14之前,onLowMemory()
就相当于这个级别的回调。assets
下,要不放在nodpi
下,要不都带,否则缩放会带来额外耗时与内存问题AndroidManifest
中配置largeHeap=true
,一般dvm heep最大值可增大50%以上。但是没有特殊明确的需要,尽可能的避免这样设置,因为这样一来很可能隐藏了消耗了完全没有必要的内存的问题。Activity#onDestory
以后,遍历所有View,干掉所有View可能的引用(通常泄漏一个Activity,连带泄漏其上的View,然后就泄漏了大于全屏图片的内存)。WeakReference
引用外部类,防止内部类长期存在,泄漏了外部类的问题。Android 2.3.x或更低版本的设备,是将所有的Bitmap对象存储在native heap,因此我们很难通过工具去检测其内存大小,在Android 3.0或更高版本的设备,已经调整为存储到了每个应用自身的Dalvik heap中了。
BitmapFactory#decode
出口,捕获此处decode oom,控制长宽(小于屏幕分辨率大小 )BitmapFactory#options
的InNativeAlloc
参数为true,此时decode的内存不会上报到dvm中,便不会oom。productFlavors
参数,定义不同的variations,快速批量打渠道包Activity
以及Application
对打开的耗时进行统计,观察其变化,避免因为迭代导致某些页面非预期的打开变慢。measure
、layout
、draw
的耗时。final
能用就用(高效: 编译器在调用final
方法时,会转入内嵌机制)ListView
、RecyclerView
等滑动列表控件,停留在当前页面的时候,可以考虑直接预加载下个页面所需图片/sdcard/Android/data/[package name]/
里(在应用卸载时,会随即删除)(Context#getExternalFilesDir()
),而非/sdcard/
根目录建文件夹(节操问题)shrinkResources
与minifyEnabled
参数可以简单快速的在编包的时候自动删除无用资源Throwable
生成栈快照是一项耗时的工作。