这篇文章主要介绍一下,如果暂停了,或者网段了,然后有恢复了,接着之前的继续下载。

首先我们先添加数据库 greendao

步骤1:首先在项目中创建一个java的Modle。如图
这里写图片描述

步骤2:创建DbGenerate文件,

public class DbGenerate {

    public static void main(String args[]) {

        // 版本号 \ 包名
        Schema schema = new Schema(1, "com.imooc.db");

        Entity entity = schema.addEntity("DownloadEntity");
        // 设置键名称
        entity.addLongProperty("start_position");
        entity.addLongProperty("end_position");
        entity.addLongProperty("progress_position");
        entity.addStringProperty("download_url");
        entity.addIntProperty("thread_id");
        // 设置主键
        entity.addIdProperty().autoincrement();

        try {
            new DaoGenerator().generateAll(schema, "dbgenerate/src-gen");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

步骤3:然后创建 “src-gen”目录空文件夹

步骤4:重新编译项目,src-gen里面会出现4个文件,拷贝到项目中直接用就可以了。
这里写图片描述

这里写图片描述

步骤5:在使用的项目的gradle中添加,完成
compile ‘de.greenrobot:greendao-generator:2.1.0’
compile ‘de.greenrobot:greendao:2.1.0’

数据加进来完成之后,下面我们看在这个下载案例中如何使用。

**先创建DownloadHelper操作类 public class DownloadHelper {

private DaoMaster mMaster;

private DaoSession mSession;

private DownloadEntityDao mDao;

private static DownloadHelper sHelper = new DownloadHelper();

public static DownloadHelper getInstance() {
    return sHelper;
}

private DownloadHelper() {

}

/**
 * 初始化、创建数据库
 * @param context
 */
public void init(Context context) {
    // 数据库名
    SQLiteDatabase db = new DaoMaster.DevOpenHelper(context, "download.db", null).getWritableDatabase();
    mMaster = new DaoMaster(db);
    mSession = mMaster.newSession();
    mDao = mSession.getDownloadEntityDao();
}

/**
 * 保存数据
 * @param entity
 */
public void insert(DownloadEntity entity) {
    mDao.insertOrReplace(entity);
}

/**
 * 获取全部数据
 * @param url
 * @return
 */
public List<DownloadEntity> getAll(String url) {
    return mDao.queryBuilder().where(DownloadEntityDao.Properties.Download_url.eq(url)).orderAsc(DownloadEntityDao.Properties.Thread_id).list();
}

}1、首先在DownloadManager中的downLoad下载方法中,首先读取一下数据库,有没有数据,如果有数据按之前的逻辑走,正常下载,如果没有,就查询数据库数据,接着下载。然后更新进度条*

 /** * 判断每个线程下载多长的数据,并多线程下载 */
    public void downLoad(final String url, final DownLoadCallBack callBack){

        // 下载之前先查询一下数据库有没有之前下载的
        mCache = DownloadHelper.getInstance().getAll(url);

        if(mCache == null || mCache.size() == 0) {
            // 如果之前没有下载,没有缓存正常走
            HttpManager.getInstance().asyncRequest(url, new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {

                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    if(response == null && callBack != null) {
                        callBack.fail(HttpManager.NETWORK_ERROR_CODE, "网络出问题了");
                        return;
                    }
                    length = response.body().contentLength();
                    if(length == -1) {
                        // 获取不到文件的总长度
                        callBack.fail(HttpManager.CONTENT_LENGTH_ERROR_CODE, "contenLength -1");
                        return;
                    }
                    processDownload(url, length, callBack, mCache);
                }
            });

        }else {
            // 之前有下载逻辑处理
            for(int i = 0; i < mCache.size(); i++) {
                // 取出每个线程存储的下载长度,从新下载
                DownloadEntity entity = mCache.get(i);
                long startSize = entity.getStart_position() + entity.getProgress_position();
                long endSize = entity.getEnd_position();
                sThreadPool.execute(new DownloadRunnable(startSize, endSize, url, callBack, entity));
                // 获取文件的总长度,进度下载用到
                if(i == mCache.size() - 1) {
                    length = mCache.get(i).getEnd_position() + 1;
                }
            }
        }

        // TODO: 更新下载进度
        sLocalProgressPool.execute(new Runnable() {
            @Override
            public void run() {
                while (true){
                    try {
                        // 每隔500更新一次
                        Thread.sleep(500);
                        // 取到下载的文件
                        File file = FlieStorageManager.getInstance().getFileByName(url);
                        long fileSize = file.length();
                        // 计算下载的百分比
                        int progress = (int) (fileSize * 100 / length);
                        callBack.progress(progress);
                        // 下载完退出线程、循环
                        if(progress >= 100) {
                            callBack.progress(progress);
                            return;
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

    }

2、然后看一下我们下载保存操作:

  /** * 下载 * @param length 下载文件的长度 保存数据 */
    private void processDownload(String url, long length, DownLoadCallBack callBack,List<DownloadEntity> cache) {
        if(cache == null || cache.size() == 0) {
            mCache = new ArrayList<>();
        }

        // 计算每一个线程下载的大小
        long threadDownloadSize = length / MAX_THREAD;
        // 分配每一个线程下载
        for(int i = 0; i < MAX_THREAD; i++) {
            // 计算每一个线程从多少下载 比如长度100 2个线程 0-49 50-99, 下面是计算的算法
            long startSize = i * threadDownloadSize;
            long endSize = 0;
            if (endSize == MAX_THREAD - 1) {
                endSize = length - 1;
            } else {
                endSize = (i + 1) * threadDownloadSize - 1;
            }

            // 创建实体,保存到数据库
            DownloadEntity entity = new DownloadEntity();
            entity.setDownload_url(url);
            entity.setEnd_position(endSize);
            entity.setStart_position(startSize);
            entity.setThread_id(i + 1);

            // 执行下载
            sThreadPool.execute(new DownloadRunnable(startSize, endSize, url, callBack, entity));
        }
    }

在看下线程里面的操作:

public class DownloadRunnable implements Runnable{

    /** 指定下载开始位置*/
    private long mStart;
    /** 指定下载结束位置*/
    private long mEnd;
    /** 请求url*/
    private String mUrl;
    /** 结果回调*/
    private DownLoadCallBack mCallBack;
    /** 需要保存数据的实体信息类*/
    private DownloadEntity mEntity;

    public DownloadRunnable(long mStart, long mEnd, String mUrl, DownLoadCallBack mCallBack, DownloadEntity entity) {
        this.mStart = mStart;
        this.mEnd = mEnd;
        this.mUrl = mUrl;
        this.mCallBack = mCallBack;
        this.mEntity = entity;
    }

    @Override
    public void run() {
        // 下载完成后返回的结果 response
        Response response = HttpManager.getInstance().syncRequestByRange(mUrl, mStart, mEnd);

        if(response == null && mCallBack != null) {
            mCallBack.fail(HttpManager.NETWORK_ERROR_CODE, "网络出问题了");
            return;
        }

        // 获取本地下载储存的文件
        File file = FlieStorageManager.getInstance().getFileByName(mUrl);

        // 用处是,如果我断网或者暂停后,杀死App,重新进来,就根据此字段取进度
        long finshProgress = mEntity.getProgress_position() == null ? 0 : mEntity.getProgress_position();

        // 记录线程下载的进度
        long progress = 0;

        // 多个线程对文件指定的位置写入数据(因为是多线程下载,多个线程肯定会对一个文件可读 可写 可修改)
        try {
            // 参数1:指定操作的文件 参数2:可读 可写 可修改
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rwd");
            // 指定一个偏移,下载的起始位置
            randomAccessFile.seek(mStart);
            byte[] buffer = new byte[1024];
            int len = 0;
            InputStream inputStream = response.body().byteStream();
            // 读取返回来的数据, 写入本地文件中
            while((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
                randomAccessFile.write(buffer, 0, len);

                // 更新数据库的进度
                progress += len;
                mEntity.setProgress_position(progress);
            }

            // 数据库取得数据 + 杀死App进来后记录的 == 一直没有退出应用的进度
            mEntity.setProgress_position(mEntity.getProgress_position() + finshProgress);

            randomAccessFile.close();
            // 下载成功
            mCallBack.success(file);


        } catch (FileNotFoundException e) {

            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            // 下载的数据保存到数据库
            DownloadHelper.getInstance().insert(mEntity);
        }
    }
}

然后再项目的Applaction中初始化数据库

public class MyApplacation extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        FlieStorageManager.getInstance().init(this);
        // 初始化数据库
        DownloadHelper.getInstance().init(this);
    }
}

下载Apk案例:

private void multipleDownFileImage() {
// DownloadManager.getInstance().downLoad(“http://szimg.mukewang.com/5763765d0001352105400300-360-202.jpg“, new DownLoadCallBack() {
DownloadManager.getInstance().downLoad(“http://shouji.360tpcdn.com/160901/84c090897cbf0158b498da0f42f73308/com.icoolme.android.weather_2016090200.apk“, new DownLoadCallBack() {
@Override
public void success(final File file) {
// 目前成功回调两次,通过cout标识一下 暂时性解决一下
if(cout < 1) {
cout++;
return;
}
Log.e(“file”, “file success: ” + file.getAbsolutePath());
Log.e(“file”, “file : ” + file.length());
}

        @Override
        public void fail(int errorCode, String errorMessage) {
            Log.e("file", "shibai");
        }

        @Override
        public void progress(int progress) {
            Log.e("file", "progress : " + progress);

            mProgress.setProgress(progress);
        }
    });
}

OK,多线程下载APK案例,就说完了。看了这4篇文章,相信是可以学会的。

更多相关文章

  1. 充分利用 Java 的元数据,第 3 部分:高级处理
  2. Java多线程六:线程优先级和yield()让步函数
  3. java多线程爬虫
  4. java 的 数据库连接测试类 (SQL server)
  5. 如何将mysql中的数据插入组合框中?
  6. jsoup 分页抓取网页数据Java HTML Parser
  7. javafx 和netty 混合使用出现线程不一致问题,求大神指点
  8. Java中的数据类型
  9. 在文件中添加新数据后,JComboBox不会刷新

随机推荐

  1. python学习的第一天---
  2. 如何在python中使用命名组和datadict从正
  3. 【问题解决方案】ImportError: No module
  4. 120行python代码解锁10000分微信跳一跳
  5. 使用特定顺序的ID列表从Django数据库中获
  6. Python3入门(六)——函数式编程
  7. Python中psutil模块的安装
  8. python环境配置问题及解决办法集锦
  9. SQLAlchemy使用笔记--简介
  10. 在seaborn.jointplot中绘制两个分布