android app性能优化大汇总(内存性能优化)

转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持!
html

 

写在最前:java

本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各类内存零散知识点进行汇总、挑选、简化后整理而成。android

因此我将本文定义为一个工具类的文章,若是你在Android开发中遇到关于内存问题,或者立刻要参加面试,或者就是单纯的学习或复习一下内存相关知识,都欢迎阅读。(本文最后我会尽可能列出所参考的文章)。git

 

 

内存简介:
 
RAM(random access memory)随机存取存储器。说白了就是内存。

通常Java在内存分配时会涉及到如下区域:程序员

 

寄存器(Registers):速度最快的存储场所,由于寄存器位于处理器内部咱们在程序中没法控制github

栈(Stack):存放基本类型的数据和对象的引用,但对象自己不存放在栈中,而是存放在堆中面试

堆(Heap):堆内存用来存放由new建立的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器(GC)来管理。正则表达式

静态域(static field):  静态存储区域就是指在固定的位置存放应用程序运行时一直存在的数据,Java在内存中专门划分了一个静态存储区域来管理一些特殊的数据变量如静态的数据变量算法

常量池(constant pool):虚拟机必须为每一个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和floating point常量)和对其余类型,字段和方法的符号引用。shell

非RAM存储:硬盘等永久存储空间

 

 

堆栈特色对比:

因为篇幅缘由,下面只简单的介绍一下堆栈的一些特性。

:当定义一个变量时,Java就在栈中为这个变量分配内存空间,当该变量退出该做用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间能够当即被另做他用。

:当堆中的new产生数组和对象超出其做用域后,它们不会被释放,只有在没有引用变量指向它们的时候才变成垃圾,不能再被使用。即便这样,所占内存也不会当即释放,而是等待被垃圾回收器收走。这也是Java比较占内存的缘由。

 

:存取速度比堆要快,仅次于寄存器。但缺点是,存在栈中的数据大小与生存期必须是肯定的,缺少灵活性。

:堆是一个运行时数据区,能够动态地分配内存大小,所以存取速度较慢。也正由于这个特色,堆的生存期没必要事先告诉编译器,并且Java的垃圾收集器会自动收走这些再也不使用的数据。

 

:栈中的数据能够共享, 它是由编译器完成的,有利于节省空间。

例如:须要定义两个变量int a = 3;int b = 3;

编译器先处理int a = 3;首先它会在栈中建立一个变量为a的引用,而后查找栈中是否有3这个值,若是没找到,就将3存放进来,而后将a指向3。接着处理int b = 3;在建立完b的引用变量后,由于在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的状况。这时,若是再让a=4;那么编译器会从新搜索栈中是否有4值,若是没有,则将4存放进来,并让a指向4;若是已经有了,则直接将a指向这个地址。所以a值的改变不会影响到b的值。

:例如上面栈中a的修改并不会影响到b, 而在堆中一个对象引用变量修改了这个对象的内部状态,会影响到另外一个对象引用变量。

 

内存耗用名词解析:

VSS - Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)

RSS - Resident Set Size 实际使用物理内存(包含共享库占用的内存)

PSS - Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)

USS - Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

通常来讲内存占用大小有以下规律:VSS >= RSS >= PSS >= USS

 

 

OOM:

 

内存泄露能够引起不少的问题:

1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)

2.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反以内存占用越小,在后台存在的时间就越长)

3.直接崩溃(OutOfMemoryError)

 

ANDROID内存面临的问题:

 

1.有限的堆内存,原始只有16M

2.内存大小消耗等根据设备,操做系统等级,屏幕尺寸的不一样而不一样

3.程序不能直接控制

4.支持后台多任务处理(multitasking)

5.运行在虚拟机之上

 

 

 

5R:

本文主要经过以下的5R方法来对ANDROID内存进行优化:

1.Reckon(计算)

首先须要知道你的app所消耗内存的状况,知己知彼才能百战不殆

2.Reduce(减小)

消耗更少的资源

 

3.Reuse(重用)

当第一次使用完之后,尽可能给其余的使用

 

5.Recycle(回收)

返回资源给生产流

4.Review(检查)

回顾检查你的程序,看看设计或代码有什么不合理的地方。

 

 

Reckon (计算):

了解本身应用的内存使用状况是颇有必要的。若是当内存使用太高的话就须要对其进行优化,由于更少的使用内存能够减小ANDROID系统终止咱们的进程的概率,也能够提升多任务执行效率和体验效果。

下面从系统内存(system ram)和堆内存(heap)两个方面介绍一些查看和计算内存使用状况的方法:

 

System Ram(系统内存):

观察和计算系统内存使用状况,可使用Android提供给咱们的两个工具procstatsmeminfo。他们一个侧重于后台的内存使用,另外一个是运行时的内存使用。

 

Process Stats: 

Android 4.4 KitKat 提出了一个新系统服务,叫作procstats。它将帮助你更好的理解你app在后台(background)时的内存使用状况。
Procstats能够去监视你app在一段时间的行为,包括在后台运行了多久,并在此段时间使用了多少内存。从而帮助你快速的找到应用中不效率和不规范的地方去避免影响其performs,尤为是在低内存的设备上运行时。
你能够经过adb shell命令去使用procstats(adb shell dumpsys procstats --hours 3),或者更方便的方式是运行Process Stats开发者工具(在4.4版本的手机中点击Settings > Developer options > Process Stats)
点击单个条目还能够查看详细信息
 
meminfo:
Android还提供了一个工具叫作meminfo。它是根据PSS标准 (Proportional Set Size——实际物理内存)计算每一个进程的内存使用而且按照重要程度排序。
你能够经过命令行去执行它:(adb shell dumpsys meminfo)或者使用在设备上点击Settings > Apps > Running(与Procstats不用,它也能够在老版本上运行)
更多关于 Procstatsmeninfo的介绍能够参考我翻译的一篇文章: Process Stats:了解你的APP如何使用内存

 

Heap(堆内存):

在程序中可使用以下的方法去查询内存使用状况

 

ActivityManager#getMemoryClass()

查询可用堆内存的限制

3.0(HoneyComb)以上的版本能够经过largeHeap=“true”来申请更多的堆内存(不过这算做“做弊”)

 

 

ActivityManager#getMemoryInfo(ActivityManager.MemoryInfo)
获得的MemoryInfo中能够查看以下Field的属性:
availMem:表示系统剩余内存
lowMemory:它是boolean值,表示系统是否处于低内存运行
hreshold:它表示当系统剩余内存低于好多时就当作低内存运行

 

 

