Android多线程应用之-加载图片列表

Android加载图片列表,估计在很多应用中都会涉及到;固然,有许多开源框架提供了很方便的使用方法;并且相当稳定,但是如果不会自己实现,那么我相信遇到问题后就不会自己解决,只会依托于百度或者谷歌;这样,我认为不是你想要的结果吧;这几天我就自己设计实现了一下这个需求:
首先,分析如下:需求是加载图片列表,那么我需要完成以下的基本过程:
(一):下载图片
(二):显示图片
结合安卓自身的特点,下载图片过程不能在主线程;列表经过复用组件后,前面下载的东西如果不经过保存在本地那么就会造成二次下载;因此也需要缓存;缓存方案选择现在成熟的内存缓存方案和磁盘缓存方案;显示图片时需要根据页面对图片进行一定的优化,以优化内存占用;
因此我的具体实现如下:
首先将下载,下载回调;本地缓存寻找,寻找回调;分别实现在2个不同的线程实例;
因此实现了以下几个接口

//下载接口
public interface DownloadPictureTaskinf {
    public Bitmap download(String url);
}
//下载回调
public interface DownloadPictureCallbackinf {
    public void downloadCallback(Bitmap bitmap,String url);
}
//本地查找接口
public interface GetLoaclPictureTaskinf {
    public Bitmap getLocalBitmap();
}
//本地查找回调接口
public interface GetLocalPictureCallbackinf {
    public void getLocalCallback(Bitmap bitmap, String url);
}

线程池设计如下:


public class ThreadPool {

    private static ThreadPool instance = new ThreadPool();

    public static ThreadPool getInstance() {
        return instance;
    }

    private ThreadPool() {
        //下载的线程池
        downloadservice = Executors.newFixedThreadPool(8);
        //获取本地的线程池
        getLocalservice = Executors.newFixedThreadPool(1);
    }

    private ExecutorService downloadservice, getLocalservice;
    private Set<String> downloadSet = new HashSet<String>();
    private Set<String> getlocalTask = new HashSet<String>();
    //提交下载任务
    public void submitDownloadRunnable(
            final DownloadPictureTaskinf downloadPictureTaskinf,
            final DownloadPictureCallbackinf downloadPictureCallbackinf,
            final String url) {
        if (!downloadSet.contains(url)) {
            synchronized (downloadSet) {
                if (!downloadSet.contains(url)) {
                    downloadservice.submit(new Runnable() {
                        @Override
                        public void run() {
                            Bitmap temp = null;
                            temp = downloadPictureTaskinf.download(url);
                            downloadPictureCallbackinf.downloadCallback(temp,
                                    url);
                            downloadSet.remove(url);
                        }
                    });
                }
            }
        }

    }
    //提交查找本地任务
    public void submitgetLocalRunnable(
            final GetLoaclPictureTaskinf getLoaclPictureTaskinf,
            final GetLocalPictureCallbackinf getLocalPictureCallbackinf,
            final String url) {
        if (!getlocalTask.contains(url)) {
            synchronized (getlocalTask) {
                if (!getlocalTask.contains(url)) {
                    getlocalTask.add(url);
                    getLocalservice.submit(new Runnable() {
                        @Override
                        public void run() {
                            Bitmap temp = null;
                            temp = getLoaclPictureTaskinf.getLocalBitmap();
                            getLocalPictureCallbackinf.getLocalCallback(temp,
                                    url);
                            getlocalTask.remove(url);
                        }
                    });
                }
            }
        }

    }

}

下载图片类如下:


public class DownloadPictureTask implements DownloadPictureTaskinf,
        GetLoaclPictureTaskinf, DownloadPictureCallbackinf,
        GetLocalPictureCallbackinf {

    private Handler handler;
    private FileCache cache;
    private String url;
    private DownloadPictureCallbackinf callbackinf;
    private GetLocalPictureCallbackinf callbackinf2;

    public DownloadPictureTask(Context context, FileCache cache, String url,
            DownloadPictureCallbackinf callbackinf,
            GetLocalPictureCallbackinf callbackinf2) {
        this.cache = cache;
        this.url = url;
        this.callbackinf = callbackinf;
        this.callbackinf2 = callbackinf2;
        handler = new Handler(context.getMainLooper());
    }
    //当本地没有时,将下载任务提交到下载的线程池中
    @Override
    public void getLocalCallback(final Bitmap bitmap, final String url) {
        Log.v("本地==", bitmap + ":" + url);
        if (bitmap == null) {
            Log.v("本地为空", url);
            // 启动下载任务
            ThreadPool.getInstance().submitDownloadRunnable(this, this, url);
        } else {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    callbackinf2.getLocalCallback(bitmap, url);
                }
            });
        }

    }
    //下载成功回调
    @Override
    public void downloadCallback(final Bitmap bitmap, final String url) {
        handler.post(new Runnable() {
            @Override
            public void run() {
                callbackinf.downloadCallback(bitmap, url);
            }
        });
    }
   //从本地缓存查找实现
    @Override
    public Bitmap getLocalBitmap() {
        Log.v("本地任务开启", url);
        Bitmap bmp = cache.getCacheFromMemory(url);
        if (bmp == null) {
            bmp = BitmapFactory.decodeStream(cache.getCacheFromDisk(url));
        }
        return bmp;
    }
    //下载实现
    @Override
    public Bitmap download(String url) {
        Log.v("下载任务开启", url);
        Bitmap bmp = null;
        try {
            URL mUrl = new URL(url);
            InputStream ins = mUrl.openConnection().getInputStream();
            //加入到本地缓存
            bmp = cache.addCache(url, ins);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bmp;
    }
   //启动查找本地任务
    public void execute() {
        ThreadPool.getInstance().submitgetLocalRunnable(this, this, url);
    }

}

下面是缓存实现类:


/**
 * 借用了LruCache和DiskLruCache
 * 文件名就是下载链接的MD5值
 * @author 徐晔
 * @note 文件缓存类,集合了内存缓存和磁盘缓存
 */
public class FileCache {

    private final static int MEMMAXSIZE = 1024 * 30;
    private final static int DISKMAXSIZE = 10*1024 * 1024;
    /** 内存缓存 */
    private LruCache<String, Bitmap> memoryCache;
    /** 磁盘缓存 */
    private DiskLruCache diskCache;

