安卓系统下的多线程断点下载实现

近期研究多线程下载,写了个demo。整理下来,或许会对别人有帮助。

多线程下载的话一般开启两到三个线程吧。假设线程太多的话时间会浪费在线程的切换上,倒是浪费了大把的时间。线程多了也不是一件好事。

原理的话看我的还有一篇博文,其实是将代码移植到了安卓系统上。java实现的多线程下载demo

public class MainActivity extends Activity {
    protected static final int DOWNLOAD_ERROR = 1;
    private static final int THREAD_ERROR = 2;
    public static final int DWONLOAD_FINISH = 3;
    private EditText et_path;
    private EditText et_count;
    /**
     * 存放进度条的布局
     */
    private LinearLayout ll_container;

    /**
     * 进度条的集合
     */
    private List<ProgressBar> pbs;

    /**
     * android下的消息处理器。在主线程创建。才干够更新ui
     */
    private Handler handler = new Handler(){
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case DOWNLOAD_ERROR:
                Toast.makeText(getApplicationContext(), "下载失败", 0).show();
                break;
            case THREAD_ERROR:
                Toast.makeText(getApplicationContext(), "下载失败,请重试", 0).show();
                break;
            case DWONLOAD_FINISH:
                Toast.makeText(getApplicationContext(), "完成下载", 0).show();
                break;
            }
        };
    };

    /**
     * 线程的数量
     */
    private int threadCount = 3;

    /**
     * 每一个下载区块的大小
     */
    private long blocksize;

    /**
     * 正在执行的线程的数量
     */
    private  int runningThreadCount;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        et_path = (EditText) findViewById(R.id.et_addr);
        et_count = (EditText) findViewById(R.id.et_num);
        ll_container = (LinearLayout) findViewById(R.id.ll_pb);
    }

    public void download(View view){
        //下载文件的路径
        final String path = et_path.getText().toString().trim();
        if(TextUtils.isEmpty(path)){
            Toast.makeText(this, "对不起下载路径不能为空", 0).show();
            return;
        }
        String count = et_count.getText().toString().trim();
        if(TextUtils.isEmpty(path)){
            Toast.makeText(this, "对不起,线程数量不能为空", 0).show();
            return;
        }
        threadCount = Integer.parseInt(count);
        //清空掉旧的进度条
        ll_container.removeAllViews();
        //在界面里面加入count个进度条
        pbs = new ArrayList<ProgressBar>();
        for(int j=0;j<threadCount;j++){
            ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.pb, null);
            ll_container.addView(pb);
            pbs.add(pb);
        }
        Toast.makeText(this, "開始下载", 0).show();
        new Thread(){
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");
                    conn.setConnectTimeout(5000);
                    int code = conn.getResponseCode();
                    if (code == 200) {
                        long size = conn.getContentLength();// 得到服务端返回的文件的大小
                        System.out.println("server文件的大小:" + size);
                        blocksize = size / threadCount;
                        // 1.首先在本地创建一个大小跟server一模一样的空白文件。
                        File file = new File(Environment.getExternalStorageDirectory(),getFileName(path));
                        RandomAccessFile raf = new RandomAccessFile(file, "rw");
                        raf.setLength(size);
                        // 2.开启若干个子线程分别去下载相应的资源。

runningThreadCount = threadCount;
                        for (int i = 1; i <= threadCount; i++) {
                            long startIndex = (i - 1) * blocksize;
                            long endIndex = i * blocksize - 1;
                            if (i == threadCount) {
                                // 最后一个线程
                                endIndex = size - 1;
                            }
                            System.out.println("开启线程:" + i + "下载的位置:" + startIndex + "~"
                                    + endIndex);
                            int threadSize = (int) (endIndex - startIndex);
                            pbs.get(i-1).setMax(threadSize);
                            new DownloadThread(path, i, startIndex, endIndex).start();
                        }
                    }
                    conn.disconnect();
                } catch (Exception e) {
                    e.printStackTrace();
                    Message msg = Message.obtain();
                    msg.what = DOWNLOAD_ERROR;
                    handler.sendMessage(msg);
                }

            };
        }.start();

    }
    private class DownloadThread extends Thread {

        private int threadId;
        private long startIndex;
        private long endIndex;
        private String path;

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

        @Override
        public void run() {
            try {
                // 当前线程下载的总大小
                int total = 0;
                File positionFile = new File(Environment.getExternalStorageDirectory(),getFileName(path)+threadId + ".txt");
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setRequestMethod("GET");
                // 接着从上一次的位置继续下载数据
                if (positionFile.exists() && positionFile.length() > 0) {// 推断是否有记录
                    FileInputStream fis = new FileInputStream(positionFile);
                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(fis));
                    // 获取当前线程上次下载的总大小是多少
                    String lasttotalstr = br.readLine();
                    int lastTotal = Integer.valueOf(lasttotalstr);
                    System.out.println("上次线程" + threadId + "下载的总大小:"
                            + lastTotal);
                    startIndex += lastTotal;
                    total += lastTotal;// 加上上次下载的总大小。
                    fis.close();
                    //存数据库。
                    //_id path threadid total
                }

                conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
                        + endIndex);

                conn.setConnectTimeout(5000);
                int code = conn.getResponseCode();
                System.out.println("code=" + code);
                InputStream is = conn.getInputStream();
                File file = new File(Environment.getExternalStorageDirectory(),getFileName(path));
                RandomAccessFile raf = new RandomAccessFile(file, "rw");
                // 指定文件開始写的位置。

raf.seek(startIndex);
                System.out.println("第" + threadId + "个线程:写文件的開始位置:"
                        + String.valueOf(startIndex));
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = is.read(buffer)) != -1) {
                    RandomAccessFile rf = new RandomAccessFile(positionFile,
                            "rwd");
                    raf.write(buffer, 0, len);
                    total += len;
                    rf.write(String.valueOf(total).getBytes());
                    rf.close();
                    pbs.get(threadId-1).setProgress(total);
                }
                is.close();
                raf.close();

            } catch (Exception e) {
                e.printStackTrace();
                Message msg = Message.obtain();
                msg.what = THREAD_ERROR;
                handler.sendMessage(msg);
            } finally {
                // 仅仅有全部的线程都完成下载后 才干够删除记录文件。
                synchronized (MainActivity.class) {
                    System.out.println("线程" + threadId + "完成下载了");
                    runningThreadCount--;
                    if (runningThreadCount < 1) {
                        System.out.println("全部的线程都工作完成了。

删除暂时记录的文件");
                        for (int i = 1; i <= threadCount; i++) {
                            File f = new File(Environment.getExternalStorageDirectory(),getFileName(path)+ i + ".txt");
                            System.out.println(f.delete());
                        }
                        Message msg = Message.obtain();
                        msg.what = DWONLOAD_FINISH;
                        handler.sendMessage(msg);
                    }
                }

            }
        }
    }

    private String getFileName(String path){
        int start = path.lastIndexOf("/")+1;
        return path.substring(start);
    }

}