android.os.Debug#getMemoryInfo(Debug.MemoryInfo memoryInfo)

获得的MemoryInfo中能够查看以下Field的属性:

 

dalvikPrivateDirty: The private dirty pages used by dalvik。
dalvikPss :The proportional set size for dalvik.
dalvikSharedDirty :The shared dirty pages used by dalvik.
nativePrivateDirty :The private dirty pages used by the native heap.
nativePss :The proportional set size for the native heap.
nativeSharedDirty :The shared dirty pages used by the native heap.
otherPrivateDirty :The private dirty pages used by everything else.
otherPss :The proportional set size for everything else.
otherSharedDirty :The shared dirty pages used by everything else.

 

 

 
dalvik:是指dalvik所使用的内存。
native:是被native堆使用的内存。应该指使用C\C++在堆上分配的内存。
other:是指除dalvik和native使用的内存。可是具体是指什么呢?至少包括在C\C++分配的非堆内存,好比分配在栈上的内存。
private:是指私有的。非共享的。
share:是指共享的内存。
PSS:实际使用的物理内存(比例分配共享库占用的内存)
 PrivateDirty:它是指非共享的,又不能换页出去(can not be paged to disk )的内存的大小。好比Linux为了提升分配内存速度而缓冲的小对象,即便你的进程结束,该内存也不会释放掉,它只是又从新回到缓冲中而已。
SharedDirty:参照PrivateDirty我认为它应该是指共享的,又不能换页出去(can not be paged to disk )的内存的大小。好比Linux为了提升分配内存速度而缓冲的小对象,即便全部共享它的进程结束,该内存也不会释放掉,它只是又从新回到缓冲中而已。
 

 

android.os.Debug#getNativeHeapSize()

返回的是当前进程navtive堆自己总的内存大小

android.os.Debug#getNativeHeapAllocatedSize()

返回的是当前进程navtive堆中已使用的内存大小

android.os.Debug#getNativeHeapFreeSize()

返回的是当前进程navtive堆中已经剩余的内存大小

 

 

Memory Analysis Tool(MAT):

一般内存泄露分析被认为是一件颇有难度的工做,通常由团队中的资深人士进行。不过,今天咱们要介绍的 MAT(Eclipse Memory Analyzer)被认为是一个“傻瓜式“的堆转储文件分析工具,你只须要轻轻点击一下鼠标就能够生成一个专业的分析报告。

以下图:

关于详细的MAT使用我推荐下面这篇文章:使用 Eclipse Memory Analyzer 进行堆转储文件分析

 

OOM:

 

内存泄露能够引起不少的问题:

1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)

2.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反以内存占用越小,在后台存在的时间就越长)

3.直接崩溃(OutOfMemoryError)

 

ANDROID内存面临的问题:

 

1.有限的堆内存,原始只有16M

2.内存大小消耗等根据设备,操做系统等级,屏幕尺寸的不一样而不一样

3.程序不能直接控制

4.支持后台多任务处理(multitasking)

5.运行在虚拟机之上

 

5R:

本文主要经过以下的5R方法来对ANDROID内存进行优化:

 

1.Reckon(计算)

首先须要知道你的app所消耗内存的状况,知己知彼才能百战不殆

2.Reduce(减小)

消耗更少的资源

 

3.Reuse(重用)

当第一次使用完之后,尽可能给其余的使用

5.Recycle(回收)

返回资源给生产流

4.Review(检查)

回顾检查你的程序,看看设计或代码有什么不合理的地方。

 

Reckon:

 

关于内存简介,和Reckon(内存计算)的内容请看上一篇文章:ANDROID内存优化(大汇总——上)

 

 

Reduce :

 

Reduce的意思就是减小,直接减小内存的使用是最有效的优化方式。

下面来看看有哪些方法能够减小内存使用:

 

 

Bitmap
Bitmap是内存消耗大户,绝大多数的OOM崩溃都是在操做Bitmap时产生的,下面来看看如何几个处理图片的方法:

 

图片显示:

咱们须要根据需求去加载图片的大小。

例如在列表中仅用于预览时加载缩略图(thumbnails )。

只有当用户点击具体条目想看详细信息的时候,这时另启动一个fragment/activity/对话框等等,去显示整个图片

 

 

图片大小:

直接使用ImageView显示bitmap会占用较多资源,特别是图片较大的时候,可能致使崩溃。 
使用BitmapFactory.Options设置inSampleSize, 这样作能够减小对系统资源的要求。 
属性值inSampleSize表示缩略图大小为原始图片大小的几分之一,即若是这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。 

 

[java]  view plain copy
 
 
  1. BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();  
  2. bitmapFactoryOptions.inJustDecodeBounds = true;  
  3. bitmapFactoryOptions.inSampleSize = 2;  
  4. // 这里必定要将其设置回false,由于以前咱们将其设置成了true    
  5. // 设置inJustDecodeBounds为true后,decodeFile并不分配空间,即,BitmapFactory解码出来的Bitmap为Null,但可计算出原始图片的长度和宽度    
  6. options.inJustDecodeBounds = false;  
  7. Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);  

 

 

图片像素:

Android中图片有四种属性,分别是:
ALPHA_8:每一个像素占用1byte内存 
ARGB_4444:每一个像素占用2byte内存 
ARGB_8888:每一个像素占用4byte内存 (默认)
RGB_565:每一个像素占用2byte内存 
 
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但一样的,占用的内存也最大。 因此在对图片效果不是特别高的状况下使用RGB_565(565没有透明度属性),以下:
[java]  view plain copy
 
 
  1. publicstaticBitmapreadBitMap(Contextcontext, intresId) {  
  2.     BitmapFactory.Optionsopt = newBitmapFactory.Options();  
  3.     opt.inPreferredConfig = Bitmap.Config.RGB_565;  
  4.     opt.inPurgeable = true;  
  5.     opt.inInputShareable = true;  
  6.     //获取资源图片   
  7.     InputStreamis = context.getResources().openRawResource(resId);  
  8.     returnBitmapFactory.decodeStream(is, null, opt);  
  9. }  

图片回收:

使用Bitmap事后,就须要及时的调用Bitmap.recycle()方法来释放Bitmap占用的内存空间,而不要等Android系统来进行释放。

