android 获取当地全部图片列表的实现及源码下载(二)

android 获取本地全部图片列表的实现及源码下载(二)

链接上一篇博文

本篇博文接着讲述本地图片的获取。
下面给出获取本地图片的异步线程类LoadLoacalPhotoCursorTask的代码:

/**
* 获取本地图片的异步线程类
*/
public class LoadLoacalPhotoCursorTask extends AsyncTask<Object, Object, Object> {
    private Context mContext;
    private final ContentResolver mContentResolver;
    private boolean mExitTasksEarly = false;//退出任务线程的标志位
    private OnLoadPhotoCursor onLoadPhotoCursor;//定义回调接口,获取解析到的数据

    private ArrayList<Uri> uriArray = new ArrayList<Uri>();//存放图片URI
    private ArrayList<Long> origIdArray = new ArrayList<Long>();//存放图片ID

    public LoadLoacalPhotoCursorTask(Context mContext) {
        this.mContext = mContext;
        mContentResolver = mContext.getContentResolver();
    }

    @Override
    protected Object doInBackground(Object... params) {
        String[] projection = {
                MediaStore.Images.Media._ID
        };
        Uri ext_uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        String where = MediaStore.Images.Media.SIZE + ">=?";
        /**
        * 这个查询操作完成图片大小大于100K的图片的ID查询。
        * 大家可能疑惑为什么不查询得到图片DATA呢?
        * 这样是为了节省内存。通过图片的ID可以查询得到指定的图片
        * 如果这里就把图片数据查询得到,手机中的图片大量的情况下
        * 内存消耗严重。那么,什么时候查询图片呢?应该是在Adapter
        * 中完成指定的ID的图片的查询,并不一次性加载全部图片数据
        */
        Cursor c = MediaStore.Images.Media.query(
                mContentResolver,
                ext_uri,
                projection,
                where,
                new String[]{1 * 100 * 1024 + ""},
                MediaStore.Images.Media.DATE_ADDED+" desc");

        int columnIndex = c.getColumnIndexOrThrow(MediaStore.Images.Media._ID);

        int i = 0;
        while (c.moveToNext() && i < c.getCount() && !mExitTasksEarly) {   //移到指定的位置,遍历数据库
            long origId = c.getLong(columnIndex);
            uriArray.add(
                    Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            origId + "")
            );

            origIdArray.add(origId);
            c.moveToPosition(i);
            i++;
        }
        c.close();//关闭数据库
        if (mExitTasksEarly) {
            uriArray = new ArrayList<Uri>();
            origIdArray = new ArrayList<Long>();
        }
        return null;
    }

    @Override
    protected void onPostExecute(Object o) {
        if (onLoadPhotoCursor != null && !mExitTasksEarly) {
        /**
        * 查询完成之后,设置回调接口中的数据,把数据传递到Activity中
        */          onLoadPhotoCursor.onLoadPhotoSursorResult(uriArray, origIdArray);
        }
    }

    @Override
    protected void onCancelled() {
        super.onCancelled();    //To change body of overridden methods use File | Settings | File Templates.
        mExitTasksEarly = true;
    }

    public void setExitTasksEarly(boolean exitTasksEarly) {
        this.mExitTasksEarly = exitTasksEarly;
    }

    public void setOnLoadPhotoCursor(OnLoadPhotoCursor onLoadPhotoCursor) {
        this.onLoadPhotoCursor = onLoadPhotoCursor;
    }

    public interface OnLoadPhotoCursor {
        public void onLoadPhotoSursorResult(ArrayList<Uri> uriArray, ArrayList<Long> origIdArray);
    }
}

下面给出加载图片的类ImageWorker的代码:

/**
 * 手机本地图片异步加载处理类
 * 图片的加载性能影响很大,使用弱引用和软引用
 * 缓存图片,加快响应的速度,提高性能。
 */
public class ImageWorker {

    //这个值设置的是加载图片的动画效果的间隔时间,达到渐隐渐显的效果
    private static final int FADE_IN_TIME = 200;

