《老罗Android第二季》Bitmap位图解码

1. Bitmap位图解码
 Android设备系统资源是有限的,会给单独的应用分配大概16MB的内存。如果Bitmap资源太大,会造成内存溢出。
示例:让一个很大的图片,以一个比例缩小后显示在一个ImageView中。
1. 在Activity中,点击按钮显示图片 
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
	Bitmap bm = BitmapTools.decodeBitmap(getResources(),
			R.drawable.a, 50, 50);
	imageView.setImageBitmap(bm);
	}
});
2. 定义一个Bitmap工具类
public class BitmapTools {
public BitmapTools() {
}
/**@param resources        资源文件
 * @param resId            解码位图的id
 * @param reqWith          指定输出位图的宽度
 * @param reqHeight        指定输出位图的高度
 */
public static Bitmap decodeBitmap(Resources resources, int resId,
		int reqWith, int reqHeight) {
	// 对位图进行解码的参数设置
	BitmapFactory.Options options = new BitmapFactory.Options();
	// 在对位图进行解码的过程中,避免申请内存空间
	options.inJustDecodeBounds = true;
	BitmapFactory.decodeResource(resources, resId, options);
	// 对图片进行一定比例的压缩处理
	options.inSampleSize = calculateInSimpleSize(options, reqWith,reqHeight);
	options.inJustDecodeBounds = false;// 真正输出位图
	return BitmapFactory.decodeResource(resources, resId, options);
}
public static int calculateInSimpleSize(BitmapFactory.Options options,
		int reqWith, int reqHeight) {
	// 获得图片的原始宽高
	int imageHeight = options.outHeight;
	int imageWidth = options.outWidth;
	int inSimpleSize = 1;// 压缩比例
	if (imageHeight > reqHeight || imageWidth > reqWith) {
		final int heightRatio = Math.round((float) imageHeight/(float) reqHeight);
		final int widthRatio = Math.round((float)imageWidth/(float)reqWith);
		inSimpleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
	}
	return inSimpleSize;
}
}
假如要用到一张1024*768像素的图片,它最终是以128*96像素显示在一个ImageView当中的。
这就要decoder对图片做二次抽样,导入一张小版本的图片到内存中,并设置BitmapFactory.Options的 inSampleSize为true.这个值表示压缩的比较,如为3,就为原来的1/3。

2. Bitmap UI加载大位图处理
加载位图的时间是不确定的,一旦这些任务阻塞了UI线程,那么Ui就无反应了。可以用异步任务来完成。
这个例子要实现的效果:定义一个数组保存了大量大像素的网络图片的地址,通过异步任务把它们下载下来,并进行解码。在Activity中显示上下划动时,看不见的就释放掉,重新加载图片。
1) 图片下载工具类
public class HttpUtils {
	public HttpUtils() {
	}
	public static byte[] sendPost(String path) {
		HttpClient httpClient = new DefaultHttpClient();
		HttpPost httpPost = new HttpPost(path);
		HttpResponse response = null;
		try {
			response = httpClient.execute(httpPost);
			if (response.getStatusLine().getStatusCode() == 200) {
				return EntityUtils.toByteArray(response.getEntity());
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			httpClient.getConnectionManager().shutdown();
		}
		return null;
	}
}
2)图片解码类与上面的一样,但是图片来源不同,有些小修改
public static Bitmap decodeBitmap(byte[] data, int reqWith, int reqHeight) {
	// 对位图进行解码的参数设置
	BitmapFactory.Options options = new BitmapFactory.Options();
	// 在对位图进行解码的过程中,避免申请内存空间
	options.inJustDecodeBounds = true;
	BitmapFactory.decodeByteArray(data, 0, data.length, options);
	// 对图片进行一定比例的压缩处理
	options.inSampleSize = calculateInSimpleSize(options, reqWith,reqHeight);
	options.inJustDecodeBounds = false;// 真正输出位图
	return BitmapFactory.decodeByteArray(data, 0, data.length, options);
}
3)Activity
public class MainActivity extends Activity {
private ListView listView;
// 图片的链接
private String[] imageUrls = Images.imageUrls;
private ImageAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	listView = (ListView) this.findViewById(R.id.listView1);
	adapter = new ImageAdapter();
	listView.setAdapter(adapter);
	adapter.notifyDataSetChanged();
}
public class ImageAdapter extends BaseAdapter {
	@Override
	public int getCount() {
		return imageUrls.length;
	}
	@Override
	public Object getItem(int position) {
		return imageUrls[position];
	}
	public long getItemId(int position) {
		return position;
	}
	public View getView(int position, View convertView, ViewGroup parent) {
		View view = null;
		if (convertView == null) {
			view = LayoutInflater.from(MainActivity.this).inflate(
					R.layout.item, null);
		} else {
			view = convertView;
		}
		ImageView imageView = (ImageView) view.findViewById(R.id.imageView1);
		// 从网络中获取数据,填充到imageview中
		// 可能会造成图片的错位:
		loadBitmap(imageUrls[position], imageView);
		return view;
	}
}
/** * 在滑动这些ListView的时候,会对就的布局进行资源回收,如果ListView结合异步任务操作的时候,不能确保重用的布局被及时回收
 */
