Android 内存浅析【泄漏、溢出】【二】
程序员文章站
2022-07-02 12:29:50
...
内存溢出之Bitmap
可以说出现OutOfMemory问题的绝大多数人,都是因为Bitmap的问题。因为Bitmap占用的内存实在是太多了,它是一个“超级大胖子”,特别是分辨率大的图片,如果要显示多张那问题就更显著了。
如何解决Bitmap带给我们的内存问题?
第一、及时销毁。
有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。
内存溢出之Adapter
构造 Adapter时,没有使用缓存的 convertView
以构造ListView的 BaseAdapter 为例,在BaseAdapter 中提共了方public View getView(int position, View convertView, ViewGroup pare来向ListView提供每一个item所需要的view对象。初始时ListBaseAdapter 中根据当前的屏幕布局实例化一定数量的view对象,同些view对象缓存起来。当向上滚动ListView时,原先位于最上面的象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程方法完成的,getView()的第二个形参 View convertView就是被缓存起view对象(初始化时缓存中没有view对象则convertView是null)。
由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力,如果垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,造成不必要的内存开支。构造 Adapter时,没有使用缓存的 convertView
以构造ListView的 BaseAdapter 为例,在BaseAdapter 中提共了方法:
内存溢出之Cursor
Cursor是Android查询数据后得到的一个管理数据集合的类,正常情况下,操作小数据查询时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉。然而如果Cursor的数据量特别大,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android 文档中提倡开发者手动的关闭Cursor。
但在CursorAdapter中应用的情况下,必须注意,CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭:
可以说出现OutOfMemory问题的绝大多数人,都是因为Bitmap的问题。因为Bitmap占用的内存实在是太多了,它是一个“超级大胖子”,特别是分辨率大的图片,如果要显示多张那问题就更显著了。
如何解决Bitmap带给我们的内存问题?
第一、及时销毁。
虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制。因此,在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。所以Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才它设置为null. 虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试结果显示它并没能立即释放内存。
if(!bitmap.isRecycled()){
bitmap.recycle();
bitmap = null;
}
第二、设置一定的采样率。手机就那么屁大的屏幕,所以有时候我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。如下面的代码:
public ImageView iv ;
public void example() {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;// 图片宽高都为原来的二分之一,即图片为原来的四分之一。数值越大图片越模糊.你懂的
//Uri u = Uri.parse( "content://media/internal/audio/media/81" );
// Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(Uri.parse(uriString)), null, options);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher, options);
iv.setImageBitmap(bitmap);
}
第三、巧妙的运用软引用(SoftRefrence)有些时候,我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。
内存溢出之Adapter
构造 Adapter时,没有使用缓存的 convertView
以构造ListView的 BaseAdapter 为例,在BaseAdapter 中提共了方public View getView(int position, View convertView, ViewGroup pare来向ListView提供每一个item所需要的view对象。初始时ListBaseAdapter 中根据当前的屏幕布局实例化一定数量的view对象,同些view对象缓存起来。当向上滚动ListView时,原先位于最上面的象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程方法完成的,getView()的第二个形参 View convertView就是被缓存起view对象(初始化时缓存中没有view对象则convertView是null)。
由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力,如果垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,造成不必要的内存开支。构造 Adapter时,没有使用缓存的 convertView
以构造ListView的 BaseAdapter 为例,在BaseAdapter 中提共了方法:
public View getView(int position, View convertView, ViewGroup parent) 来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter 中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参 View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。
所以综合以上的理论,写了一个小例子,表达一下:
/**
*
******************************************
* @author 老牛比
* 文件名称 : ExampleAdapter.java
* 创建时间 : 2012-10-1 下午11:11:28
* 文件描述 : 优化小例子
******************************************
*/
public class ExampleAdapter extends BaseAdapter {
private ArrayList<SoftReference<Bitmap>> mBitmaps = new ArrayList<SoftReference<Bitmap>>();
private ArrayList<Object> mValues;
private LayoutInflater mInflater;
public ExampleAdapter(ArrayList<SoftReference<Bitmap>> mBitmaps,
ArrayList<Object> mValues, Context mContext,
LayoutInflater mInflater) {
super();
this.mValues = mValues;
this.mInflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public int getCount() {
return mValues.size();
}
public Object getItem(int position) {
return mValues.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
Bitmap bitmap = BitmapFactory.decodeFile(mValues.get(position).XXX);
mBitmaps.add(new SoftReference<Bitmap>(bitmap)); // 此处加入ArrayList
if (convertView == null) {
convertView = mInflater.inflate(R.layout.example, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText("xxx");
holder.icon.setImageBitmap(bitmap);
return convertView;
}
/**
*
******************************************
* @author 老牛比
* 文件名称 : ViewHolder.java
* 创建时间 : 2012-10-1 下午10:35:25
* 文件描述 : 使用 ViewHolder 模式, 效率再提高 50%
******************************************
*/
class ViewHolder {
TextView text;
ImageView icon;
}
}
内存溢出之Cursor
Cursor是Android查询数据后得到的一个管理数据集合的类,正常情况下,操作小数据查询时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉。然而如果Cursor的数据量特别大,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android 文档中提倡开发者手动的关闭Cursor。
所以我们最好这样使用Cursor:
public void example() {
Cursor mCursor = null;
try {
mCursor = mContext.getContentResolver().query(uri, null, null,
null, null);
if (mCursor != null) {
mCursor.moveToFirst();
// TODO:do something
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCursor != null) {
mCursor.close();
}
}
}
但在CursorAdapter中应用的情况下,必须注意,CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭:
@Override
protected void onDestroy() {
if (mAdapter != null && mAdapter.getCurosr() != null) {
mAdapter.getCursor().close();
}
super.onDestroy();
}