这里有两个功能点。
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());
            }
        }
    }