下面是释放Bitmap的示例代码片断。

 

[java]  view plain copy
 
 
  1. // 先判断是否已经回收  
  2. if(bitmap != null && !bitmap.isRecycled()){  
  3.     // 回收而且置为null  
  4.     bitmap.recycle();  
  5.     bitmap = null;  
  6. }  
  7. System.gc();  

捕获异常:

通过上面这些优化后还会存在报OOM的风险,因此下面须要一道最后的关卡——捕获OOM异常:

 

[java]  view plain copy
 
 
  1. Bitmap bitmap = null;  
  2. try {  
  3.     // 实例化Bitmap  
  4.     bitmap = BitmapFactory.decodeFile(path);  
  5. catch (OutOfMemoryError e) {  
  6.     // 捕获OutOfMemoryError,避免直接崩溃  
  7. }  
  8. if (bitmap == null) {  
  9.     // 若是实例化失败 返回默认的Bitmap对象  
  10.     return defaultBitmapMap;  
  11. }  
 

 

修改对象引用类型:

 

引用类型:

引用分为四种级别,这四种级别由高到低依次为:强引用>软引用>弱引用>虚引用。

强引用(strong reference)
如:Object object=new Object(),object就是一个强引用了。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具备强引用的对象来解决内存不足问题。

软引用(SoftReference)
只有内存不够时才回收,经常使用于缓存;当内存达到一个阀值,GC就会去回收它;

 

弱引用(WeakReference)   

弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。 

 

虚引用(PhantomReference)   

"虚引用"顾名思义,就是形同虚设,与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,在任什么时候候均可能被垃圾回收。  

 

软引用和弱引用的应用实例:

注意:对于SoftReference(软引用)或者WeakReference(弱引用)的Bitmap缓存方案,如今已经不推荐使用了。自Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,因此下面的内容能够选择忽略。

在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大并且声明周期较长的对象时候,能够尽可能应用软引用和弱引用技术。

下面以使用软引用为例来详细说明(弱引用的使用方式与软引用是相似的):

假设咱们的应用会用到大量的默认图片,并且这些图片不少地方会用到。若是每次都去读取图片,因为读取文件须要硬件操做,速度较慢,会致使性能较低。因此咱们考虑将图片缓存起来,须要的时候直接从内存中读取。可是,因为图片占用内存空间比较大,缓存不少图片须要不少的内存,就可能比较容易发生OutOfMemory异常。这时,咱们能够考虑使用软引用技术来避免这个问题发生。

首先定义一个HashMap,保存软引用对象。

[java]  view plain copy
 
 
  1. private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();  
再来定义一个方法,保存Bitmap的软引用到HashMap。

 

[java]  view plain copy
 
 
  1. public void addBitmapToCache(String path) {  
  2.        // 强引用的Bitmap对象  
  3.        Bitmap bitmap = BitmapFactory.decodeFile(path);  
  4.        // 软引用的Bitmap对象  
  5.        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);  
  6.        // 添加该对象到Map中使其缓存  
  7.        imageCache.put(path, softBitmap);  
  8.    }  
获取的时候,能够经过SoftReference的get()方法获得Bitmap对象。
[java]  view plain copy
 
 
  1. public Bitmap getBitmapByPath(String path) {  
  2.         // 从缓存中取软引用的Bitmap对象  
  3.         SoftReference<Bitmap> softBitmap = imageCache.get(path);  
  4.         // 判断是否存在软引用  
  5.         if (softBitmap == null) {  
  6.             return null;  
  7.         }  
  8.         // 取出Bitmap对象,若是因为内存不足Bitmap被回收,将取得空  
  9.         Bitmap bitmap = softBitmap.get();  
  10.         return bitmap;  
  11.     }  
使用软引用之后,在OutOfMemory异常发生以前,这些缓存的图片资源的内存空间能够被释放掉的,从而避免内存达到上限,避免Crash发生。

须要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象以后,get方法将返回null。因此在获取软引用对象的代码中,必定要判断是否为null,以避免出现NullPointerException异常致使应用崩溃。

 

到底何时使用软引用,何时使用弱引用呢?

我的认为,若是只是想避免OutOfMemory异常的发生,则可使用软引用。若是对于应用的性能更在乎,想尽快回收一些占用内存比较大的对象,则可使用弱引用。

还有就是能够根据对象是否常用来判断。若是该对象可能会常用的,就尽可能用软引用。若是该对象不被使用的可能性更大些,就能够用弱引用。

另外,和弱引用功能相似的是WeakHashMap。WeakHashMap对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收之后,其条目从映射中有效地移除。WeakHashMap使用ReferenceQueue实现的这种机制。

 

其余小tips:

 

对常量使用static final修饰符

让咱们来看看这两段在类前面的声明:

static int intVal = 42;
static String strVal = "Hello, world!";
编译器会生成一个叫作clinit的初始化类的方法,当类第一次被使用的时候这个方法会被执行。方法会将42赋给intVal,而后把一个指向类中常量表 的引用赋给strVal。当之后要用到这些值的时候,会在成员变量表中查找到他们。 下面咱们作些改进,使用“final”关键字:

static final int intVal = 42;
static final String strVal = "Hello, world!";

如今,类再也不须要clinit方法,由于在成员变量初始化的时候,会将常量直接保存到类文件中。用到intVal的代码被直接替换成42,而使用strVal的会指向一个字符串常量,而不是使用成员变量。

将一个方法或类声明为final不会带来性能的提高,可是会帮助编译器优化代码。举例说,若是编译器知道一个getter方法不会被重载,那么编译器会对其采用内联调用。

你也能够将本地变量声明为final,一样,这也不会带来性能的提高。使用“final”只能使本地变量看起来更清晰些(可是也有些时候这是必须的,好比在使用匿名内部类的时候)。

 

静态方法代替虚拟方法

若是不须要访问某对象的字段,将方法设置为静态,调用会加速15%到20%。这也是一种好的作法,由于你能够从方法声明中看出调用该方法不须要更新此对象的状态。

 

减小没必要要的全局变量

尽可能避免static成员变量引用资源耗费过多的实例,好比Context

 

由于Context的引用超过它自己的生命周期,会致使Context泄漏。因此尽可能使用Application这种Context类型。 你能够经过调用Context.getApplicationContext()或 Activity.getApplication()轻松获得Application对象。 
 

避免建立没必要要的对象