static class AsyncDrawable extends BitmapDrawable {
	private final SoftReference softReference;
	public AsyncDrawable(Resources resources, Bitmap bitmap,
			BitmapWorkerTask bitmapWorkerTask) {
		super(resources, bitmap);
	softReference = new SoftReference(bitmapWorkerTask);
	}
	public BitmapWorkerTask getBitmapWorkerTask() {
		return softReference.get();
	}
}
/** 异步任务 */
class BitmapWorkerTask extends AsyncTask {
	private SoftReference imageSoftReference;
	private String data = "";
	public BitmapWorkerTask(ImageView imageView) {
		imageSoftReference = new SoftReference(imageView);
	}
	protected Bitmap doInBackground(String... params) {
		data = params[0];
		byte[] result = HttpUtils.sendPost(data);
		// 解码过程
		return BitmapTools.decodeBitmap(result, 100, 100);
	}
	protected void onPostExecute(Bitmap bitmap) {
		super.onPostExecute(bitmap);
		if (isCancelled()) {
			bitmap = null;
		}
		if (imageSoftReference != null && bitmap != null) {
			final ImageView imageView = imageSoftReference.get();
			final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
			if (this == bitmapWorkerTask && imageView != null) {
				imageView.setImageBitmap(bitmap);
			}
		}
	}
}
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
	if (imageView != null) {
		final Drawable drawable = imageView.getDrawable();
		if (drawable instanceof AsyncDrawable) {
			final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
			return asyncDrawable.getBitmapWorkerTask();
		}
	}
	return null;
}
public static boolean cancelPotntialWork(String data, ImageView imageView) {
	final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
	if (bitmapWorkerTask != null) {
		final String bitmapData = bitmapWorkerTask.data;
		if (bitmapData != data) {
			bitmapWorkerTask.cancel(true);
		} else {
			return false;
		}
	}
	return true;
}
/**
 * 加载图片 */
public void loadBitmap(String data, ImageView imageView) {
	Bitmap placeBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.empty_photo);
	if (cancelPotntialWork(data, imageView)) {
		final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
		final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), placeBitmap, task);
		imageView.setImageDrawable(asyncDrawable);
		task.execute(data);
	}
}
}

getView()中convertView被不断的重复使用,可能会导致旧的引用不会被及时释放,从而造成图片错位的问题。
并发的问题:
普通的View,如ListView 和GridView在联合异步任务加载资源的时候,可能会导致图片错位和占用内存过大的问题。
在view划动的过程中,系统会对view资源进行回收。要被回收的view,使用到异步任务加载的时候,就不能确保它会被及时回收。因为异步任务和view是分别在两个不同的线程中的。所以这里的话使用了SoftReference来保证资源能够被回收。

你可能感兴趣的:(android)