当我们在做项目过程中,一遇到显示图片时,就要考虑图片的大小,所占内存的大小,原因就是Android分配给Bitmap的大小只有8M,试想想我们用手机拍照,普通的一张照片不也得1M以上,所以android处理图片时不得不考虑图片过大造成的内存异常。
那时候只是简单地缓存图片到本地 然后将图片进行压缩,但是感觉这个问题没有很好的解决办法,只是减小了发生的几率
这里,我将前辈们解决的方法重新整理一番,方便自己以后使用。
1.在内存引用上做些处理,常用的有软引用、强化引用、弱引用(可以参考这篇博客:http://smallwoniu.blog.51cto.com/blog/3911954/1248751)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;
public class Test {
public static boolean isRun = true ;
public static void main( String [] args) throws Exception {
String abc = new String ( "abc" );
System.out.println(abc.getClass() + "@" + abc.hashCode());
final ReferenceQueue referenceQueue = new ReferenceQueue< String >();
new Thread() {
public void run() {
while (isRun) {
Object o = referenceQueue.poll();
if (o != null ) {
try {
Field rereferent = Reference. class
.getDeclaredField( "referent" );
rereferent.setAccessible( true );
Object result = rereferent. get (o);
System.out.println( "gc will collect:"
+ result.getClass() + "@"
+ result.hashCode());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}.start();
PhantomReference< String > abcWeakRef = new PhantomReference< String >(abc,
referenceQueue);
abc = null ;
Thread.currentThread().sleep( 3000 );
System.gc();
Thread.currentThread().sleep( 3000 );
isRun = false ;
}
} |
结果:
1
2
|
class java.lang. String @ 96354
gc will collect: class java.lang. String @ 96354
|
2.在内存中加载图片时直接在内存中做处理
A.边界压缩
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
@SuppressWarnings( "unused" )
private Bitmap copressImage( String imgPath){
File picture = new File(imgPath);
Options bitmapFactoryOptions = new BitmapFactory.Options();
//下面这个设置是将图片边界不可调节变为可调节
bitmapFactoryOptions.inJustDecodeBounds = true ;
bitmapFactoryOptions.inSampleSize = 2 ;
int outWidth = bitmapFactoryOptions.outWidth;
int outHeight = bitmapFactoryOptions.outHeight;
bmap = BitmapFactory.decodeFile(picture.getAbsolutePath(),
bitmapFactoryOptions);
float imagew = 150 ;
float imageh = 150 ;
int yRatio = ( int ) Math.ceil(bitmapFactoryOptions.outHeight
/ imageh);
int xRatio = ( int ) Math
.ceil(bitmapFactoryOptions.outWidth / imagew);
if (yRatio > 1 || xRatio > 1 ) {
if (yRatio > xRatio) {
bitmapFactoryOptions.inSampleSize = yRatio;
} else {
bitmapFactoryOptions.inSampleSize = xRatio;
}
}
bitmapFactoryOptions.inJustDecodeBounds = false ; //false --- allowing the caller to query the bitmap without having to allocate the memory for its pixels.
bmap = BitmapFactory.decodeFile(picture.getAbsolutePath(),
bitmapFactoryOptions);
if (bmap != null ){
//ivwCouponImage.setImageBitmap(bmap);
return bmap;
}
return null ;
} |
B.边界压缩的情况下间接的使用了软引用来避免OOM
1
2
3
4
5
6
7
8
9
10
11
12
|
/* 自定义Adapter中部分代码*/ public View getView( int position, View convertView, ViewGroup parent) {
File file = new File(it. get (position));
SoftReference<Bitmap> srf = imageCache. get (file.getName());
Bitmap bit = srf. get ();
ImageView i = new ImageView(mContext);
i.setImageBitmap(bit);
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setLayoutParams( new Gallery.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT));
return i;
}
|
但大家都知道,这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,如果图片多且大,这种方式还是会引用OOM异常的,因此需要进一步处理:
A.第一种方式
1
2
3
4
5
6
7
8
9
|
InputStream is = this .getResources().openRawResource(R.drawable.pic1);
BitmapFactory.Options options= new BitmapFactory.Options();
options.inJustDecodeBounds = false ;
options.inSampleSize = 10 ; //width,hight设为原来的十分一
Bitmap btp =BitmapFactory.decodeStream( is , null ,options);
if (!bmp.isRecycle() ){
bmp.recycle() //回收图片所占的内存
system.gc() //提醒系统及时回收
} |
B.第二中方式
1
2
3
4
5
6
7
8
9
10
11
12
|
/** * 以最省内存的方式读取本地资源的图片 * */ public static Bitmap readBitMap(Context context, int resId){
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true ;
opt.inInputShareable = true ;
//获取资源图片
InputStream is = context.getResources().openRawResource(resId);
return BitmapFactory.decodeStream( is , null ,opt);
}
|
C.在适当的时候垃圾回收
1
2
|
if (bitmapObject.isRecycled()== false ) //如果没有回收
bitmapObject.recycle();
|
D.优化Dalvik虚拟机的堆内存分配
对于Android平台来说,其托管层使用的Dalvik JavaVM从目前的表现来看还有很多地方可以优化处理,eg我们在开发一些大型游戏或耗资源的应用中可能考虑手动干涉GC处理,使用 dalvik.system.VMRuntime类提供的setTargetHeapUtilization方法可以增强程序堆内存的处理效率。
1
2
3
4
|
private final static floatTARGET_HEAP_UTILIZATION = 0 .75f;
//在程序onCreate时就可以调用 VMRuntime.getRuntime().setTargetHeapUtilization(TARGET_HEAP_UTILIZATION); 即可 |
至于上面为何是0.75,是因为堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。
E.自定义我们的应用需要多大的内存
1
2
3
|
private final static int CWJ_HEAP_SIZE = 6 * 1024 * 1024 ;
//设置最小heap内存为6MB大小
VMRuntime.getRuntime().setMinimumHeapSize(CWJ_HEAP_SIZE); |
以上这些就是本人总结的一些解决OOM异常的方法,希望能帮助到大家!
参考博客:http://blog.sina.com.cn/s/blog_7501670601014dcj.html