最多见的例子就是当你要频繁操做一个字符串时,使用StringBuffer代替String。

对于全部全部基本类型的组合:int数组比Integer数组好,这也归纳了一个基本事实,两个平行的int数组比 (int,int)对象数组性能要好不少。

整体来讲,就是避免建立短命的临时对象。减小对象的建立就能减小垃圾收集,进而减小对用户体验的影响。

 
避免内部Getters/Setters
在Android中,虚方法调用的代价比直接字段访问高昂许多。一般根据面向对象语言的实践,在公共接口中使用Getters和Setters是有道理的,但在一个字段常常被访问的类中宜采用直接访问。

 

避免使用浮点数

一般的经验是,在Android设备中,浮点数会比整型慢两倍。

 

使用实体类比接口好

 

假设你有一个HashMap对象,你能够将它声明为HashMap或者Map:

Map map1 = new HashMap();
HashMap map2 = new HashMap();

哪一个更好呢?

按照传统的观点Map会更好些,由于这样你能够改变他的具体实现类,只要这个类继承自Map接口。传统的观点对于传统的程序是正确的,可是它并不适合嵌入式系统。调用一个接口的引用会比调用实体类的引用多花费一倍的时间。若是HashMap彻底适合你的程序,那么使用Map就没有什么价值。若是有些地方你不能肯定,先避免使用Map,剩下的交给IDE提供的重构功能好了。(固然公共API是一个例外:一个好的API经常会牺牲一些性能)

 

避免使用枚举

枚举变量很是方便,但不幸的是它会牺牲执行的速度和并大幅增长文件体积。

使用枚举变量可让你的API更出色,并能提供编译时的检查。因此在一般的时候你毫无疑问应该为公共API选择枚举变量。可是当性能方面有所限制的时候,你就应该避免这种作法了。

 

for循环

访问成员变量比访问本地变量慢得多,以下面一段代码:

 

[java]  view plain copy
 
 
  1. for(int i =0; i < this.mCount; i++)  {}  

永远不要在for的第二个条件中调用任何方法,以下面一段代码:

 

[java]  view plain copy
 
 
  1. for(int i =0; i < this.getCount(); i++) {}  

对上面两个例子最好改成:

 

[java]  view plain copy
 
 
  1. int count = this.mCount; / int count = this.getCount();  
  2. for(int i =0; i < count; i++)  {}  
在java1.5中引入的for-each语法。编译器会将对数组的引用和数组的长度保存到本地变量中,这对访问数组元素很是好。 可是编译器还会在每次循环中产生一个额外的对本地变量的存储操做(以下面例子中的变量a),这样会比普通循环多出4个字节,速度要稍微慢一些:

 

[java]  view plain copy
 
 
  1. for (Foo a : mArray) {  
  2.     sum += a.mSplat;  
  3. }  

了解并使用类库

选择Library中的代码而非本身重写,除了一般的那些缘由外,考虑到系统空闲时会用汇编代码调用来替代library方法,这可能比JIT中生成的等价的最好的Java代码还要好。

当你在处理字串的时候,不要吝惜使用String.indexOf()String.lastIndexOf()等特殊实现的方法。这些方法都是使用C/C++实现的,比起Java循环快10到100倍。

System.arraycopy方法在有JIT的Nexus One上,自行编码的循环快9倍。

android.text.format包下的Formatter类,提供了IP地址转换、文件大小转换等方法;DateFormat类,提供了各类时间转换,都是很是高效的方法。

TextUtils类,对于字符串处理Android为咱们提供了一个简单实用的TextUtils类,若是处理比较简单的内容不用去思考正则表达式不妨试试这个在android.text.TextUtils的类

高性能MemoryFile类,不少人抱怨Android处理底层I/O性能不是很理想,若是不想使用NDK则能够经过MemoryFile类实现高性能的文件读写操做。MemoryFile适用于哪些地方呢?对于I/O须要频繁操做的,主要是和外部存储相关的I/O操做,MemoryFile经过将 NAND或SD卡上的文件,分段映射到内存中进行修改处理,这样就用高速的RAM代替了ROM或SD卡,性能天然提升很多,对于Android手机而言同时还减小了电量消耗。该类实现的功能不是不少,直接从Object上继承,经过JNI的方式直接在C底层执行。

 

 

Reuse:

 
Reuse重用,减小内存消耗的重要手段之一。
核心思路就是将已经存在的内存资源从新使用而避免去建立新的,最典型的使用就是 缓存(Cache 池(Pool)
 
Bitmap缓存:
 

Bitmap缓存分为两种:

一种是内存缓存,一种是硬盘缓存。

 

内存缓存(LruCache):

以牺牲宝贵的应用内存为代价,内存缓存提供了快速的Bitmap访问方式。系统提供的LruCache类是很是适合用做缓存Bitmap任务的,它将最近被引用到的对象存储在一个强引用的LinkedHashMap中,而且在缓存超过了指定大小以后将最近不常使用的对象释放掉。

注意:之前有一个很是流行的内存缓存实现是SoftReference(软引用)或者WeakReference(弱引用)的Bitmap缓存方案,然而如今已经不推荐使用了。自Android2.3版本(API Level 9)开始,垃圾回收器更着重于对软/弱引用的回收,这使得上述的方案至关无效。

 

硬盘缓存(DiskLruCache):

 

一个内存缓存对加速访问最近浏览过的Bitmap很是有帮助,可是你不能局限于内存中的可用图片。GridView这样有着更大的数据集的组件能够很轻易消耗掉内存缓存。你的应用有可能在执行其余任务(如打电话)的时候被打断,而且在后台的任务有可能被杀死或者缓存被释放。一旦用户从新聚焦(resume)到你的应用,你得再次处理每一张图片。

在这种状况下,硬盘缓存能够用来存储Bitmap并在图片被内存缓存释放后减少图片加载的时间(次数)。固然,从硬盘加载图片比内存要慢,而且应该在后台线程进行,由于硬盘读取的时间是不可预知的。

注意:若是访问图片的次数很是频繁,那么ContentProvider可能更适合用来存储缓存图片,例如Image Gallery这样的应用程序。

更多关于内存缓存和硬盘缓存的内容请看Google官方教程 https://developer.android.com/develop/index.html
 
图片缓存的开源项目:
对于图片的缓存如今都倾向于使用开源项目,这里我列出几个我搜到的:
 

1. Android-Universal-Image-Loader 图片缓存

目前使用最普遍的图片缓存,支持主流图片缓存的绝大多数特性。
项目地址:https://github.com/nostra13/Android-Universal-Image-Loader

 

2. picasso square开源的图片缓存
项目地址:https://github.com/square/picasso
特色:(1)能够自动检测adapter的重用并取消以前的下载
(2)图片变换
(3)能够加载本地资源
(4)能够设置占位资源
(5)支持debug模式

 

3. ImageCache 图片缓存,包含内存和Sdcard缓存
项目地址:https://github.com/Trinea/AndroidCommon
特色:

(1)支持预取新图片,支持等待队列
(2)包含二级缓存,可自定义文件名保存规则
(3)可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13种)或自定义缓存算法
(4)可方便的保存及初始化恢复数据
(5)支持不一样类型网络处理
(6)可根据系统配置初始化缓存等