    public FileCache(Context context, int appVersion) {
        memoryCache = new LruCache<String, Bitmap>(MEMMAXSIZE);
        try {
            diskCache = DiskLruCache.open(
                    Util.getDiskCacheDir(context, "cache"), appVersion, 1,
                    DISKMAXSIZE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加缓存
     * 
     * @note 首先添加到磁盘中,然后添加到内存中
     */
    public Bitmap addCache(String key, InputStream in) {
        if(in==null){
            return null;
        }
        key = MD5Utils.getMD5(key);
        DiskLruCache.Editor editor = null;
        Bitmap bitmap = null;
        ByteArrayOutputStream baos=null;
        OutputStream mOutputStream=null;
        try {
            editor = diskCache.edit(key);
                baos=new ByteArrayOutputStream();
                if (editor != null) {
                     mOutputStream = editor.newOutputStream(0);
                }
                int b;
                while ((b = in.read()) != -1) {
                    if (editor != null) {
                        mOutputStream.write(b);
                    }
                    baos.write(b);
                }
                editor.commit();
                bitmap=BitmapDecoder.decoderFromByteArray(baos.toByteArray(), 300, 300);
                memoryCache.put(key, bitmap);
        } catch (Exception e) {
            try {
                editor.abort();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
//              diskCache.flush();
                baos.close();
                in.close();
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return bitmap;
    }

    /**
     * 在内存中取出缓存
     * 
     */
    public InputStream getCacheFromDisk(String key) {
        key = MD5Utils.getMD5(key);
        InputStream ins = null;
        try {
            Snapshot snapshot = diskCache.get(key);
            if (snapshot != null) {
                ins = snapshot.getInputStream(0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ins;
    }

    /**
     * 在磁盘中取出缓存
     */
    public Bitmap getCacheFromMemory(String key) {
        key = MD5Utils.getMD5(key);
        return memoryCache.get(key);
    }
}

图片配置类:


/**
 * 
 * @author 徐晔
 * @note 根据配置对图片进行编码
 */
public class BitmapDecoder {

    /**
     * 将流转化为Bitmap文件
     * 
     * @param inputStream
     *            :输入流
     * @param rect
     *            :
     * @param realwidth
     *            :想要获取图片的宽度
     * @param realheight
     *            :想要获取的图片的高度
     * @return
     * @throws Exception
     *             :主要指内存溢出异常
     */
    public static Bitmap decoderFromByteArray(byte[] butearray,
            int realwidth, int realheight) throws Exception {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        options.inPreferredConfig = Bitmap.Config.ARGB_4444;
        BitmapFactory.decodeByteArray(butearray, 0, butearray.length, options);
        options.inJustDecodeBounds = false;
        options.inSampleSize = calculateInSampleSize(options, realwidth,
                realheight);
        try {
            return BitmapFactory.decodeByteArray(butearray, 0, butearray.length, options);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 转化文件大小
     */
    private static int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            if (width > height) {
                inSampleSize = Math.round((float) height / (float) reqHeight);
            } else {
                inSampleSize = Math.round((float) width / (float) reqWidth);
            }
            final float totalPixels = width * height;
            final float totalReqPixelsCap = reqWidth * reqHeight * 2;
            while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                inSampleSize++;
            }
        }
        return inSampleSize;
    }

}

适配器:


public class ImageAdapter extends BaseAdapter {

    private LayoutInflater inflater;
    private FileCache cache;
    private DownloadPictureTask task;
    private Context context;

    public ImageAdapter(Context context) {
        this.context = context;
        inflater = (LayoutInflater) context
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        cache = new FileCache(context, 1);
    }

    @Override
    public int getCount() {
        return Consts.image.length;
    }

    @Override
    public String getItem(int position) {
        return Consts.image[position];
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder viewHolder;
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView = inflater.inflate(R.layout.image_item, null);
            viewHolder.ii_item = (ImageView) convertView
                    .findViewById(R.id.ii_item);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        final String key = (String) getItem(position);
        viewHolder.ii_item.setTag(key);
        viewHolder.ii_item.setBackgroundDrawable(null);
        //启动异步任务
        task = new DownloadPictureTask(context, cache, key,
                new DownloadPictureCallbackinf() {

                    @Override
                    public void downloadCallback(Bitmap bitmap, String url) {
                        if (viewHolder.ii_item != null
                                && viewHolder.ii_item.getTag().toString()
                                        .equals(url)) {
                            viewHolder.ii_item.setBackgroundDrawable(new BitmapDrawable(bitmap));
                        }
                    }
                }, new GetLocalPictureCallbackinf() {

                    @Override
                    public void getLocalCallback(Bitmap bitmap, String url) {
                        if (viewHolder.ii_item != null
                                && viewHolder.ii_item.getTag().toString()
                                        .equals(url)) {
                            viewHolder.ii_item.setBackgroundDrawable(new BitmapDrawable(bitmap));

                        }
                    }
                });
        task.execute();
        return convertView;
    }

    class ViewHolder {
        ImageView ii_item;
    }

}

主类文件:


public class ImageActiviy extends Activity {

    private GridView ai_list;
    private ImageAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_image);
        ai_list = (GridView) findViewById(R.id.ai_list);
        adapter = new ImageAdapter(this);
        ai_list.setAdapter(adapter);
    }

}

以下是截图:
这里写图片描述
这个就是加载图片列表的大概过程;其中缓存借用了LruCache和DiskLruCache,这样不但节省了网络流量,还大大的加快了图片的加载速度;优化了用户体验;需要注意的是,在对异步任务进行提交时,有没有现有的任务在运行;不管是下载图片还是从本地磁盘取文件,需要避免重复下载和重复读取一个文件的问题;这两者一个浪费流量;另一个会造成系统卡顿,因为同时提交多个读取文件的操作特别容易引起APP卡顿;因此我这边从本地缓存读取文件的异步任务最大运行数量为1;

阅读更多
想对作者说点什么? 我来说一句

HTML5+CSS3+jquery应用之

2017年11月15日 72.94MB 下载

Android用之《宋词三百首》

2014年05月08日 2.97MB 下载

android小应用之手机地图源码

2011年06月15日 114KB 下载

android小应用之手机日历源码

2011年06月15日 50KB 下载

【转】Android用之加速度传感器

2014年06月23日 1.26MB 下载

没有更多推荐了,返回首页

不良信息举报

Android多线程应用之-加载图片列表

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