Android多线程下载

本文将介绍在Android平台下如何实现多线程下载,Java中支持的多线程下载方式在Android平台下都支持,其中主要有两种方式:一种方式是使用N多个线程分别下载文件的不同部分,最后把所有下载完的文件合并成一个文件;另一种方式是使用Java提供的RandomAccessFile类来实现文件的多线程下载,顾名思义,这个类的主要作用就是随机访问,也就是可以随时指定不同的读取、写入位置,本文就是用的这个类,用N个线程同时去下载文件的不同部分,最终合成一个文件,大大加快文件的下载。

第一步,我们先写一个线程类,来完成对指定的url进行下载:

public class FileDownloadThread extends Thread {

    private static final int BUFFER_SIZE = 1024;

    private URL url;

    private File file;

    private int startPosition;

    private int endPosition;

    private int curPosition;

    // 是否下载完成

    private boolean finished = false;

    private int downloadSize = 0;

    public FileDownloadThread(URL url, File file, int startPosition,

            int endPosition) {

        this.url = url;

        this.file = file;

        this.startPosition = startPosition;

        this.curPosition = startPosition;

        this.endPosition = endPosition;

    }

    @Override

    public void run() {

        BufferedInputStream bis = null;

        RandomAccessFile fos = null;

        byte[] buf = new byte[BUFFER_SIZE];

        URLConnection con = null;

        try {

            con = url.openConnection();

            con.setAllowUserInteraction(true);

            // 设置当前线程下载的起点,终点

            con.setRequestProperty("Range", "bytes=" + startPosition + "-"

                    + endPosition);

            // 使用java中的RandomAccessFile 对文件进行随机读写操作

            fos = new RandomAccessFile(file, "rw");

            // 设置开始写文件的位置

            fos.seek(startPosition);

            bis = new BufferedInputStream(con.getInputStream());

            // 开始循环以流的形式读写文件

            while (curPosition < endPosition) {

                int len = bis.read(buf, 0, BUFFER_SIZE);

                if (len == -1) {

                    break;

                }

                fos.write(buf, 0, len);

                curPosition = curPosition + len;

                if (curPosition > endPosition) {

                    downloadSize += len - (curPosition - endPosition) + 1;

                } else {

                    downloadSize += len;

                }

            }

            // 下载完成

            this.finished = true;

            bis.close();

            fos.close();

        } catch (IOException e) {

            Log.w(getName() + " yilee:", e.getMessage());

        }

    }

    public boolean isFinished() {

        return finished;

    }

    public int getDownloadSize() {

        return downloadSize;

    }

}

接下来就是Activity类了,实时更新下载进度条:

public class MultiThreadDownActivity extends Activity {

    private EditText downloadUrl;

    private EditText downloadFileName;

    private EditText downloadThreadNum;

    private Button downloadBt;

    private ProgressBar downloadProgressBar;

    private TextView progressMessage;

    private int downloadedSize = 0;

    private int fileSize = 0;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        downloadUrl = (EditText) findViewById(R.id.downloadUrl);

        downloadFileName = (EditText) findViewById(R.id.downloadFileName);

        downloadThreadNum = (EditText) findViewById(R.id.downloadThreadNum);

        progressMessage = (TextView) findViewById(R.id.progressMessage);

        downloadBt = (Button) findViewById(R.id.downloadBt);

        downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);

        downloadProgressBar.setVisibility(View.VISIBLE);

        downloadProgressBar.setMax(100);

        downloadProgressBar.setProgress(0);

        downloadBt.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {

                download();

            }

        });

    }

    private void download() {

        // 获取SD卡目录

        String dowloadDir = Environment.getExternalStorageDirectory()

                + "/yilee/";

        File file = new File(dowloadDir);

        // 创建下载目录

        if (!file.exists()) {

            file.mkdirs();

        }

        // 读取下载线程数,如果为空,则单线程下载

        int downloadTN = Integer.valueOf("".equals(downloadThreadNum.getText()

                .toString()) ? "1" : downloadThreadNum.getText().toString());

        // 如果下载文件名为空则获取Url尾为文件名

        int fileNameStart = downloadUrl.getText().toString().lastIndexOf("/");

        String fileName = "".equals(downloadFileName.getText().toString()) ? downloadUrl

                .getText().toString().substring(fileNameStart)

                : downloadFileName.getText().toString();

        // 开始下载前把下载按钮设置为不可用

        downloadBt.setClickable(false);

        // 启动文件下载线程

        new downloadTask(downloadUrl.getText().toString(),

                Integer.valueOf(downloadTN), dowloadDir + fileName).start();

    }

    Handler handler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            // 当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息

            int progress = (Double

                    .valueOf((downloadedSize * 1.0 / fileSize * 100)))

                    .intValue();

            if (progress == 100) {

                downloadBt.setClickable(true);

                progressMessage.setText("下载完成");

            } else {

                progressMessage.setText("进度:" + progress + "%");

            }

            downloadProgressBar.setProgress(progress);

        }

    };

    /**

     * 主下载线程

     */

    public class downloadTask extends Thread {

        private int blockSize, downloadSizeMore;

        private int threadNum;

        String urlStr, threadNo, fileName;

        public downloadTask(String urlStr, int threadNum, String fileName) {

            this.urlStr = urlStr;

            this.threadNum = threadNum;

            this.fileName = fileName;

        }

        @Override

        public void run() {

            FileDownloadThread[] fds = new FileDownloadThread[threadNum];

            try {

                URL url = new URL(urlStr);

                URLConnection conn = url.openConnection();

                // 获取下载文件的总大小

                fileSize = conn.getContentLength();

                // 计算每个线程要下载的数据量

                blockSize = fileSize / threadNum;

                // 解决整除后百分比计算误差

                downloadSizeMore = (fileSize % threadNum);

                File file = new File(fileName);

                for (int i = 0; i < threadNum; i++) {

                    // 启动线程,分别下载自己需要下载的部分

                    FileDownloadThread fdt = new FileDownloadThread(url, file,

                            i * blockSize, (i + 1) * blockSize - 1);

                    fdt.setName("Thread" + i);

                    fdt.start();

                    fds[i] = fdt;

                }

                boolean finished = false;

                while (!finished) {

                    // 先把整除的余数搞定

                    downloadedSize = downloadSizeMore;

                    finished = true;

                    for (int i = 0; i < fds.length; i++) {

                        downloadedSize += fds[i].getDownloadSize();

                        if (!fds[i].isFinished()) {

                            finished = false;

                        }

                    }

                    // 通知handler去更新视图组件

                    handler.sendEmptyMessage(0);

                    // 休息1秒后再读取下载进度

                    sleep(100);

                }

            } catch (Exception e) {

                Log.w("yilee", e.getMessage());

            }

        }

    }

}

XMl就不贴代码 了,几个控件而已,别忘了加入权限:

<!- 访问Internet权限 ->

<uses-permission android:name="android.permission.INTERNET"/>

<!- 在SDCard中创建与删除文件权限 ->

<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

<!- 往SDCard写入数据权限 ->

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

效果图如下:

5是进程数

值得注意的是,Android只是一个演示,其它很多地方如需要多线程下载都可以用到这个方法,感觉RandomAccessFile这个类好强大呀

PS,经过本天才大量实际测试(文件大小从几K到几百M,地址从内网,国内,国外,线程从1到100),感觉不出快了多少-。-,真是悲剧唉,不过想来想去想不出原因,应该是可以加快很多的,如果哪位大神看到了希望能给个看法,也不知道在真机上效果怎么样,开发Android应用却没有Android手机的程序猿真是伤不起呐~~

无觅相关文章插件,快速提升流量

,

发表评论