Android中实现多线程下载的两种方式示例及浅析之一(无断点续传)

来源:互联网 发布:正版spss软件多少钱 编辑:程序博客网 时间:2024/06/05 17:49

Android里面下载一般应该有两种方式,一种是多线程实现,一种是异步。而实现多线程在java里可以有两种方式,一种是多个线程实现同一个Runnable,另外就是将一个文件长度分为n个部分,然后让n个线程去分别下载1部分。
[1]我阅读了网上的一个方法2的实现代码,然后自己实现了一个,基本思路就是实现一个单个线程类,在这个单个线程类就完成3个操作,
1是从URL连接读取流
2是将读取的字节数组写入到本地文件中
3是将当前已经读取的长度返回
然后开启一个线程,这个线程的操作是创建一个线程数组,每个元素是刚刚的那个单个线程类,在这个线程中分配每个线程的下载起点和下载终点,然后根据各个线程的下载进度之和来更新进度条。
demo如下:

package com.study.daniel.downloadtest;import java.io.BufferedInputStream;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;/** * Created by daniel on 15-8-1. */public class DownloadSingleThread extends Thread{//单个下载线程    /**文件下载URL*/    private String downloadURL;    /**文件保存路径*/    private File file;    /**单个线程下载文件大小*/    private int partSize;    /**当前已经下载的长度*/    private int currentLength;    /**线程id*/    private int threadID;    /**当前线程是否下载完成*/    private boolean isCompleted=false;    public DownloadSingleThread(String downloadURL,File file,int partSize,int threadID){        this.partSize=partSize;        this.downloadURL=downloadURL;        this.file=file;        this.threadID=threadID;    }    public void run(){        BufferedInputStream bis=null;//读取下载文件流        RandomAccessFile raf=null;//写入文件流        try {            //计算下载起点,终点            int startPos = partSize * threadID;//开始位置            int endPos = partSize * (threadID +1)-1;//结束位置            //建立连接            URL url=new URL(downloadURL);            URLConnection connection=url.openConnection();//获得连接            //设置本次连接的下载起点和终点            connection.setRequestProperty("Range","bytes="+startPos+"-"+endPos);            //创建输入流            bis=new BufferedInputStream(connection.getInputStream());           //创建文件写入流            raf=new RandomAccessFile(file,"rwd");            raf.seek(startPos);            byte[] buffer=new byte[1024];//一次读取一k            int len;            while((len=bis.read(buffer,0,1024))!=-1){//本线程下载是否完成                raf.write(buffer,0,len);//读取并写入                currentLength+=len;            }            isCompleted=true;//当前线程下载完成        } catch (MalformedURLException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally {//检查并关闭IO流            if(bis!=null)                try {                    bis.close();                } catch (IOException e) {                    e.printStackTrace();                }            if(raf!=null)                try {                    raf.close();                } catch (IOException e) {                    e.printStackTrace();                }        }    }    public int getCurrentLength(){//返回当前下载长度        return currentLength;    }    public boolean isCompleted(){        return isCompleted;    }}-------------------------------------------------------package com.study.daniel.downloadtest;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.support.v7.app.ActionBarActivity;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import java.io.File;import java.io.IOException;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;public class MainActivity extends ActionBarActivity implements View.OnClickListener {    private TextView tv_downloaded;    private ProgressBar progressBar;    Handler handler = new Handler() {        public void handleMessage(Message msg) {            //更新进度条            progressBar.setProgress(msg.getData().getInt("size"));            //算算当前进度比            float temp = (float) progressBar.getProgress() / (float) progressBar.getMax();            //显示进度百分比            tv_downloaded.setText((int) (temp * 100) + "%");            //下载完成            if (progressBar.getProgress() == progressBar.getMax())                Toast.makeText(MainActivity.this, "下载完成!", Toast.LENGTH_SHORT).show();            msg.arg1 = 0;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv_downloaded = (TextView) findViewById(R.id.tv_downloaded);        progressBar = (ProgressBar) findViewById(R.id.progressbar);        findViewById(R.id.bt_download).setOnClickListener(this);    }    public void onClick(View view) {        if (view.getId() == R.id.bt_download)            download();    }    public void download() {        //设置下载保存路径        String path = Environment.getExternalStorageDirectory()                + "/newdownload/";//这是下载文件保存目录        File file = new File(path);        if (!file.exists()) {//如果该保存目录不存在就创建            file.mkdir();        }        //初始化进度条        progressBar.setProgress(0);        //设置下载URL        String downloadURL = "http://gdown.baidu.com/data/wisegame/91319a5a1dfae322/baidu_16785426.apk";        String fileName = "baidu_16785426.apk";        int threadNum = 5;        String filePath = path + fileName;        System.out.println("download file  path:" + filePath);        DownloadTask downloadTask = new DownloadTask(threadNum, downloadURL, filePath);        downloadTask.start();    }    private class DownloadTask extends Thread {        private int threadNum;//开启线程数        private String downloadURL;//下载链接地址        private String savePath;//文件保存路径        private int partSize;//每一个线程的下载量,根据下载文件总长度计算得出        public DownloadTask(int threadNum, String downloadURL, String savePath) {            this.threadNum = threadNum;            this.downloadURL = downloadURL;            this.savePath = savePath;        }        public void run() {            try {                URL url = new URL(downloadURL);                URLConnection connection = url.openConnection();                int totalLength = connection.getContentLength();//总长度                if (totalLength <= 0) {                    System.out.println("读取文件失败");                    return;                }                //根据下载文件长度设置进度条的最大值                progressBar.setMax(totalLength);                //计算单个线程下载长度                partSize = (totalLength % threadNum == 0) ? totalLength / threadNum                        : totalLength / threadNum + 1;                DownloadSingleThread[] threads = new DownloadSingleThread[threadNum];                File file = new File(savePath);                for (int i = 0; i < threads.length; i++) {//创建并开启单个线程                    DownloadSingleThread thread = new DownloadSingleThread(downloadURL, file, partSize, i);                    threads[i] = thread;                    threads[i].start();                }                boolean isfinished = false;                int totalDownloadSize = 0;//5个线程当前下载总量                while (!isfinished) {                    isfinished = true;                    totalDownloadSize = 0;                    for (int i = 0; i < threads.length; i++) {                        if (!threads[i].isCompleted())//只要有一个线程没有完成,就没有完成下载                            isfinished = false;                        totalDownloadSize += threads[i].getCurrentLength();//累加                        System.out.println("totalDownloadSize----------" + totalDownloadSize);                    }                    //更新视图                    Message msg = handler.obtainMessage(0x11);//发送当前下载总量去更新视图                    msg.getData().putInt("size", totalDownloadSize);                    msg.sendToTarget();                    sleep(1000);//每隔1秒更新一次                }            } catch (MalformedURLException e) {                e.printStackTrace();            } catch (IOException e) {                e.printStackTrace();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    }

[2]方法1的代码是我参考方法2写的,自己实现。最后调很多遍不知为什么显示下载结束的Toast始终不消失,我以为是setRequestProperty()方法造成的下载没有结束引起,结果发现不是。后来终于发现是因为始终没有跳出while(!isfinished)循环,所以一直在更新handler,所以一直显示Toast,这个小问题搞了很久,逻辑上的,后来用了一点小技巧搞定。
demo如下:

package com.study.daniel.runnabletest;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.support.v7.app.ActionBarActivity;import android.view.View;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import java.io.BufferedInputStream;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;public class MainActivity extends ActionBarActivity implements View.OnClickListener{    /**本地保存目录*/    private  String path= Environment.getExternalStorageDirectory()            + "/RunnableDownload/";    private File directory;    /**下载连接URL*/    private String downloadURL="http://gdown.baidu.com/data/wisegame/91319a5a1dfae322/baidu_16785426.apk";    private String fileName="baidu_16785426.apk";    /**下载文件总大小*/    private int fileSize;    /**输入流读取连接*/    private BufferedInputStream bis;    /**随机流保存文件*/    private RandomAccessFile raf;    /**每次读入保存的字节数组*/    private byte[] buffer;    /**下载文件*/    private File file;    /**线程数组*/    private Thread[] threads;    /**自定义线程类完成下载前的准备工作*/    PrepareThread prepareThread;    /**记录已下载文件距离文件开头的位置,初始为0*/    private static int position=0;    private boolean isfinished=false;    private boolean runIsFinished=false;    /**控件*/    private TextView tv_downloaded;    private ProgressBar progressBar;    Handler handler=new Handler(){        public void handleMessage(Message msg){                     progressBar.setProgress(msg.getData().getInt("size"));                     float temp=(float)progressBar.getProgress() /                        (float)progressBar.getMax();                     tv_downloaded.setText((int)(temp*100)+"%");                    if(progressBar.getMax()==progressBar.getProgress())                    {//下载完成                           Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show();                    }        }    };    private Runnable runnable=new Runnable() {        @Override        public void run() {            try {                URL url=new URL(downloadURL);                URLConnection connection=url.openConnection();                connection.setRequestProperty("Range","bytes="+0+"-"+fileSize);                //输入流                bis=new BufferedInputStream(connection.getInputStream());                buffer=new byte[1024];                //写入流                raf=new RandomAccessFile(file,"rw");                raf.seek(0);                int len;                while((len=bis.read(buffer,0,1024))!=-1){                    raf.write(buffer,0,len);                    position+=len;//为了更新进度条                }                runIsFinished=true;            } catch (MalformedURLException e) {                e.printStackTrace();            } catch (IOException e) {                e.printStackTrace();            }        }    };    public void onClick(View view){        if(view.getId()==R.id.bt_download)        {            //下载前的准备,文件及连接            prepareThread=new PrepareThread();            prepareThread.start();        }    }    protected void onCreate(Bundle savedInstance){        super.onCreate(savedInstance);        setContentView(R.layout.activity_main);        tv_downloaded=(TextView)findViewById(R.id.tv_downloaded);        progressBar=(ProgressBar)findViewById(R.id.progressbar);        findViewById(R.id.bt_download).setOnClickListener(this);    }    @Override    protected void onStart() {        super.onStart();    }    @Override    protected void onResume() {        super.onResume();    }    @Override    protected void onDestroy() {//程序销毁时候销毁线程        super.onDestroy();    }    /**     * 自定义线程类,做初始化工作     */    private class PrepareThread extends Thread{        int temp=0;        public void run(){            //设置本地下载文件保存目录            directory=new File(path);            if(!directory.exists()){//如果目录不存在则创建                directory.mkdir();            }            file=new File(path+fileName);//将下载文件保存为file            //连接            try {                URL url=new URL(downloadURL);                URLConnection connection=url.openConnection();                fileSize=connection.getContentLength();                progressBar.setMax(fileSize);                /**在这个线程里开启多线程,解决了执行先后的问题*/                threads=new Thread[3];//3个线程下载                for(int i=0;i<threads.length;i++){                    Thread thread=new Thread(runnable);//所有线程都执行同一个任务                    threads[i]=thread;                    thread.start();                }                while(!isfinished){//更新视图                    Message message=handler.obtainMessage(0x11);                    message.getData().putInt("size",position);                    message.sendToTarget();                    sleep(1000);                    isfinished=false;                    if(temp==2 && runIsFinished){                        isfinished=true;                    }                    if(runIsFinished ){                        temp++;                    }                }               System.out.println("----------------------out of while(!isfinished)");            } catch (MalformedURLException e) {                e.printStackTrace();            } catch (IOException e) {                e.printStackTrace();            } catch (InterruptedException e) {                e.printStackTrace();            }            finally {                /**既然使用runnable,那么所有线程使用的都是一个流,那就不妨在这个线程里面,                 * 等所有线程都结束再关闭流,就不存在问题了*/                if(bis!=null)                    try {                        bis.close();                    } catch (IOException e) {                        e.printStackTrace();                    }                if(raf!=null)                    try {                        raf.close();                    } catch (IOException e) {                        e.printStackTrace();                    }            }        }    }}

还遇到流的关闭的顺序问题,我原先把流的关闭放在Runnable的最后,但是造成流关闭多次的异常。我还没太搞清楚为什么。但是我将操作放到了那个准备线程中,就解决了。
运行结果如下,测试没有问题:
这里写图片描述
这里写图片描述
我将两个demo工程上传到csdn,大家下载参考吧!我没有实现的是断点续传。实现之后更新博客,传代码。
以下是下载地址:
Thread方法:http://download.csdn.net/detail/u012320459/8957715
Runnable方法:http://download.csdn.net/detail/u012320459/8957709

0 0