4. Android 网络通讯框架Volley
项目地址:https://android.googlesource.com/platform/frameworks/volley
咱们在程序中须要和网络通讯的时候,大致使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient(Apache)等,在2013年的Google I/O发布了Volley。Volley是Android平台上的网络通讯库,能使网络通讯更快,更简单,更健壮。
特色:
(1)JSON,图像等的异步下载;
(2)网络请求的排序(scheduling)
(3)网络请求的优先级处理
(4)缓存
(5)多级别取消请求
(6)和Activity和生命周期的联动(Activity结束时同时取消全部网络请求)
 
Adapter适配器
 
在Android中Adapter使用十分普遍,特别是在list中。因此adapter是数据的  “集散地” ,因此对其进行内存优化是颇有必要的。
下面算是一个标准的使用模版:
主要使用convertView和ViewHolder来进行缓存处理

 

[java]  view plain copy
 
 
  1. @Override  
  2. public View getView(int position, View convertView, ViewGroup parent) {  
  3.     ViewHolder vHolder = null;  
  4.     //若是convertView对象为空则建立新对象,不为空则复用    
  5.     if (convertView == null) {  
  6.         convertView = inflater.inflate(..., null);  
  7.         // 建立 ViewHodler 对象    
  8.         vHolder = new ViewHolder();  
  9.         vHolder.img= (ImageView) convertView.findViewById(...);  
  10.         vHolder.tv= (TextView) convertView.findViewById(...);  
  11.         // 将ViewHodler保存到Tag中(Tag能够接收Object类型对象,因此任何东西均可以保存在其中)  
  12.         convertView.setTag(vHolder);  
  13.     } else {  
  14.         //当convertView不为空时,经过getTag()获得View    
  15.         vHolder = (ViewHolder) convertView.getTag();  
  16.     }  
  17.     // 给对象赋值,修改显示的值    
  18.     vHolder.img.setImageBitmap(...);  
  19.     vHolder.tv.setText(...);  
  20.     return convertView;  
  21. }  
  22. //将显示的View 包装成类    
  23. static class ViewHolder {  
  24.     TextView tv;  
  25.     ImageView img;  
  26. }  
 
池(PooL)
 

对象池:

对象池使用的基本思路是:将用过的对象保存起来,等下一次须要这种对象的时候,再拿出来重复使用,从而在必定程度上减小频繁建立对象所形成的开销。 并不是全部对象都适合拿来池化――由于维护对象池也要形成必定开销。对生成时开销不大的对象进行池化,反而可能会出现“维护对象池的开销”大于“生成新对象的开销”,从而使性能下降的状况。可是对于生成时开销可观的对象,池化技术就是提升性能的有效策略了。

 

 

线程池:

线程池的基本思想仍是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样能够避免反复建立线程对象所带来的性能开销,节省了系统的资源。

好比:一个应用要和网络打交道,有不少步骤须要访问网络,为了避免阻塞主线程,每一个步骤都建立个线程,在线程中和网络交互,用线程池就变的简单,线程池是对线程的一种封装,让线程用起来更加简便,只须要创一个线程池,把这些步骤像任务同样放进线程池,在程序销毁时只要调用线程池的销毁函数便可。

 

java提供了ExecutorServiceExecutors类,咱们能够应用它去创建线程池。

一般能够创建以下4种:

 

[java]  view plain copy
 
 
  1. /** 每次只执行一个任务的线程池 */  
  2. ExecutorService singleTaskExecutor =  Executors.newSingleThreadExecutor();  
  3.   
  4. /** 每次执行限定个数个任务的线程池 */  
  5. ExecutorService limitedTaskExecutor = Executors.newFixedThreadPool(3);  
  6.   
  7. /** 全部任务都一次性开始的线程池 */  
  8. ExecutorService allTaskExecutor = Executors.newCachedThreadPool();  
  9.   
  10. /** 建立一个可在指定时间里执行任务的线程池,亦可重复执行 */  
  11. ExecutorService scheduledTaskExecutor = Executors.newScheduledThreadPool(3);  

 

 

更多关于线程池的内容我推荐这篇文章: http://www.xuanyusong.com/archives/2439
注意:

 

要根据状况适度使用缓存,由于内存有限。

能保存路径地址的就不要存放图片数据,不常用的尽可能不要缓存,不用时就清空。

 

OOM:

 

内存泄露能够引起不少的问题:

1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)

2.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反以内存占用越小,在后台存在的时间就越长)

3.直接崩溃(OutOfMemoryError)

 

ANDROID内存面临的问题:

 

1.有限的堆内存,原始只有16M

2.内存大小消耗等根据设备,操做系统等级,屏幕尺寸的不一样而不一样

3.程序不能直接控制

4.支持后台多任务处理(multitasking)

5.运行在虚拟机之上

 

5R:

本文主要经过以下的5R方法来对ANDROID内存进行优化:

 

1.Reckon(计算)

首先须要知道你的app所消耗内存的状况,知己知彼才能百战不殆

2.Reduce(减小)

消耗更少的资源

 

3.Reuse(重用)

当第一次使用完之后,尽可能给其余的使用

5.Recycle(回收)

回收资源

4.Review(检查)

回顾检查你的程序,看看设计或代码有什么不合理的地方。

 

 

 

内存简介Reckon(计算):

 

关于内存简介,和Reckon的内容请看:ANDROID内存优化(大汇总——上)

 

 

Reduce(减小) ,Reuse(重用):

 

关于Reduce,和Reuse的内容请看:ANDROID内存优化(大汇总——中)

 

 

