AdapterView中异步加载图片,能有效防止OOM

来源:互联网 发布:js姓名正则表达式 编辑:程序博客网 时间:2024/05/17 03:40

根据官网上的异步加载图片方案改编

1. 首先,写一个有效的缓存类,缓存图片,该类缓存设置上限8M,占用的空间超过8M,会回收最先加进来的元素,

用lruCache作为主要的缓存工具。

import java.lang.ref.SoftReference;import java.util.LinkedHashMap;import android.graphics.Bitmap;import android.support.v4.util.LruCache;import android.util.Log;public class MemoryLruCache {private final int hardCachedSize = 8 * 1024 * 1024;// hard cacheprivate final LruCache<String, Bitmap> sHardBitmapCache = new LruCache<String, Bitmap>(hardCachedSize) {@Overridepublic int sizeOf(String key, Bitmap value) {return value.getRowBytes() * value.getHeight();}@Overrideprotected void entryRemoved(boolean evicted, String key,Bitmap oldValue, Bitmap newValue) {Log.v("tag", "hard cache is full , push to soft cache");sSoftBitmapCache.put(key, new SoftReference<Bitmap>(oldValue));}};private static final int SOFT_CACHE_CAPACITY = 40;@SuppressWarnings("serial")/** * LinkedHashMap保存了数据的插入顺序,遍历出来的顺序与插入的顺序相符 */private final static LinkedHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_CAPACITY, 0.75f, true) {@Overridepublic SoftReference<Bitmap> put(String key, SoftReference<Bitmap> value) {return super.put(key, value);}};public boolean putBitmap(String key, Bitmap bitmap) {if (bitmap != null) {synchronized (sHardBitmapCache) {sHardBitmapCache.put(key, bitmap);}return true;}return false;}public Bitmap getBitmap(String key) {synchronized (sHardBitmapCache) {final Bitmap bitmap = sHardBitmapCache.get(key);if (bitmap != null)return bitmap;}synchronized (sSoftBitmapCache) {SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(key);if (bitmapReference != null) {final Bitmap bitmap2 = bitmapReference.get();if (bitmap2 != null)return bitmap2;else {sSoftBitmapCache.remove(key);}}}return null;}}
2. 写主要的异步加载类,ImageWorkTN,封装具体的加载方法,最关键的在于BitmapWorkerTask的设计,其构造函数需要传入一个ImageView作为参数。个人理解,BitmapWorkerTask和目标ImageView是你中有我,我中有你的关系,这样可以有效地实现一对一的异步加载模式,避免重复加载。具体的判断参cancelPotentialWork(Object data, ImageView imageView)这个方法。

import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.lang.ref.WeakReference;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import android.content.Context;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.drawable.BitmapDrawable;import android.graphics.drawable.Drawable;import android.os.AsyncTask;import android.os.Build;import android.os.Environment;import android.provider.MediaStore;import android.util.Log;import android.widget.ImageView;import com.company.carhostapp.R;import com.company.carhostapp.cache.MemoryLruCache;import com.company.carhostapp.cache.PrintHelper;/** * This class wraps up completing some arbitrary long running work when loading * a bitmap to an ImageView. It handles things like using a memory and disk * cache, running the work in a background thread and setting a placeholder * image. */public class ImageWorkerTN {private Bitmap mLoadingBitmap;private boolean isSaveSD = false;protected Context context;private int flag = 0;public int getFlag() {return flag;}public void setFlag(int flag) {this.flag = flag;}public ImageWorkerTN(Context context) {this.context = context;}public boolean isSaveSD() {return isSaveSD;}public void setSaveSD(boolean isSaveSD) {this.isSaveSD = isSaveSD;}/** * Load an image specified by the data parameter into an ImageView (override * {@link ImageWorkerTN#processBitmap(Object)} to define the processing * logic). A memory and disk cache will be used if an {@link ImageCache} has * been set using {@link ImageWorkerTN#setImageCache(ImageCache)}. If the * image is found in the memory cache, it is set immediately, otherwise an * {@link AsyncTask} will be created to asynchronously load the bitmap. *  * @param data *            The URL of the image to download. * @param imageView *            The ImageView to bind the downloaded image to. */public void loadImage(Object data, ImageView imageView) {Bitmap bitmap = null;if (Constants.mImageCache != null) {Log.i("cache", "事先拿   key = " + String.valueOf(data));bitmap = Constants.mImageCache.getBitmap(String.valueOf(data));}if (bitmap != null) {// Bitmap found in memory cacheLog.i("cache", "Bitmap found in memory cache");imageView.setImageBitmap(bitmap);} else {mLoadingBitmap = PrintHelper.readBitMap(context,R.drawable.zhaopian);if (PrintHelper.isScrolling) {imageView.setImageBitmap(mLoadingBitmap);} else if (cancelPotentialWork(data, imageView)) {final BitmapWorkerTask task = new BitmapWorkerTask(imageView);final AsyncDrawable asyncDrawable = new AsyncDrawable(context.getResources(), mLoadingBitmap, task);imageView.setImageDrawable(asyncDrawable);// task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, data);task.execute(data);}}}/** * Workaround for bug pre-Froyo, see here for more info: * http://android-developers.blogspot.com/2011/09/androids-http-clients.html */public static void disableConnectionReuseIfNecessary() {// HTTP connection reuse which was buggy pre-froyoif (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {System.setProperty("http.keepAlive", "false");}}private Bitmap processBitmap(String urlStr) {URL url = null;InputStream ins = null;Bitmap bitmap = null;disableConnectionReuseIfNecessary();HttpURLConnection urlConnection = null;File file = null;FileOutputStream fos = null;try {url = new URL(urlStr);urlConnection = (HttpURLConnection) url.openConnection();if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {ins = urlConnection.getInputStream();/** * 需要缓存至本地且本地存储可用 */if (isSaveSD()) {int lastIndex = urlStr.lastIndexOf("/");String fileName = "";String dirPath = "";if (lastIndex != -1) {fileName = urlStr.substring(lastIndex + 1);if (Environment.getExternalStorageState().equals(                  Environment.MEDIA_MOUNTED)) { dirPath = Environment.getExternalStorageDirectory() + "/powerlong/advertise/";File dir = new File(dirPath);if (!dir.exists()) {dir.mkdirs();}file = new File(dir,fileName);if (!file.exists()) {file.createNewFile();}byte[] buffer = new byte[1024];fos = new FileOutputStream(file);int length = -1;while ((length = ins.read(buffer)) != -1) {fos.write(buffer, 0, length);}bitmap = BitmapFactory.decodeFile(dirPath + fileName);} else {fos = context.openFileOutput(fileName,Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE);int length = -1;byte[] buffer = new byte[1024];while ((length = ins.read(buffer)) != -1) {fos.write(buffer, 0, length);}fos.flush();bitmap = BitmapFactory.decodeStream(context.openFileInput(fileName));}} } else {bitmap = BitmapFactory.decodeStream(ins);}}} catch (MalformedURLException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {if (urlConnection != null) {urlConnection.disconnect();}if (ins != null) {try {ins.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}if (fos != null) {try {fos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}return bitmap;}/** * The actual AsyncTask that will asynchronously process the image. */private class BitmapWorkerTask extends AsyncTask<Object, Void, Bitmap> {private Object data;private final WeakReference<ImageView> imageViewReference;public BitmapWorkerTask(ImageView imageView) {imageViewReference = new WeakReference<ImageView>(imageView);}/** * Background processing. */@Overrideprotected Bitmap doInBackground(Object... params) {data = params[0];final String dataString = String.valueOf(data);Bitmap bitmap = null;if (flag == 0) {bitmap = processBitmap(dataString);} else {bitmap = PrintHelper.loadThumbnailImage(dataString,MediaStore.Images.Thumbnails.MICRO_KIND, null, context);}return bitmap;}/** * Once the image is processed, associates it to the imageView */@Overrideprotected void onPostExecute(Bitmap bitmap) {final ImageView imageView = getAttachedImageView();if (bitmap != null && imageView != null) {imageView.setImageBitmap(bitmap);Log.i("cache", "mImageCache.putBitmap key = " + String.valueOf(data));Constants.mImageCache.putBitmap(String.valueOf(data), bitmap);}}/** * Returns the ImageView associated with this task as long as the * ImageView's task still points to this task as well. Returns null * otherwise. */private ImageView getAttachedImageView() {final ImageView imageView = imageViewReference.get();final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);if (this == bitmapWorkerTask) {return imageView;}return null;}}/** * Returns true if the current work has been canceled or if there was no * work in progress on this image view. Returns false if the work in * progress deals with the same data. The work is not stopped in that case. */public static boolean cancelPotentialWork(Object data, ImageView imageView) {final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);if (bitmapWorkerTask != null) {final Object bitmapData = bitmapWorkerTask.data;if (bitmapData == null || !bitmapData.equals(data)) {bitmapWorkerTask.cancel(false);} else {// The same work is already in progress.return false;}}return true;}/** * A custom Drawable that will be attached to the imageView while the work * is in progress. Contains a reference to the actual worker task, so that * it can be stopped if a new binding is required, and makes sure that only * the last started worker process can bind its result, independently of the * finish order. */private static class AsyncDrawable extends BitmapDrawable {private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;public AsyncDrawable(Resources res, Bitmap bitmap,BitmapWorkerTask bitmapWorkerTask) {super(res, bitmap);bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);}public BitmapWorkerTask getBitmapWorkerTask() {return bitmapWorkerTaskReference.get();}}/** * @param imageView *            Any imageView * @return Retrieve the currently active work task (if any) associated with *         this imageView. null if there is no such task. */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;}}

3.使用的时候很简单,只需要实例化ImageWorkerTN,在需要的地方调用loadImage方法,传入相关的参数即可,第一个参数是网络图片的url,第二个是显示这张图片的ImageView,同时在AdapterView上添加滚动监听,当它滚动的时候,滚动标记设为true,这样优化体验。
mListMsg.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView arg0, int scrollState) {MessageAdapter tempAdapter = ((MessageAdapter) mListMsg.getAdapter());if (tempAdapter != null) {if (scrollState != 0) {PrintHelper.isScrolling = true;} else {PrintHelper.isScrolling = false;timeTags.clear();tempAdapter.notifyDataSetChanged();}}}@Overridepublic void onScroll(AbsListView arg0, int arg1, int arg2, int arg3) {}});




0 0
原创粉丝点击