安卓系统下须要加上訪问网络的权限和訪问本地内存卡的权限。例如以下图:

源代码已经上传到了CSDN: http://download.csdn.net/detail/rootusers/8508137

时间: 2024-10-05 22:13:45

安卓系统下的多线程断点下载实现的相关文章

安卓系统下的多线程断点下载实现2利用开源框架XUtils

使用开源框架可以大大降低开发的难度,减少开发的周期,并且bug也少的多,软件运行起来更稳定. xUtils简介 xUtils 包含了很多实用的android工具. xUtils 支持大文件上传,更全面的http请求协议支持(10种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响- xUitls 最低兼容android 2.2 (api level 8) 下载地址:https://github.com/wyouflf/xUtils 下面是一个demo: public class Ma

JAVA下实现多线程断点下载

多线程断点下载:顾名思义是用多线程实现的,断点是当第三方因素(断电.断网等)中断下载时,下次下载可以继续上次下载的地方下载. 1.通过getContentLength可以获取要下载文件的大小,这样可以在本机上创建一个相同大小的文件用来下载. int fileLength = connection.getContentLength(); 2.由于是多线程,所以要给每一个线程均分分配要下载的位置. for(int i = 0; i < threadCount; i ++) { int startTh

在Http协议下实现多线程断点的下载

0.使用多线程下载会提升文件下载的速度,那么多线程下载文件的过程是: (1)首先获得下载文件的长度,然后设置本地文件的长度 HttpURLConnection.getContentLength(); RandomAccessFile file = new RandomAccessFile("QQWubiSetup.exe","rwd"); file.setLength(filesize);//设置本地文件的长度 (2)根据文件长度和线程数计算每条线程下载的数据长度和

多线程断点下载原理(java代码实例演示)

其实多线程断点下载原理,很简单的,那么我们就来先了解下,如何实现多线程的断点下载,首先:你必须明白第一点,那么就是,什么是多线程下载,该知识点可以查看本博客上一篇文章,Android之多线程下载原理,断点下载呢,其实就是在这个的基础之上添加了一些东西,那么添加了什么东西了,现在来做一个详细的了解. 1.在下载的过程中,边下载,变用一个文件来记录下载的位置,也就是下载了多少的数据 1.创建文件 2.记录下载多少数据 3.存储数据 2.第二次下载的时候,就去读取文件中是否存有数据,读取上次下载的位置

Android/java http多线程断点下载(附源码)

先看下项目结构: http多线程断点下载涉及到 数据库,多线程和http请求等几个模块,东西不是很多,想弄清楚也不是很困难,接下来我和大家分享下我的做法. 一.先看MainActivity.java 成员变量,主要是一些下载过程的变量和handler private String path = "http://192.168.1.3:8080/wanmei/yama.apk"; private String sdcardPath; private int threadNum = 5;

iOS开发网络篇—大文件的多线程断点下载(转)

http://www.cnblogs.com/wendingding/p/3947550.html iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分). 项目中用到的主要

iOS开发网络篇—多线程断点下载

iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始,创建一个和要下载的文件大小相同的文件(如果要下载的文件为100M,那么就在沙盒中创建一个100M的文件,然后计算每一段的下载量,开启多条线程下载各段的数据,分别写入对应的文件部分). 项目中用到的主要类如下: 完成的实现代码如下: 主控制器中的代码: 1 #import "YYViewControl

iOS开发网络请求——大文件的多线程断点下载

iOS开发中网络请求技术已经是移动app必备技术,而网络中文件传输就是其中重点了.网络文件传输对移动客户端而言主要分为文件的上传和下载.作为开发者从技术角度会将文件分为小文件和大文件.小文件因为文件大小比较小导致传输所需时间少传输就快,因此不太容易影响用户体验,可用的技术就多.而大文件因为文件大小比较大导致传输时间长,因此就需要考虑到各种用户体验,比如避免在上传下载文件过程中阻塞主线程影响用户体验,就需要使用到多线程技术:为了给用户友好的进度提示,因此又需要开发中跟踪数据上传和下载数据的变化:为

Java多线程断点下载

多线程下载已经提高了下载的效率,但是当一些特殊情况发生的时候,我们需要对程序进行处理,这样效率会更高.比如,断电断网等造成下载中断,那么我们下一次又要重新开始下载,这样效率底下,所以我们可以考虑使用断点下载.其原理主要是把每次每个线程的下载状况(已经下载的位置)保存到文件,下次读取出来,从上一次下载的位置继续下载,这样就大大提高了下载的效率. 效果: 开始下载: 下载过程中: 下载过程中,系统临时文件保存已下载的位置: 下载完毕,系统清楚记录下载位置的临时文件: 附代码如下: 1 import