Recycle(回收):

 

Recycle(回收),回收能够说是在内存使用中最重要的部分。由于内存空间有限,不管你如何优化,如何节省内存总有用完的时候。而回收的意义就在于去清理和释放那些已经闲置,废弃再也不使用的内存资源和内存空间。

由于在Java中有垃圾回收(GC)机制,因此咱们平时都不会太关注它,下面就来简单的介绍一下回收机制:

 

 

垃圾回收(GC):

 

Java垃圾回收器:

在C,C++或其余程序设计语言中,资源或内存都必须由程序员自行声明产生和回收,不然其中的资源将消耗,形成资源的浪费甚至崩溃。但手工回收内存每每是一项复杂而艰巨的工做。

因而,Java技术提供了一个系统级的线程,即垃圾收集器线程(Garbage Collection Thread),来跟踪每一块分配出去的内存空间,当Java 虚拟机(Java Virtual Machine)处于空闲循环时,垃圾收集器线程会自动检查每一快分配出去的内存空间,而后自动回收每一快能够回收的无用的内存块。 

 

做用:

1.清除不用的对象来释放内存:

采用一种动态存储管理技术,它自动地释放再也不被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。当一个对象再也不被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。 

2.消除堆内存空间的碎片:

因为建立对象和垃圾收集器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。 

 

垃圾回收器优势:

1.减轻编程的负担,提升效率:

使程序员从手工回收内存空间的繁重工做中解脱了出来,由于在没有垃圾收集机制的时候,可能要花许多时间来解决一个难懂的存储器问题。在用Java语言编程的时候,靠垃圾收集机制可大大缩短期。

2.它保护程序的完整性:

所以垃圾收集是Java语言安全性策略的一个重要部份。 

 

垃圾回收器缺点:

1.占用资源时间:

Java虚拟机必须追踪运行程序中有用的对象, 并且最终释放没用的对象。这一个过程须要花费处理器的时间。

2.不可预知:

垃圾收集器线程虽然是做为低优先级的线程运行,但在系统可用内存量太低的时候,它可能会突发地执行来挽救内存资源。固然其执行与否也是不可预知的。 

3.不肯定性:

 

不能保证一个无用的对象必定会被垃圾收集器收集,也不能保证垃圾收集器在一段Java语言代码中必定会执行。

一样也没有办法预知在一组均符合垃圾收集器收集标准的对象中,哪个会被首先收集。 

4.不可操做

 

垃圾收集器不能够被强制执行,但程序员能够经过调用System. gc方法来建议执行垃圾收集器。
 
垃圾回收算法:
 
1.引用计数(Reference Counting) 
比较古老的回收算法。原理是此对象有一个引用,即增长一个计数,删除一个引用则减小一个计数。垃圾回收时,只用收集计数为0的对象。此算法最致命的是没法处理循环引用的问题。 
2.标记-清除(Mark-Sweep) 
此算法执行分两阶段。第一阶段从引用根节点开始标记全部被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法须要暂停整个应用,同时,会产生内存碎片。
3.复制(Copying) 
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另一个区域中。次算法每次只处理正在使用中的对象,所以复制成本比较小,同时复制过去之后还能进行相应的内存整理,不过出现“碎片”问题。固然,此算法的缺点也是很明显的,就是须要两倍内存空间。
4.标记-整理(Mark-Compact) 
此算法结合了 “标记-清除”和“复制”两个算法的优势。也是分两阶段,第一阶段从根节点开始标记全部被引用对象,第二阶段遍历整个堆,把清除未标记对象而且把存活对象 “压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。 
5.增量收集(Incremental Collecting) 
实施垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么缘由JDK5.0中的收集器没有使用这种算法的。 
6.分代(Generational Collecting) 
基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不一样生命周期的对象使用不一样的算法(上述方式中的一个)进行回收。如今的垃圾回收器(从J2SE1.2开始)都是使用此算法的。 
 
 

finalize():

每个对象都有一个finalize方法,这个方法是从Object类继承来的。 

当垃圾回收肯定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

 

Java 技术容许使用finalize方法在垃圾收集器将对象从内存中清除出去以前作必要的清理工做。一旦垃圾回收器准备好释放对象占用的空间,将首先调用其finalize()方法,而且在下一次垃圾回收动做发生时,才会真正回收对象占用的内存。
简单的说finalize方法是在垃圾收集器删除对象以前对这个对象调用的


System.gc():

咱们能够调用System.gc方法,建议虚拟机进行垃圾回收工做(注意,是建议,但虚拟机会不会这样干,咱们也没法预知!)

 

下面来看一个例子来了解finalize()System.gc()的使用:

 

[java]  view plain  copy
 
 print?
  1. public class TestGC {  
  2.     public TestGC() {}  
  3.       
  4.     //当垃圾回收器肯定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。  
  5.     protected void finalize() {  
  6.         System.out.println("我已经被垃圾回收器回收了...");  
  7.     }  
  8.       
  9.     public static void main(String [] args) {  
  10.         TestGC gc = new TestGC();  
  11.         gc = null;    
  12.         // 建议虚拟机进行垃圾回收工做  
  13.         System.gc();  
  14.     }  
  15. }  
如上面的例子所示,你们能够猜猜重写的finalize方法会不会执行?

 

答案是:不必定

 

由于不管是设置gc的引用为null仍是调用System.gc()方法都只是"建议"垃圾回收器进行垃圾回收,可是最终全部权还在垃圾回收器手中,它会不会进行回收咱们没法预知!

垃圾回收面试题:
最后经过网上找到的3道面试题来结束垃圾回收的内容。

 

面试题一: 

 

[java]  view plain  copy
 
 print?
  1. 1.fobj = new Object ( ) ;   
  2. 2.fobj. Method ( ) ;   
  3. 3.fobj = new Object ( ) ;   
  4. 4.fobj. Method ( ) ;   

 

问:这段代码中,第几行的fobj 符合垃圾收集器的收集标准? 
答:第3行。由于第3行的fobj被赋了新值,产生了一个新的对象,即换了一块新的内存空间,也至关于为第1行中的fobj赋了null值。这种类型的题是最简单的。 

面试题二: 
[java]  view plain  copy
 
 print?
  1. 1.Object sobj = new Object ( ) ;   
  2. 2.Object sobj = null ;   
  3. 3.Object sobj = new Object ( ) ;   
  4. 4.sobj = new Object ( ) ;   
