Android -- 多线程下载

因为Android应用程序是java写的,基本上很多java写的程序都可以直接照搬到Android上面,移植性非常Good。这里讲一下多线程下载,就是每个线程都下载自己的那部分,那么就需要平均分配分割线程下载多少,一张图来说明一下。

image

第一个要点:http头里面有一个”Range”,就是在这里设置从哪里开始下载。

第二个要点:RandomAccessFile,java给出的这个API。

分别开启多个线程,每个线程规定得有下载的大小,然后分别下载,下载下来的inputstream再组合在一起,怎么组合呢,就会用到RandomAccessFile。

downLoad函数                                                                          

public void downLoad() throws Exception {
        currentProcess = 0;
        new Thread() {

            @Override
            public void run() {
                URL url;
                try {
                    url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url
                            .openConnection();
                    conn.setConnectTimeout(5000);
                    conn.setRequestMethod("GET");
                    int code = conn.getResponseCode();
                    System.out
                            .println("int code = conn.getResponseCode();---------->"
                                    + code);
                    if (code == 200) {
                        // 服务器返回的数据的长度,实际上就是文件的长度
                        int length = conn.getContentLength();
                        System.out.println("文件总长度" + length);

                        // 在客户端本地创建出来一个大小跟服务器端文件大小一样的临时文件
                        RandomAccessFile raf;
                        raf = new RandomAccessFile("/data/data/com.yydcdut.duothread/lala.exe", "rwd");
                        raf.setLength(length);
                        raf.close();
                        // 假设是3个线程去下载资源
                        // 平均每一个线程下载的文件大小
                        int blockSize = length / threadCount;
                        for (int threadId = 1; threadId <= threadCount; threadId++) {
                            // 第一个线程下载的开始位置
                            int startIndex = (threadId - 1) * blockSize;
                            int endIndex = threadId * blockSize;
                            if (threadId == threadCount) {
                                // 最后个线程下载的长度弄得稍微大一点
                                endIndex = length;
                            }
                            System.out.println("线程:" + threadId + "---下载:"
                                    + startIndex + "--->" + endIndex);
                            // 开始下载
                            new DownloadThread(path, threadId, startIndex,
                                    endIndex).start();
                        }
                    } else {
                        System.out.println("服务器错误!!!");
                        Message msg = new Message();
                        msg.what = SERVER_ERROR;
                        handler.sendMessage(msg);
                    }
                } catch (MalformedURLException e1) {
                    // TODO 自动生成的 catch 块
                    e1.printStackTrace();
                } catch (Exception e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
                super.run();
            }
        }.start();
    }

httpUrlConnection先得到要下载的文件的大小,然后将这个文件分别分为几份(我这里是开启三个线程下载,所以平均分了3份)。还有先建立一个与下载大小一样的文件,通过RandomAccessFile实现。

下载子线程                                                                                   

public class DownloadThread extends Thread {
        private int threadId;
        private int startIndex;
        private int endIndex;
        private String path;

        public DownloadThread(String path, int threadId, int startIndex,
                int endIndex) {
            super();
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.path = path;
        }

        @Override
        public void run() {

            File tempFile = new File("/data/data/com.yydcdut.duothread/" + threadId + ".txt");
            if (tempFile.exists() && tempFile.length() > 0) {
                try {
                    FileInputStream fis = new FileInputStream(tempFile);
                    byte[] temp = new byte[1024];
                    int leng = fis.read(temp);
                    String downLoadLen = new String(temp, 0, leng);
                    int downloadlenInt = Integer.parseInt(downLoadLen);

                    int alreadyDowlLoad = downloadlenInt - startIndex;
                    currentProcess += alreadyDowlLoad;
                    startIndex = downloadlenInt;// 修改下载的真实的开始位置
                    fis.close();
                } catch (Exception e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }

            }

            URL url;
            try {
                url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setRequestMethod("GET");
                conn.setConnectTimeout(5000);
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
                        + endIndex);
                System.out.println("线程真实下载:" + threadId + "下载----" + startIndex
                        + "---->" + endIndex);
                int code = conn.getResponseCode();// 从服务器请求全部资源200 如果请求部分资源206
                System.out.println("threadId--->" + threadId + "----code:"
                        + code);
                InputStream is = conn.getInputStream();// 设置请求位置,返回是当前位置对应的文件输入流
                // 随机写文件的时候从那个位置开始写
                RandomAccessFile raf = new RandomAccessFile("/data/data/com.yydcdut.duothread/lala.exe",
                        "rwd");
                raf.seek(startIndex);
                int len = 0;
                byte[] buffer = new byte[1024];
                int total = 0;// 记录下载多少了
                while ((len = is.read(buffer)) != -1) {
                    total += len;
                    raf.write(buffer, 0, len);
                    // System.out.println("线程:"+threadId+"下载了total:"+total);
                    RandomAccessFile file = new RandomAccessFile("/data/data/com.yydcdut.duothread/"
                            + threadId + ".txt", "rwd");
                    file.write((total + startIndex + "").getBytes());
                    file.close();
                    // 更新进度条
                    synchronized (MainActivity.this) {
                        currentProcess += len;
                        pb.setProgress(currentProcess);
                        Message msg = new Message();
                        msg.what = UPDATE_TEXT;
                        handler.sendMessage(msg);
                    }
                }
                is.close();
                raf.close();
                System.out.println("线程" + threadId + "下载完毕............");
            } catch (Exception e) {
                // TODO 自动生成的 catch 块
                e.printStackTrace();
            } finally {
                threadFinish();
            }
            super.run();
        }
    }

在这里建立了一个txt文件,目的就是将下载到哪的数据存进去,如果网断了再下载的话,实现断点下载,那么下次下载就可以从这里读取数据,然后在写入文件,就不需要重头再分配大小下载了。这里还用到了线程同步去更新进度条。

删除临时txt文件threadFinish                                                      

private synchronized void threadFinish() {
        runningThread--;
        if (runningThread == 0) {
            for (int i = 1; i <= 3; i++) {
                File file = new File("/data/data/com.yydcdut.duothread/" + i + ".txt");
                file.delete();
            }
            System.out.println("文件下载完毕,删除所有的下载记录。。。");
            Message msg = new Message();
            msg.what = DOWN_LOAD_FINISH;
            handler.sendMessage(msg);
        }
    }

Handler                                                                                    

private Handler handler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case DOWN_LOAD_ERROR:
                Toast.makeText(getApplicationContext(), "下载失败", 0).show();
                break;
            case SERVER_ERROR:
                Toast.makeText(getApplicationContext(), "服务器 错误,下载失败", 0)
                        .show();
                break;
            case DOWN_LOAD_FINISH:
                Toast.makeText(getApplicationContext(), "文件下载完毕", 0).show();
                break;
            case UPDATE_TEXT:
                et_path.setText("当前进度:" + pb.getProgress() * 100 / pb.getMax());
                break;
            }

            super.handleMessage(msg);
        }

    };

我是天王盖地虎的分割线                                                               

源代码:http://pan.baidu.com/s/1dD1Qx01

MutilDownLoader.zip(JAVA)

多线程下载.zip(Android)

转载请注明出处:http://www.cnblogs.com/yydcdut

原文地址:https://www.cnblogs.com/yydcdut/p/3796197.html