Android 内存优化

来源:互联网 发布:lomo拍立得知乎 编辑:程序博客网 时间:2024/06/04 18:16
 1.1布局优化

(1)   抽象布局标签,使用<include>, <viewstub>, <merge>等标签进行布局;

(2)   减少嵌套层次,多使用RelativeLayout布局;

(3)   使用ListView缓存机制;

(4)   优化图片资源,尽量使用.9图片;

(5)   使用hierarchy viewer调优工具,查看布局measurelayoutdraw的时间。

 

1.2 代码优化

(1)   字符串拼接用StringBuilder代替String;尽量使用StringBuilderStringBuffer进行字符串连接

(2)   数据类型尽可能选择占用空间小的;

(3)   了解弱引用、虚引用;

(4)   优化算法,尽量不用递归;

(5)   尽量少的声明全局变量;

(6)   声明全局静态变量,一定要加final声明;

(7)   声明非静态的全局变量,最好不要初始化任何值,在使用到的地方,在进行初始化;

(8)   函数中若干次使用全局变量,应该将全局变量赋值给本地变量,然后直接使用本地变量;

(9)   能用Int,不要使用浮点数;

(10)能用乘法不用除法。(我记得用位移会更好);

(11)尽量避免使用getersetter方法;

(12)ActivityonCreate函数中,尽量做少的事;

(13)Activity中声明的静态数组或者静态代码块,重构到单独的一个类里;

(14)Activity启动后开始进行异步线程的加载,最好delay一下再开启线程;

(15)对于存在于集合中的Bean对象,尽可能少的声明变量。能int就不要用long,声明的string等复杂变量,最好不要进行初始化。

(16)使用线程,一定要给它传一个名字,然后需要定义线程的优先级

(17)在使用集合的时候,优先选择SparseArray,(代替HashMap)。Java中每个类(包括匿名内部类)都占用至少 500字节左右的代码;每个类的实例会在 RAM中占用大约 12 ~ 16 字节的内存;每向 HashMap中添加一个 Entry 时,新生成的 Entry占用大约 32 个字节。

(18)尽量避免使用枚举,枚举类型 Enum会比静态常量占用更多的内存;

(19)工具方法尽量写成是静态方法;

(20)线程间同步尽量使用开销小的同步锁;

(21)在使用集合类的时候,如果已知数据的规模,在初始化的时候,就设定好默认大小;

(22)私有内部类访问外部类的私有变量,要将变量修改为包继承权限;

(23)对于开销大的算法,且不止是执行一次的,要使用缓存策略;

(24)避免在绘制或者解析布局的时候,分配对象,例如onDraw方法;

(25)不要给布局写无用的参数,例如RelativeLayout,写layout_weight属性;

(26)如果LinearLayout用于嵌套的layout空间计算,它的android:baselineAligned设置为false,可以加速layout计算;

(27)FloatMath代替Math

(28)尽量避免嵌套的使用layout_weight,那样会影响执行效率;

(29)如果为rootView设置了背景,那么会先用Theme指定的背景绘制一遍,然后才用指定的背景绘制,这叫做"overdraw",可以通过themebackgroundnull来避免;

(30)记得关闭启动的服务;

(31)当服务中的任务完成后,要记得停止该服务。可以考虑使用 IntentService,因为IntentService在完成任务后会自动停止;

(32)UI不可见时释放资源;

(33) onStop中关闭网络连接、注销广播接收器、释放传感器等资源;

(34) onTrimMemory()回调方法中监听TRIM_MEMORY_UI_HIDDEN 级别的信号,此时可在 Activity 中释放 UI使用的资源,大符减少应用占用的内存,从而避免被系统清除出内存。内存紧张时释放资源。运行中的程序,如果内存紧张,会在 onTrimMemory(int level)回调方法中接收到以下级别的信号:

l TRIM_MEMORY_RUNNING_MODERATE:系统可用内存较低,正在杀掉 LRU缓存中的进程。你的进程正在运行,没有被杀掉的危险。

l TRIM_MEMORY_RUNNING_LOW:系统可用内存更加紧张,程序虽然暂没有被杀死的危险,但是应该尽量释放一些资源,以提升系统的性能(这也会直接影响你程序的性能)。