问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准? 
答:第2行和第4行。由于第2行为sobj赋值为null,因此在此第1行的sobj符合垃圾收集器的收集标准。而第4行至关于为sobj赋值为null,因此在此第3行的sobj也符合垃圾收集器的收集标准。 

若是有一个对象的句柄a,且你把a做为某个构造器的参数,即 new Constructor ( a )的时候,即便你给a赋值为null,a也不符合垃圾收集器的收集标准。直到由上面构造器构造的新对象被赋空值时,a才能够被垃圾收集器收集。 

面试题三: 
[java]  view plain  copy
 
 print?
  1. 1.Object aobj = new Object ( ) ;   
  2. 2.Object bobj = new Object ( ) ;   
  3. 3.Object cobj = new Object ( ) ;   
  4. 4.aobj = bobj;   
  5. 5.aobj = cobj;   
  6. 6.cobj = null;   
  7. 7.aobj = null;   
问:这段代码中,第几行的内存空间符合垃圾收集器的收集标准? 
答:第4,7行。注意这类题型是认证考试中可能遇到的最难题型了。 
行1-3:分别建立了Object类的三个对象:aobj,bobj,cobj
行4:此时对象aobj的句柄指向bobj,原来aojb指向的对象已经没有任何引用或变量指向,这时,就符合回收标准。
行5:此时对象aobj的句柄指向cobj,因此该行的执行不能使aobj符合垃圾收集器的收集标准。 
行6:此时仍没有任何一个对象符合垃圾收集器的收集标准。 
行7:对象cobj符合了垃圾收集器的收集标准,由于cobj的句柄指向单一的地址空间。在第6行的时候,cobj已经被赋值为null,但由cobj同时还指向了aobj(第5行),因此此时cobj并不符合垃圾收集器的收集标准。而在第7行,aobj所指向的地址空间也被赋予了空值null,这就说明了,由cobj所指向的地址空间已经被彻底地赋予了空值。因此此时cobj最终符合了垃圾收集器的收集标准。 但对于aobj和bobj,仍然没法判断其是否符合收集标准。 

总之,在Java语言中,判断一块内存空间是否符合垃圾收集器收集的标准只有两个: 
1.给对象赋予了空值null,如下再没有调用过。 
2.给对象赋予了新值,既从新分配了内存空间。 

最后再次提醒一下,一块内存空间符合了垃圾收集器的收集标准,并不意味着这块内存空间就必定会被垃圾收集器收集。

 

 

 

资源的回收:

刚才讲了一堆理论的东西,下面来点实际能用上的,资源的回收:

 

Thread(线程)回收:

线程中涉及的任何东西GC都不能回收(Anything reachable by a thread cannot be GC'd ),因此线程很容易形成内存泄露。

以下面代码所示:

 

[java]  view plain  copy
 
 print?
  1. Thread t = new Thread() {  
  2.     public void run() {  
  3.         while (true) {  
  4.             try {  
  5.                 Thread.sleep(1000);  
  6.                 System.out.println("thread is running...");  
  7.             } catch (InterruptedException e) {  
  8.               
  9.             }  
  10.         }  
  11.     }  
  12. };  
  13. t.start();  
  14. t = null;  
  15. System.gc();  
如上在线程t中每间隔一秒输出一段话,而后将线程设置为null而且调用System.gc方法。

 

最后的结果是线程并不会被回收,它会一直的运行下去。

 

 

由于运行中的线程是称之为垃圾回收根(GC Roots)对象的一种,不会被垃圾回收。当垃圾回收器判断一个对象是否可达,老是使用垃圾回收根对象做为参考点。

 

Cursor(游标)回收:

 

Cursor是Android查询数据后获得的一个管理数据集合的类,在使用结束之后。应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。而且Android明显是倾向于编程者手动的将Cursor close掉,由于在源代码中咱们发现,若是等到垃圾回收器来回收时,会给用户以错误提示。

因此咱们使用Cursor的方式通常以下:

 

[java]  view plain  copy
 
 print?
  1. Cursor cursor = null;  
  2. try {  
  3.     cursor = mContext.getContentResolver().query(uri,null, null,null,null);  
  4.     if(cursor != null) {  
  5.         cursor.moveToFirst();  
  6.         //do something  
  7.     }  
  8. catch (Exception e) {  
  9.     e.printStackTrace();  
  10. finally {  
  11.     if (cursor != null) {  
  12.         cursor.close();  
  13.     }  
  14. }  
有一种状况下,咱们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的状况,可是注意,CursorAdapter在Acivity结束时并无自动的将Cursor关闭掉,所以,你须要在onDestroy函数中,手动关闭。
[java]  view plain  copy
 
 print?
  1. @Override    
  2. protected void onDestroy() {          
  3.     if (mAdapter != null && mAdapter.getCurosr() != null) {    
  4.         mAdapter.getCursor().close();    
  5.     }    
  6.     super.onDestroy();     
  7. }    

 

Receiver(接收器)回收

 

调用registerReceiver()后未调用unregisterReceiver(). 
当咱们Activity中使用了registerReceiver()方法注册了BroadcastReceiver,必定要在Activity的生命周期内调用unregisterReceiver()方法取消注册 
也就是说registerReceiver()和unregisterReceiver()方法必定要成对出现,一般咱们能够重写Activity的onDestory()方法: 
[java]  view plain  copy
 
 print?
  1. @Override    
  2. protected void onDestroy() {    
  3.       this.unregisterReceiver(receiver);    
  4.       super.onDestroy();    
  5. }    
 

Stream/File(流/文件)回收:

主要针对各类流,文件资源等等如:

InputStream/OutputStream,SQLiteOpenHelper,SQLiteDatabase,Cursor,文件,I/O,Bitmap图片等操做等都应该记得显示关闭。
和以前介绍的Cursor道理相似,就很少说了。

 

 

 

 

Review:

 

Review(回顾,检查),你们都知道Code Review的重要性。而这里我说的Review和Code Review差很少,主要目的就是检查代码中存在的不合理和能够改进的地方,固然这个Review须要你们本身来作啦。

 

 

Code Review(代码检查):

Code Review主要检查代码中存在的一些不合理或能够改进优化的地方,你们能够参考以前写的Reduce,Reuse和Recycle都是侧重讲解这方面的。

 

 

 

UI Review(视图检查):

Android对于视图中控件的布局渲染等会消耗不少的资源和内存,因此这部分也是咱们须要注意的。

 

减小视图层级:
减小视图层级能够有效的减小内存消耗,由于视图是一个树形结构,每次刷新和渲染都会遍历一次。
 
hierarchyviewer:
想要减小视图层级首先就须要知道视图层级,因此下面介绍一个SDK中自带的一个很是好用的工具hierarchyviewer。
你能够在下面的地址找到它: your sdk path\sdk\tools

 

如上图你们能够看到,hierarchyviewer能够很是清楚的看到当前视图的层级结构,而且能够查看视图的执行效率(视图上的小圆点,绿色表示流畅,黄色和红色次之),因此咱们能够很方便的查看哪些view可能会影响咱们的性能从而去进一步优化它。

 

hierarchyviewer还提供另一种列表式的查看方式,能够查看详细的屏幕画面,具体到像素级别的问题均可以经过它发现。

 

 

ViewStub标签

此标签可使UI在特殊状况下,直观效果相似于设置View的不可见性,可是其更大的意义在于被这个标签所包裹的Views在默认状态下不会占用任何内存空间。

 

 

include标签

能够经过这个标签直接加载外部的xml到当前结构中,是复用UI资源的经常使用标签。

 

merge标签

它在优化UI结构时起到很重要的做用。目的是经过删减多余或者额外的层级,从而优化整个Android Layout的结构。

 

(注意:灵活运用以上3个标签能够有效减小视图层级,具体使用你们能够上网搜搜)

 

 

布局用Java代码比写在XML中快

通常状况下对于Android程序布局每每使用XML文件来编写,这样能够提升开发效率,可是考虑到代码的安全性以及执行效率,能够经过Java代码执行建立,虽然Android编译过的XML是二进制的,可是加载XML解析器的效率对于资源占用仍是比较大的,Java处理效率比XML快得多,可是对于一个复杂界面的编写,可能须要一些套嵌考虑,若是你思惟灵活的话,使用Java代码来布局你的Android应用程序是一个更好的方法。

 
 

重用系统资源:

 

1. 利用系统定义的id

好比咱们有一个定义ListView的xml文件,通常的,咱们会写相似下面的代码片断。

 

[html]  view plain  copy
 
 print?
  1. <ListView  
  2.     android:id="@+id/mylist"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"/>  

 

这里咱们定义了一个ListView,定义它的id是"@+id/mylist"。实际上,若是没有特别的需求,就能够利用系统定义的id,相似下面的样子。

 

[html]  view plain  copy
 
 print?
  1. <ListView  
  2.     android:id="@android:id/list"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"/>  
在xml文件中引用系统的id,只须要加上“@android:”前缀便可。若是是在Java代码中使用系统资源,和使用本身的资源基本上是同样的。不一样的是,须要使用android.R类来使用系统的资源,而不是使用应用程序指定的R类。这里若是要获取ListView可使用android.R.id.list来获取。

 

2. 利用系统的图片资源

这样作的好处,一个是美工不须要重复的作一份已有的图片了,能够节约很多工时;另外一个是能保证咱们的应用程序的风格与系统一致。

3. 利用系统的字符串资源

若是使用系统的字符串,默认就已经支持多语言环境了。如上述代码,直接使用了@android:string/yes和@android:string/no,在简体中文环境下会显示“肯定”和“取消”,在英文环境下会显示“OK”和“Cancel”。

4. 利用系统的Style

 假设布局文件中有一个TextView,用来显示窗口的标题,使用中等大小字体。可使用下面的代码片断来定义TextView的Style。

 

[html]  view plain  copy
 
 print?
  1. <TextView  
  2.         android:id="@+id/title"  
  3.         android:layout_width="wrap_content"  
  4.         android:layout_height="wrap_content"  
  5.         android:textAppearance="?android:attr/textAppearanceMedium" />  
其中android:textAppearance="?android:attr/textAppearanceMedium"就是使用系统的style。须要注意的是,使用系统的style,须要在想要使用的资源前面加“?android:”做为前缀,而不是“@android:”。

 

5. 利用系统的颜色定义

除了上述的各类系统资源之外,还可使用系统定义好的颜色。在项目中最经常使用的,就是透明色的使用。

 

[html]  view plain  copy
 
 print?
  1. android:background ="@android:color/transparent"  

 

除了上面介绍的之外还有不少其余Android系统自己自带的资源,它们在应用中均可以直接使用。具体的,能够进入android-sdk的相应文件夹中去查看。例如:能够进入$android-sdk$\platforms\android-8\data\res,里面的系统资源就尽收眼底了。

开发者须要花一些时间去熟悉这些资源,特别是图片资源和各类Style资源,这样在开发过程当中,能重用的尽可能重用,并且有时候使用系统提供的效果可能会更好。

 

 

其余小tips:

1. 分辨率适配-ldpi,-mdpi, -hdpi配置不一样精度资源,系统会根据设备自适应,包括drawable, layout,style等不一样资源。

2.尽可能使用dp(density independent pixel)开发,不用px(pixel)。

3.多用wrap_content, match_parent

4.永远不要使用AbsoluteLayout

5.使用9patch(经过~/tools/draw9patch.bat启动应用程序),png格式

6.将Acitivity中的Window的背景图设置为空。getWindow().setBackgroundDrawable(null);android的默认背景是否是为空。

7.View中设置缓存属性.setDrawingCache为true。


 

 

Desgin Review(设计检查):

Desgin Review主要侧重检查一下程序的设计是否合理,包括框架的设计,界面的设计,逻辑的设计(其实这些东西开发以前就应该想好了)。

 

框架设计:

是否认义了本身的Activity和fragment等经常使用控件的基类去避免进行重复的工做

是否有完善的异常处理机制,即便真的出现OOM也不会直接崩溃致使直接退出程序

 

界面设计:

 

1.在视图中加载你所须要的,而不是你所拥有。由于用户不可能同时看到全部东西。最典型的例子就是ListView中的滑动加载。

2.若是数据特别大,此时应该暗示用户去点击加载,而不是直接加载。

3.合理运用分屏,转屏等,它是个双刃剑,由于它便可以使程序更加美观功能更加完善,但也相应增长了资源开销。

 

逻辑设计:

避免子类直接去控制父类中内容,可使用监听等方式去解决

相关文章
相关标签/搜索