在日常项目开发中,关于图片批量下载,数据缓存的相关功能比比皆是,这次也是去年在项目中需要在本地缓存商品数据,所以用到了批量下载的功能,特此记录 ~

全局代码均使用的是伪代码 ,不能直接跑项目,更多的是提供解决思想和部分功能实现

Look here:glide 自带缓存,如果不是明确需要缓存到本地磁盘的话,可以使用glide节约时间,提升效率

  • 单图下载:采用 Glide 框架为图片加载框架
  • 批量下载:结合 Glide 框架与 递归 实现批量下载图片功能

基本配置

  • 加入以下权限
<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • build.gradle(app)
//glide图片框架
    implementation 'com.github.bumptech.glide:glide:4.3.1'
    annotationProcessor('com.github.bumptech.glide:compiler:4.3.1')
    implementation 'com.github.bumptech.glide:okhttp3-integration:4.3.1'
    //greenDao数据库框架
    implementation 'org.greenrobot:greendao:3.3.0'
    implementation 'org.greenrobot:greendao-generator:3.3.0'

Glide伪代码

public class GlideTool {
        /**
         * 主要用于监听Glide下载结果,成功或失败 ~
         */
        public interface PicListener {
            void success(File file);
            void failure();
        }

        /**
         * Glide图片下载
         */
        public static synchronized void downImageFile(Context context, String imageUrl, PicListener listener) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    FutureTarget<File> target = Glide.with(context)
                            .asFile()
                            .load(imageUrl)
                            .downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);

                    try {
                        File file = target.get();
                        if (file != null) {
                            listener.success(file);
                        } else {
                            listener.failure();
                        }
                    } catch (ExecutionException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

我这里的实现步骤,指的是常规项目中的一个实现过程

  1. 通过后台接口获取到我们需要缓存的数据(我网络框架用的是RxAndroid+Retrofit,可自行带入自己的网络框架)

我在项目中是要缓存商品的商品图,故此我获取的是一个商品的List,然后在进行批量下载

/**
     * 商品列表
     **/
    public void interProductList() {
        RetrofitManager.getInstance()
                .getApiService()
                .getProductList("Hint:自己的商品接口")
                .compose(RxHelper.observableIO2Main(this))
                .subscribe(new BaseObserver<"Hint:关于基类自行封装" > () {
            @Override
            public void onSuccess ("Hint:返回体类自行封装" response){
            	//可以在onCreate()创建productList用于存储商品数据
                productList.clear();
                List<ProductBean> interProductList = response.getProductList();
                productList.addAll(interProductList);
                //批量下载数据
                downMoreImg(0,productList);
            }

            @Override
            public void onFailure (Throwable e, String errorMsg){

            }
        });
    }

ProductBean (伪类)

import java.io.Serializable;

/**
 * @author Liu
 * @date 2020/07/23
 */
public class ProductBean implements Serializable {
    /**
     * 商品id
     */
    private String id;
    /**
     * 商品名称
     */
    private String name;
    /**
     * 售价
     */
    private String price;
    /**
     * 商品图片URL
     */
    private String imageUrl;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }
}
  1. 通过递归实现图片的批量下载(同步线程 - 之所以未选择异步线程,主要是因为担心数据重复下载的问题)

优化方向

  • 可以在图片下载失败的回调中进行错误数据记录,在所有图片下载完成后,再次下载之前的错误数据
  • 可以在图片下载失败的回调中直接参与二次递归,不确定是否会有卡死现象
/**
     * 批量缓存商品图片
     */
    int tmpPosition = 0;
    public synchronized void downMoreImg(int position, List<ProductBean> imgList) {
        tmpPosition = position;
        ProductBean productBean = imgList.get(tmpPosition);
        String imageUrl = productBean.getImageUrl();
        GlideTool.downImageFile(this, imageUrl, new GlideTool.PicListener() {
            @Override
            public void success(File file) {
                Bitmap bitmap = BitmapFactory.decodeFile(file.toString());
                //缓存商品数据到本地,采用GreenDao,如不用可删除
                saveImage(productBean, bitmap, Constant.LocalPath.DEFAULT_PATH);
                Log.e("tag", "下载成功:" + tmpPosition + " - position:" + position);
                tmpPosition++;
                if (tmpPosition < imgList.size()) {
                    downMoreImg(tmpPosition, imgList);
                }
            }

            @Override
            public void failure() {
                Log.e("tag", "下载失败:" + tmpPosition);
            }
        });
    }
  1. 缓存数据到本地数据库(如果你的需求只是批量下载图片,那么无需往下看了)

关于本地数据缓存,我使用的 greenDao数据库存储 ,如果想偷懒的话也可以将数据存在sp里,但是要注意sp的安全性和体积较小的问题

/**
     * 保存商品
     */
    public void saveImage(ProductBean bean, Bitmap bitmap, String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdir();
        }
        try {
            String tmpImgPath = path + "/" + System.currentTimeMillis() + ".png";
            FileOutputStream fileOutputStream = new FileOutputStream(tmpImgPath);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
            fileOutputStream.close();
            //我自己使用策略模式封装的GreenDao
            DaoStrategy.getInstance(new LyProduct(bean.getCategoryId(), bean.getId(), bean.getName(), bean.getImageUrl(), tmpImgPath, bean.getStockNum(), bean.getUnit())).insert();
        } catch (Exception e) {
            LogTool.e(e.getMessage());
        }
    }