4) The price of ENUMs
在StackOverFlow等问答社区常常出现关于在Android系统里面使用枚举类型的性能讨论,关于这一点,Android官方的Training课程里面有下面这样一句话:
Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
关于enum的效率,请看下面的讨论。假设我们有这样一份代码,编译之后的dex大小是2556 bytes,在此基础之上,添加一些如下代码,这些代码使用普通static常量相关作为判断值:
增加上面那段代码之后,编译成dex的大小是2680 bytes,相比起之前的2556 bytes只增加124 bytes。假如换做使用enum,情况如下:
使用enum之后的dex大小是4188 bytes,相比起2556增加了1632 bytes,增长量是使用static int的13倍。不仅仅如此,使用enum,运行时还会产生额外的内存占用,如下图所示:
Android官方强烈建议不要在Android程序里面使用到enum。
5) Trimming and Sharing Memory
Android系统的一大特色是多任务,用户可以随意在不同的app之间进行快速切换。为了确保你的应用在这种复杂的多任务环境中正常运行,我们需要了解下面的知识。
为了让background的应用能够迅速的切换到forground,每一个background的应用都会占用一定的内存。Android系统会根据当前的系统内存使用情况,决定回收部分background的应用内存。如果background的应用从暂停状态直接被恢复到forground,能够获得较快的恢复体验,如果background应用是从Kill的状态进行恢复,就会显得稍微有点慢。
Android系统提供了一些回调来通知应用的内存使用情况,通常来说,当所有的background应用都被kill掉的时候,forground应用会收到onLowMemory()的回调。在这种情况下,需要尽快释放当前应用的非必须内存资源,从而确保系统能够稳定继续运行。Android系统还提供了onTrimMemory()的回调,当系统内存达到某些条件的时候,所有正在运行的应用都会收到这个回调,同时在这个回调里面会传递以下的参数,代表不同的内存使用情况,下图介绍了各种不同的回调参数:
关于每个参数的更多介绍,请参考《 Android Training - 管理应用的内存》,另外onTrimMemory()的回调可以发生在Application,Activity,Fragment,Service,Content Provider。
从Android 4.4开始,ActivityManager提供了isLowRamDevice()的API,通常指的是Heap Size低于512M或者屏幕大小<=800*480的设备。
6) DO NOT LEAK VIEWS
内存泄漏的概念,下面一张图演示下:
通常来说,View会保持Activity的引用,Activity同时还和其他内部对象也有可能保持引用关系。当屏幕发生旋转的时候,activity很容易发生泄漏,这样的话,里面的view也会发生泄漏。Activity以及view的泄漏是非常严重的,为了避免出现泄漏,请特别留意以下的规则:
6.1) 避免使用异步回调
异步回调被执行的时间不确定,很有可能发生在activity已经被销毁之后,这不仅仅很容易引起crash,还很容易发生内存泄露。
6.2) 避免使用Static对象
因为static的生命周期过长,使用不当很可能导致leak,在Android中应该尽量避免使用static对象。
6.3) 避免把View添加到没有清除机制的容器里面
假如把view添加到 WeekHashMap,如果没有执行清除操作,很可能会导致泄漏。
7) Location & Battery Drain
开启定位功能是一个相对来说比较耗电的操作,通常来说,我们会使用类似下面这样的代码来发出定位请求:
上面演示中有一个方法是setInterval()指的意思是每隔多长的时间获取一次位置更新,时间相隔越短,自然花费的电量就越多,但是时间相隔太长,又无法及时获取到更新的位置信息。其中存在的一个优化点是,我们可以通过判断返回的位置信息是否相同,从而决定设置下次的更新间隔是否增加一倍,通过这种方式可以减少电量的消耗,如下图所示:
在位置请求的演示代码中还有一个方法是setFastestInterval(),因为整个系统中很可能存在其他的应用也在请求位置更新,那些应用很有可能设置的更新间隔时间很短,这种情况下,我们就可以通过setFestestInterval的方法来过滤那些过于频繁的更新。
通过GPS定位服务相比起使用网络进行定位更加的耗电,但是也相对更加精准一些,他们的图示关系如下:
为了提供不同精度的定位需求,同时屏蔽实现位置请求的细节,Android提供了下面4种不同精度与耗电量的参数给应用进行设置调用,应用只需要决定在适当的场景下使用对应的参数就好了,通过LocationRequest.setPriority()方法传递下面的参数就好了。