android断点续传下载文件

来源:互联网 发布:中兴刷机软件 编辑:程序博客网 时间:2024/06/09 18:46

这里有两个功能点。
1、下载
2、下载暂停后可以在暂停位置下载。

所以暂定涉及到的技术是,http网络请求,多线程,sqlite数据库缓存下载位置。

代码流的处理流程:从主activity按钮激发下载行为。委托DownloadTask子线程管理下载事务。DownloadTask调用下载器FileDownlodered完成下载文件。FileDownlodered调用多个DownloadThread线程的方式(多线程)从服务器下载文件块。DownloadThread在下载的时候实时把当前线程下载的情况记录到sqlite数据库中,记录下载位置。当在暂停后在启动时候直接从暂停位置开始下载。
activity ->DownloadTask ->FileDownlodered ->DownloadThread ->db;
主activity的按钮事件响应代码

    @Override        public void onClick(View v) {            // TODO Auto-generated method stub            switch (v.getId()) {            case R.id.startDownload:                String path = pathText.getText().toString();// 获取下载路径                if (Environment.getExternalStorageState().equals(                        Environment.MEDIA_MOUNTED)) {                    // 当sd卡存在时候                    // getExternalStorgeDirectory();                    File saveFile = Environment                            .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);                            //获取到sd卡的文本目录。也是我们下载文件的目录绝对地址。                    getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);download(path, saveFile);// 依据资源path,和文件在本地存放的目录地址作为参数进行下载文件                    try {                        Log.i(TAG, path);                        Log.i(TAG, saveFile.getCanonicalPath().toString());                    } catch (IOException e) {                        // TODO Auto-generated catch block                        e.printStackTrace();                    }                    Log.i(TAG, "sd卡存在,开始下载");                } else {                    Log.e(TAG, getResources().getString(R.string.sdcarderror));                }                downloadButton.setEnabled(false);                stopButton.setEnabled(true);                break;            case R.id.stopDownload:                exit();                downloadButton.setEnabled(true);                stopButton.setEnabled(false);                break;            default:                break;            }        }//这是下载方法的逻辑,委托了DownloadTask子线程进行下载管理。ui线程不可以用于延时的操作。public void download(String path, File saveDir) {        // TODO Auto-generated method stub        task = new DownloadTask(path, saveDir);        new Thread(task).start();    }

下面是DownloadTask子线程对下载任务的管理逻辑。也是委托给FileDownlodered类进行实际的下载操作。这个类的主要功能是新建一个下载任务,并实现FileDownlodered下载类回调过来的反馈信息处理。

private class DownloadTask implements Runnable {        private final String path;        private final File saveDir;        private FileDownlodered fileDownloader;        public DownloadTask(String path, File saveDir) {            this.path = path;            this.saveDir = saveDir;        }        public void exit() {            if (fileDownloader != null) {                fileDownloader.exit();            }        }        // 下载任务含一个下载监听器。        DownloadProcessListener downloadProcessListener = new DownloadProcessListener() {//这个监听器是监听来自下载器FileDownlodered反馈过来的下载进度信息,然后通过handler进制更新ui            @Override            public void onDownloadSize(int downloadSize) {                // TODO Auto-generated method stub                Message msg = new Message();                msg.what = PROCESSING;                msg.getData().putInt("size", downloadSize);                uiHandler.sendMessage(msg);            }        };        @Override        public void run() {            // TODO Auto-generated method stub            try {                // new一个下载器,下载器必                //须放在这里,放在构造器里面因为设计到网络请求方面延时操作可能会报ui线程延时的异常。                fileDownloader = new FileDownlodered(getApplicationContext(), path,                        saveDir, 8);                progressBar.setMax(fileDownloader.getFileSize());                Log.i(TAG, Thread.currentThread().getName() + "进入下载");                // 下载器开始下载                Log.i(TAG, String.valueOf(fileDownloader                        .download(downloadProcessListener)));//这个download方法是下载器真正执行下载功能的方法,//            } catch (Exception e) {                // TODO: handle exception                e.printStackTrace();                uiHandler.sendMessage(uiHandler.obtainMessage(FAILURE));            }        }    }

FileDownlodered的download方法
这个方法主要的功能是

public int download(DownloadProcessListener listener) {        try {            RandomAccessFile randOut = new RandomAccessFile(this.saveFile,                    "rwd");            if (this.fileSize > 0)                randOut.setLength(this.fileSize);            randOut.close();            URL url = new URL(this.downloadUrl);            // 如果data map中存放的数据记录数没能跟线程数一样。那么说明不同同一次下载操作,data记录的            // 数据要清零。            if (this.data.size() != this.threads.length) {                this.data.clear();                for (int i = 0; i < this.threads.length; i++)                    this.data.put(i + 1, 0);                this.downloadSize = 0;            }            Log.i(TAG, "data长度 " + String.valueOf(this.data.size()));            for (int i = 0; i < this.threads.length; i++) {                int downloadedLength = this.data.get(i + 1);                if (downloadedLength < this.block                        && this.downloadSize < this.fileSize) {                    // 表名这个线程的任务还没开始                    this.threads[i] = new DownloadThread(this, url,                            this.saveFile, this.block, this.data.get(i + 1),                            i + 1);                    this.threads[i].setPriority(7);                 this.threads[i].start();                } else {                    this.threads[i] = null;// 表明线程已完成下载任务                }            }            fileService.delete(this.downloadUrl);            fileService.save(this.downloadUrl, this.data);            boolean notfinished = true;            while (notfinished) {                Thread.sleep(90);                notfinished = false;                for (int i = 0; i < this.threads.length; i++) {                    if (this.threads[i] != null// 当每个线程都没下载完成的时候进入,                            && !this.threads[i].isFinished()) {                        notfinished = true;                        if (this.threads[i].getDownloadedLength() == -1) {                            // 判定每个线程是否发生了故障,如果发生了故障重启下载                            this.threads[i] = new DownloadThread(this, url,                                    this.saveFile, this.block,                                    this.data.get(i + 1), i + 1);                            this.threads[i].setPriority(7);                            this.threads[i].start();                        }                    }                }                if (listener != null)                    listener.onDownloadSize(this.downloadSize);            }            if (downloadSize == this.fileSize)// 如果下载的文件大小和从http头部获取的资源大小一样说明下载完成了。                //那么将所有线程记录的信息删除。                this.fileService.delete(this.downloadUrl);        } catch (FileNotFoundException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();        } catch (InterruptedException e) {            // TODO Auto-generated catch block            e.printStackTrace();        }        return this.downloadSize;    }

DownloadThread的run方法是直接httpconnection到远程服务器那边,然后用RandomAccessFile类可随机访问改写文件的类进行多线程下载。

    @Override    public void run() {        // TODO Auto-generated method stub        // 每个线程下载各自的每一块信息。        if (downloadedLength < block) {            try {//新建一个httpurl链接                HttpURLConnection connection = (HttpURLConnection) downUrl                        .openConnection();                connection.setConnectTimeout(5 * 1000);                connection.setRequestMethod("GET");                connection                        .setRequestProperty(                                "Accept",                                "image/gif, image/jpeg,image/pjpeg,image/pjpeg,application/x-shockwave-flash,"                                        + "application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application,"                                        + "application/vnd.ms-excel,application/vnd.ms-powerpoint,application/msword,*/*");                connection.setRequestProperty("Accept-Language", "zh-CN");                connection.setRequestProperty("Charset", "UTF-8");                int startPos = block * (threadId - 1) + downloadedLength;// 在每个线程里面自己计算下载的起始位置和终止位置。                int endPos = block * threadId - 1;    //利用了http协议的range字段可以设置下载的位置。           connection.setRequestProperty("Range", "bytes=" + startPos                        + "-" + endPos);                connection.setRequestProperty("Connection", "Keep-Alive");                InputStream inStream = connection.getInputStream();                byte[] buffer = new byte[1024];                int length = 0;                Print("Thread: " + this.threadId                        + " start to download from position:\t" + startPos);                RandomAccessFile threadFile = new RandomAccessFile(                        this.saveFile, "rwd");                threadFile.seek(startPos);                while (!this.downloader.getExited()                        && (length = inStream.read(buffer, 0, 1024)) != -1) {    //判定条件:依据downloader.getExited()是否给予了暂停信号,并当前下载块是否到了流的末尾。                    threadFile.write(buffer, 0, length);                    downloadedLength += length;                    downloader.update(this.threadId, this.downloadedLength);//更新下载数据到sqlite数据库中,用于暂停后恢复下载位置用。                    downloader.append(length);                }                threadFile.close();                inStream.close();                if (downloader.getExited())                    Print("Thread: " + this.threadId + " has been paused.");                else                    // 如果下载器没有暂停,                    Print("Thread: " + this.threadId + " download finish");                this.finished = true;// 无任是真的下载完成还是用于中断下载,都给出下载完的信息。            } catch (Exception e) {// 链接远程资源发生故障,                // TODO: handle exception                this.downloadedLength = -1;                Print("Thread: " + this.threadId + ":" + e.toString());            }        }    }
0 0