    private boolean mExitTasksEarly = false;//判断图片加载任务是否提前退出
    private boolean mPauseWork = false;//加载图片线程是否挂起
    private final Object mPauseWorkLock = new Object();//锁对象,这个锁对象是为了判断是否进行图片的加载

    protected final Resources mResources;
    private final ContentResolver mContentResolver;//内容解析者
    private final BitmapFactory.Options mOptions;

    private final HashMap<Long, SoftReference<BitmapDrawable>> bitmapCache = new HashMap<Long, SoftReference<BitmapDrawable>>();//用于缓存图片,每一个缓存的图片对应一个Long类型的id值,SoftReference对应该图片的软引用

    private Bitmap mLoadBitmap;//GridView中默认的背景图片
    //构造器
    public ImageWorker(Context context) {
        this.mResources = context.getResources();
        this.mContentResolver = context.getContentResolver();
        mOptions = new BitmapFactory.Options();
        mOptions.inSampleSize = 3;//缩放图片为原来的1/9。一般应用中加载图片都会进行图片的缩放,防止内存溢出的问题。这部分的内容有关Bitmap,请参考我的博文(http://blog.csdn.net/u010156024/article/details/44103557)
    }

    /**
     * 加载图片
     * @param origId 每个本地图片对应一个id值
     * @param imageView
     */
    public void loadImage(long origId, ImageView imageView) {
        BitmapDrawable bitmapDrawable = null;
        //先从缓存中加载图片,如果缓存中有,加载图片即可。
        //如果缓存中没有,首先判断当前任务是否暂停,没有暂停则使用loadBitmapTask异步任务线程加载图片
        if (bitmapCache.containsKey(origId)) {
            bitmapDrawable = bitmapCache.get(origId).get();
        }
        if (bitmapDrawable != null) {
            imageView.setImageDrawable(bitmapDrawable);
        } else if (cancelPotentialWork(origId, imageView)) {
            final LoadBitmapTask loadBitmapTask = new LoadBitmapTask(imageView);
            final AsyncDrawable asyncDrawable =
                    new AsyncDrawable(mResources, mLoadBitmap, loadBitmapTask);
            imageView.setImageDrawable(asyncDrawable);
            //SERIAL_EXECUTOR 启动线程,保证线程顺序依次执行
            loadBitmapTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, origId);
        }
    }

