即拿即用-Android单线程断点下载

 之前有看过一个著名的断点下载的框架MultiThreadDownload,用的是线程池开启下载任务,点击暂停的时候将断点的信息保存在数据库里面,下次拿出来继续下载,本文的思路也和这个差不多。
 
 这篇文章的代码将会在上次写的《即拿即用-HttpURLConnection分别实现图片,文本,文件的请求》的GitHub项目里面继续更新。完整下载地址在文章最后

 该例子在下载中多次点击开始和暂停对进度进行控制,下面放上一张效果图:

这里写图片描述

 主要步骤如下:

 点击开始的时候,我们要先开一个网络连接去获取文件的长度:

    /**
     * 点击开始
     */
    public void onStartClick(View view) {
        // 开启
        fileName.setText(getfileName(urlstr));
        // 获得Activity传来的参数
        Log.i("test", "START" + fileInfo.toString());
        //开启一个下载任务
        new InitThread(fileInfo).start();
    }
/**
     * 开启一个网络连接用来获得下载文件的信息
     */
    class InitThread extends Thread {
        private FileInfo mFileInfo = null;

        public InitThread(FileInfo mFileInfo) {
            super();
            this.mFileInfo = mFileInfo;
        }

        @Override
        public void run() {
            HttpURLConnection conn = null;
            RandomAccessFile raf = null;
            try {
                URL url = new URL(mFileInfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(5 * 1000);
                conn.setRequestMethod("GET");
                int code = conn.getResponseCode();
                int length = -1;
                if (code == HttpURLConnection.HTTP_OK) {
                    length = conn.getContentLength();
                }
                //如果文件长度为小于0,表示获取文件失败,直接返回
                if (length <= 0) {
                    return;
                }
                // 判断文件路径是否存在,不存在这创建
                File dir = new File(DownloadPath);
                if (!dir.exists()) {
                    dir.mkdir();
                }
                // 创建本地文件
                File file = new File(dir, mFileInfo.getFileName());
                raf = new RandomAccessFile(file, "rwd");
                raf.setLength(length);
                // 设置文件长度
                mFileInfo.setLength(length);
                // 将ileInfo对象传送给Handler
                Message msg = Message.obtain();
                msg.obj = mFileInfo;
                msg.what = MSG_INIT;
                mHandler.sendMessage(msg);
//              msg.setTarget(mHandler);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
                try {
                    if (raf != null) {
                        raf.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            super.run();
        }
    }

 获取文件的长度之后,发送一个handler消息通知应用去开启另外一个网络连接DownloadTask,进行下载:

    // 从InitThread线程中获取FileInfo信息,然后开始下载任务
    Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case MSG_INIT:
                    FileInfo fileInfo = (FileInfo) msg.obj;
                    Log.i("test", "INIT:" + fileInfo.toString());
                    // 获取FileInfo對象,开始下载任务
                    mTask = new DownloadTask(SingleThreadDownloadActivity.this, fileInfo);
                    mTask.download();
                    break;
            }
        }
    };

 在开启下载任务之前,根据下载的地址作为条件,看一下数据库这个地址有没有上一次下载的信息,也就是上一次文件下载的长度,结束的位置等信息,有的话获取并传给下载任务,没有的话会在下载任务里面插入一条到数据库

     /**
     * 查询数据库上一次下载的信息,有则获取,没有则新建
     */
    public void download() {
        // 从数据库中获取到下载的信息
        List<ThreadInfo> list = mDao.queryThreads(mFileInfo.getUrl());
        ThreadInfo info = null;
        if (list.size() == 0) {
            info = new ThreadInfo(0, mFileInfo.getUrl(), 0, mFileInfo.getLength(), 0);
        } else {
            info = list.get(0);

        }
        //开启下载任务
        new DownloadThread(info).start();
    }

 到了最关键的时候了,开启一个下载任务。一边下载一边记录下载的长度,并更新到数据库。如果点击了暂停,则跳出循环结束下载。

 class DownloadThread extends Thread {
        private ThreadInfo threadInfo = null;

        public DownloadThread(ThreadInfo threadInfo) {
            this.threadInfo = threadInfo;
        }

        @Override
        public void run() {
            Log.i("test", "开启一个下载任务");
            // 如果数据库不存在下载信息,添加下载信息
            if (!mDao.isExists(threadInfo.getUrl(), threadInfo.getId())) {
                mDao.insertThread(threadInfo);
            }
            HttpURLConnection conn = null;
            RandomAccessFile raf = null;
            InputStream is = null;
            try {
                URL url = new URL(mFileInfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(5 * 1000);
                conn.setRequestMethod("GET");
                //开始位置为getFinished()开始上次结束的位置
                Log.i("test", "文件的长度"+mFileInfo.getLength()+"   上次结束的位置:" + threadInfo.getFinished());
                int start = threadInfo.getStart() + threadInfo.getFinished();
                // 设置下载文件开始到结束的位置(结束的位置也就是文件的长度)
                conn.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd());
                File file = new File(SingleThreadDownloadActivity.DownloadPath, mFileInfo.getFileName());
                raf = new RandomAccessFile(file, "rwd");
                raf.seek(start);
                mFinished += threadInfo.getFinished();

                int code = conn.getResponseCode();
                if (code == HttpURLConnection.HTTP_PARTIAL) {
                    is = conn.getInputStream();
                    byte[] bt = new byte[1024];
                    int len = -1;
                    // 定义UI刷新时间
                    long time = System.currentTimeMillis();
                    while ((len = is.read(bt)) != -1) {
                        raf.write(bt, 0, len);
                        //记录结束的位置
                        mFinished += len;
                        // 设置为500毫米更新一次
                        if (System.currentTimeMillis() - time > 500) {
                            time = System.currentTimeMillis();
                            //发送一个广播提示下载的进度
                            Intent intent = new Intent(SingleThreadDownloadActivity.ACTION_UPDATE);
                            //结束的位置/文件长度*100=下载进度百分比
                            intent.putExtra("finished", mFinished * 100 / mFileInfo.getLength());
                            Log.i("test", mFinished * 100 / mFileInfo.getLength() + "");
                            // 发送广播给Activity
                            mComtext.sendBroadcast(intent);
                        }
                        if (mIsPause) {
                            //如果状态为暂停,则跳出循环,并记录这次结束的位置的长度
                            mDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), mFinished);
                            return;
                        }
                    }
                }

                // 下载完成后,刪除数据库信息
                mDao.deleteThread(threadInfo.getUrl(), threadInfo.getId());
                Intent intent = new Intent(SingleThreadDownloadActivity.ACTION_UPDATE);
                //结束的位置/文件长度*100=下载进度百分比
                intent.putExtra("finished", 100);
                // 发送广播给Activity
                mComtext.sendBroadcast(intent);
                Log.i("DownloadTask", "下载完毕");

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
                try {
                    if (is != null) {
                        is.close();
                    }
                    if (raf != null) {
                        raf.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Log.i("test", "关闭一个下载任务");
            }
            super.run();
        }
    }

在这篇文章中的重点是使用了RandomAccessFile,它支持任意访问的方式,程序可以直接跳到任意地方来读写数据,RandomAccessFile的使用方式请查看《断点下载神器-RandomAccessFile》

《即拿即用-Android单线程断点下载》

《断点下载神器-RandomAccessFile》

《即拿即用-Android多线程断点下载》

完整代码地址

https://github.com/mocn26169/HttpRequest-Demo

参考

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值