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来保证资源能够被回收。