/**
* 该类提供这个方法设置GridView中每个item默认的图片
*/
    public void setLoadBitmap(Bitmap mLoadBitmap) {
        this.mLoadBitmap = mLoadBitmap;
    }

    /**
     * 设置图片动画  加载图片渐隐渐显的效果
     *
     * @param imageView
     * @param drawable
     */
    private void setImageDrawable(ImageView imageView, Drawable drawable) {
        final TransitionDrawable td =
                new TransitionDrawable(new Drawable[]{
                        new ColorDrawable(android.R.color.transparent),
                        drawable
                });
        imageView.setImageDrawable(td);
        td.startTransition(FADE_IN_TIME);
    }

    /**
     * 取消可能在运行并且暂停的任务。
     *
     * @param origId
     * @param imageView
     * @return
     */
    private static boolean cancelPotentialWork(long origId, ImageView imageView) {
        final LoadBitmapTask loadBitmapTask = getBitmapWorkerTask(imageView);

        if (loadBitmapTask != null) {
            final long bitmapOrigId = loadBitmapTask.origId;
            if (bitmapOrigId == origId) {
                loadBitmapTask.cancel(true);
            } else {
                // The same work is already in progress.
                return false;
            }
        }
        return true;
    }

    /**
     * 图片异步加载线程类-任务线程
     */
    private class LoadBitmapTask extends AsyncTask<Long, Void, BitmapDrawable> {
        private long origId;
        private WeakReference<ImageView> imageViewReference;//指向Imageview的弱引用,把图片缓存在HashMap<Long, SoftReference<BitmapDrawable>> bitmapCache中。

        public LoadBitmapTask(ImageView imageView) {
            imageViewReference = new WeakReference<ImageView>(imageView);
        }

        @Override
        protected BitmapDrawable doInBackground(Long... params) {
            origId = params[0];
            Bitmap bitmap = null;
            BitmapDrawable drawable = null;

            // Wait here if work is paused and the task is not cancelled
            synchronized (mPauseWorkLock) {
                while (mPauseWork && !isCancelled()) {
                    try {
                        mPauseWorkLock.wait();
                    } catch (InterruptedException e) {
                    }
                }
            }

            if (bitmapCache != null && !isCancelled() && getAttachedImageView() != null
                    && !mExitTasksEarly) {
                //这里是根据图片的id值查询手机本地的图片,获取图片的缩略图,MICRO_KIND 代表96*96大小的图片
                bitmap = MediaStore.Images.Thumbnails.getThumbnail(
                        mContentResolver,
                        origId,
                    MediaStore.Images.Thumbnails.MICRO_KIND,
                        mOptions
                );
            }

            if (bitmap != null) {
                drawable = new BitmapDrawable(mResources, bitmap);
                bitmapCache.put(origId,new SoftReference<BitmapDrawable>(drawable));
            }
            return drawable;
        }

        @Override
        protected void onPostExecute(BitmapDrawable drawable) {
            if (isCancelled() || mExitTasksEarly) {
                drawable = null;
            }

            final ImageView imageView = getAttachedImageView();
            if (drawable != null && imageView != null) {
                setImageDrawable(imageView, drawable);
            }
        }

       @Override
        protected void onCancelled(BitmapDrawable drawable) {
            super.onCancelled(drawable);
            synchronized (mPauseWorkLock) {
                mPauseWorkLock.notifyAll();
            }
        }

        /**
         * 返回与此任务相关的ImageView,
         * 如果ImageView 内的任务是当前任务,
         * 则返回当前ImageView,否则返回null。
         * @return
         */
        private ImageView getAttachedImageView() {
            final ImageView imageView = imageViewReference.get();
            final LoadBitmapTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
            if (this == bitmapWorkerTask) {
                return imageView;
            }
            return null;
        }
    }

    /**
     * 存储异步信息图片资源类
     */
    private static class AsyncDrawable extends BitmapDrawable {
        private final WeakReference<LoadBitmapTask> bitmapWorkerTaskReference;//虚引用
        public AsyncDrawable(Resources res, Bitmap bitmap, LoadBitmapTask bitmapWorkerTask) {
            super(res, bitmap);
            bitmapWorkerTaskReference =
                    new WeakReference<LoadBitmapTask>(bitmapWorkerTask);
        }
        public LoadBitmapTask getLoadBitmapTask() {
            return bitmapWorkerTaskReference.get();
        }
    }

    /**
     * 返回图片资源内存放的异步线程,如果存在,则返回,不存在,返回null。
     *
     * @param imageView 当前存放异步资源图片的ImageView
     * @return
     */
    private static LoadBitmapTask getBitmapWorkerTask(ImageView imageView) {
        if (imageView != null) {
            final Drawable drawable = imageView.getDrawable();
            if (drawable instanceof AsyncDrawable) {
                final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
                return asyncDrawable.getLoadBitmapTask();
            }
        }
        return null;
    }

    /**
     * 设置异步任务是否暂停,false为启动,true为暂停。
     * @param pauseWork
     */
    public void setPauseWork(boolean pauseWork) {
        synchronized (mPauseWorkLock) {
            mPauseWork = pauseWork;
            if (!mPauseWork) {
                mPauseWorkLock.notifyAll();
            }
        }
    }

    /**
     * 退出线程
     * @param exitTasksEarly
     */
    public void setExitTasksEarly(boolean exitTasksEarly) {
        mExitTasksEarly = exitTasksEarly;
        setPauseWork(false);//这个设置为false,使得退出任务优雅。这个设置为true也是可行的,也没有问题,可以达到同样的效果。但是可以比较设置为true或false的区别。
    }
}

至此 全部讲述了android手机中获取本地图片的全部内容。这个项目代码在Github中开放学习,非常值得大家深入研究学习之后。
总结:
整个项目使用到了ContentResolver、软引用、弱引用、缓存、异步线程、节省内存技巧-滚动不加载、锁机制、图片缩放、线程通信、动画等非常实用的方法、技巧。

源码下载