l TRIM_MEMORY_RUNNING_CRITICAL:系统内存极度紧张,而LRU缓存中的大部分进程已被杀死,如果仍然无法获得足够的资源的话,接下来会清理掉 LRU中的所有进程,并且开始杀死一些系统通常会保留的进程,比如后台运行的服务等。

当程序未在运行,保留在 LRU缓存中时, onTrimMemory(int level) 中会返回以下级别的信号:

l TRIM_MEMORY_BACKGROUND:系统可用内存低,而你的程序处在 LRU的顶端,因此暂时不会被杀死,但是此时应释放一些程序再次打开时比较容易恢复的 UI资源。

l TRIM_MEMORY_MODERATE:系统可用内存低,程序处于 LRU的中部位置,如果内存状态得不到缓解,程序会有被杀死的可能。

l TRIM_MEMORY_COMPLETE:系统可用内存低,你的程序处于 LRU尾部,如果系统仍然无法回收足够的内存资源,你的程序将首先被杀死。此时应释放无助于恢复程序状态的所有资源。

注:该 API在版本 14 中加入。旧版本的onLowMemory()方法,大致相当于 onTrimMemory(int level)中接收到 TRIM_MEMORY_COMPLETE 级别的信号。

另:尽管系统主要按照 LRU中顺序来杀进程,不过系统也会考虑程序占用的内存多少,那些占用内存高的进程有更高的可能性会被首先杀死;

(35)确定你的程序应该占用多少内存,可以通过 getMemoryClass()来获取你的程序被分配的可用内存,以 M为单位。你可以通过在 <application> 标签下将 largeHeap 属性设为 true来要求更多的内存,这时通过 getLargeMemoryClass()方法来获取可用内存。大部分应用程序不需要使用此功能,因此使用该标签前,确认你的程序是否真的需要更多内存。使用更多内存会对整个系统的性能产生影响,而且当程序进入 LRU时会更容易首先被系统清理掉;

(36)谨慎使用第三方类库;这些外部类库可能原先并非针对移动平台,因此未进行过优化,在使用前应注意。另外尽量不要因为一两个特性而使用一个体积很大的类库;

(37)使用 ProGuard移除无用的代码并重命名一些类、字段、方法等,使你的代码更紧凑,节省内存空间;

(38)zipaligned 对最终打包的 apk进行字节对齐;

注:Google Play不接受未对齐过的 apk

(39)如果已经获得一个相对稳定的版本,应对程序整个生命周期的内存使用状况进行分析;

(40)使用多个进程。如果程序需要执行大量的后台工作,可考虑将程序分为两个进程,一个进程负责 UI,另一个进程负责后台任务。比如音乐播放器。

代码示例:

<serviceandroid:name=".PlaybackService"android:process=":background"/>

 android:process属性的值以“:”开头,名称可任意选取。

在决定是否使用多进程前,应注意,一个不执行任何任务的空进程至少也要占用 1.4 MB内存。另外要注意进程的相互依赖性,比如如果将 ContentProvider放在 UI 进程中,而后台任务进程也需要调用 ContentProvider,就会导致 UI进程一直保留在 RAM 中。

41)尽量减少对变量的重复计算

for(int i=0;i<list.size();i++)

应该改为

for(int i=0,len=list.size();i<len;i++)

    并且在循环中应该避免使用复杂的表达式,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。

42)尽量在finally块中释放资源

程序中使用到的资源应当被释放,以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。

43没有必要使用硬件加速的界面建议关掉硬件加速

通过DDMSheap跟踪发现,相比于关闭硬件加速,在打开硬件加速的情况下会消耗更多的内存,但有的界面打开或者关闭硬件加速对程序的运行效率并没有太大的影响,此种情况下可以考虑在AndroidManifest.xml文件中关闭掉对应Activity的硬件加速,like this

 

<!--设置界面 -->

<activity

    android:name=".SettingActivity"

    android:hardwareAccelerated="false"

    android:screenOrientation="sensorLandscape"

    android:theme="@style/Translucent_NoTitle">

</activity>

 

注意:如果使用到WebView、视频播放、手写、动画等功能时,关掉硬件加速会严重音效程序的运行效率,这种情况可以只关闭掉Activity中某些view的硬件加速,整个Activity的硬件加速不关闭。

如果Activity中某个View需要关闭硬件加速,但整个Activity不能关闭,可以调用view层级关闭硬件加速的方法:

// view.setLayerType ||在定义view的构造方法中调用该方法

setLayerType(View.LAYER_TYPE_SOFTWARE,null);

 

 

1.3 图片处理

图片资源的使用不当经常会造成OOM的问题,尤其在低端机型内存较少的情况下。

(1)   及时回收。

正确使用 Bipmap,避免浪费内存;调用recycle方法主动回收资源。代码示例:

if(null != bitmap && !bitmap.isRecycled())

{

              bitmap.recycle();

              bitmap = null;

}

(2)   适当压缩。

如果你的 ImageViwe的尺寸只有 100 * 100,那么没有必要将一张 2560 * 1600的图片整个加载入内存;对图片进行适当的压缩。代码示例:

BitmapFactory.Options newOpts = new BitmapFactory.Options();

newOpts.inPreferredConfig = Config.RGB_565;

       newOpts.inJustDecodeBounds = true;

       Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);

 

       newOpts.inJustDecodeBounds = false;

       int old_w = newOpts.outWidth;

       int old_h = newOpts.outHeight;

       int new_w = targetWidth;

       int new_h = targetHeight;

 

       int be = (int) ((old_w / new_w + old_h / new_h) >> 1);

       if (be <= 1)

       {

              be = 1;

       }

       newOpts.inSampleSize = be;

bitmap = BitmapFactory.decodeFile(srcPath, newOpts);

 

(3)   Bitmap图片占用内存计算:

Bitmap图片在加载到内存的时候是按照:宽**像素点位数来计算的。你可以把图片看成是由width行、height列的矩阵组成,每一个矩阵元素代表一个像素点,每一个像素点都是1byte整数倍的数据,这个数据越大,表示的色彩就越丰富,图片的显示质量就越高。Bitmap中有一个枚举类Config用来配置图片的压缩格式,代表每个像素是用多大的数据来存储的,数值越大能够存储的颜色信息就越多,也就越丰富,显示效果也就越好。Config.ALPHA_81 byteConfig.RGB_565Config.ARGB_4444都是2 bytesConfig.RGB_565没有Alpha值所以多用来配置没有透明度的图片,Config.ARGB_88884 bytes,一般图片都是按照这个来配置的。下面是获取配置的代码:

static int getBytesPerPixel(Config config) {

if (config == Config.ARGB_8888) {

return 4;

} else if (config == Config.RGB_565) {

return 2;

} else if (config == Config.ARGB_4444) {

return 2;

} else if (config == Config.ALPHA_8) {

return 1;

}

return 1;

}

(4)使用图片你需要注意哪些:

Ø android系统自身的问题。android系统给每一个应用分配了一定的内存空间,分配了多少要看厂家和机型,数值可以通过Runtime类获取,Runtime.getRuntime()获取实例,然后通过maxMemory()方法获取系统可为APP分配的最大内存,totalMemory()获取APP当前所分配的内存heap空间大小,freeMemory()获取当前可用的内存,当被耗尽时会自动扩张,但是不会超过maxMemory。下图为google官网提供的不同分辨率下不同的dpi分配的最小内存;


 

Ø 需要多大的照片。很多图片其实在手机上显示的时候不需要完全把原图加载到内存中,比如我手机相机拍了一张照片是4208*3120的,加载到内存中占用的内存就是52M,这是很恐怖的,两张照片差不多就把你的app内存给耗光了。一般情况下你都需要对需要加载的图片进行处理,这个处理主要是减小图片尺寸,降低分辨率,比如你的控件显示就是100*100的大小,那你就要把图片缩小到100*100

 

Ø 及时释放内存。安卓2.3.3(API级别10)之前,Bitmap像素数据和Bitmap对象是分开存储的,像素数据是存储在native memory中,对象存储在Dalvik heap中,native memory中的像素数据不是以一种可预见的方式释放,可能导致应用程序暂时超过其内存限制和崩溃,所以在Android2.3.3(API 10)之前你必须要调用recycle()方法来释放掉内存避免出现OOM,当然前提是确定这个bitmap不再使用,否则会出现Canvas: trying to use a recycled bitmap.Android3.0(API 11)之后,Bitmap的像素数据和Bitmap对象一起存储在Dalvik heap中,所以我们不用手动调用recycle()来释放Bitmap对象,内存的释放都交给垃圾回收器来做。

 

0 0