欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android 内存浅析【泄漏、溢出】【二】

程序员文章站 2022-07-02 12:29:50
...
内存溢出之Bitmap
可以说出现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();   
	}