【android ,11】11.android多线程断点下载

来源:互联网 发布:宝宝学字软件 编辑:程序博客网 时间:2024/04/27 11:10

一、多线程下载原理:

1、在客户端创建出来一个文件,该文件大小与服务器上的文件大小完全性相同。

①、首先要知道服务器上文件的大小,通过相应头里 的content-length得到文件大小。

②、使用RandomAccessFile类随机创建一个文件 ,通过setLength方法设置文件大小。

2、开辟三个线程,进行下载,计算出每一个线程下载的数据块大小。

3、当三个线程都运行完毕后,所有的数据都已经下载好了。

二、javase中的文件下载案例:支持断点下载:

 

public classTestMutileDownload {

//服务器上要下载的资源的地址

    public static final String path="http://192.168.1.247:8080/youdao.exe";

    public static int threadcount;

   

    public static void main(String[] args) throws Exception{

        URL url = new URL(path);

//打开连接

        HttpURLConnection conn =(HttpURLConnection) url.openConnection();

        conn.setRequestMethod("GET");

//设置请求头,User-Agent:表示资源来自哪里

        conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

        conn.setConnectTimeout(5000);//得到服务器响应状态码的时间

      // 获取服务器文件的大小

        int len =conn.getContentLength();

        System.out.println("文件大小为"+len);

        // 在本地创建出来一个大小跟服务器文件一样大的资源

        File file = newFile("youdao.exe");

//创建下载的文件对象

        RandomAccessFilerandomfile  = new RandomAccessFile(file,"rwd");

        randomfile.setLength(len);

       

       

       

          // 假设只开启3个子线程

        int blocksize = len/3;

        for(int i =1;i<=3;i++){

            threadcount = 0;

            int startsize = (i-1)*blocksize;

            int endsize = (i)*blocksize - 1;

           

            if(i==3){

//              if(endsize<=len)

                    endsize = len;

            }

            new Thread(new DownLoadTask(i, startsize, endsize)).start();

           

            // 怎么才能知道3个子线程都执行完毕了呢?

        }  

    }

}

 

线程类:

class DownLoadTaskimplements Runnable{

    //线程的id

    private int id;

    //要下载的文件的开始位置

    private int startsize;

    // 要下载的文件的结束位置

    private int endsize;

    //HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");

 

    public DownLoadTask(int id, int startsize, int endsize) {

        this.id = id;

        this.startsize =startsize;

        this.endsize = endsize;

    }

   

   

   

    @Override

    public void run() {

        try {

            // 每一个线程创建执行的时候 都创建一个id.txt的文件,这个文件用来记录当前线程下载的进度

            File idfile = new File(id+".txt");

            // 判断是否记录的有下载的位置信息

            if(idfile.exists()){

                FileInputStream fis =new FileInputStream(idfile);

                byte[] result =StreamTools.getBytes(fis);

                String numberstr = newString(result);

                if(numberstr!=null&&(!"".equals(numberstr))){

                    int startposition = Integer.parseInt( numberstr);

                    if(startposition>startsize){

                        startsize =startposition; // 重新指定下载的开始位置

                    }

                   

                }

            }

           

            URL url = new URL(TestMutileDownload.path);

            HttpURLConnection conn = (HttpURLConnection)url.openConnection();

            conn.setRequestMethod("GET");

            conn.setRequestProperty("User-Agent", "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)");

            conn.setConnectTimeout(5000); //得到服务器响应状态码的时间

            // 指定当前线程从服务器上的哪个位置下载文件y

            if(startsize>endsize){

                startsize=endsize-1;

            }

            conn.setRequestProperty("Range","bytes="+startsize+"-"+endsize);

            System.out.println("线程id="+id+"开始位置"+startsize+"结束位置"+endsize);

            InputStream is = conn.getInputStream();

            File file = new File("youdao.exe");

            RandomAccessFile randomfile = new RandomAccessFile(file, "rwd");

//指定指针开始的位置

            randomfile.seek(startsize);

            //randomfile.write(arg0, arg1, arg2);

            byte[] buffer = new byte[1024];

            int len = 0;

            int total = 0;

            while( (len = is.read(buffer))!=-1){

                randomfile.write(buffer,0, len);

                // 记录当前 线程下载的数据量 和对应的位置给记录

                total +=len;

                FileOutputStream idfos =new FileOutputStream(idfile);

                idfos.write((startsize+total+"").getBytes());// 记录当前线程下载的位置信息

                idfos.flush();

                idfos.close();

            }

            randomfile.close();

            is.close();

            System.out.println("线程"+id+"下载完毕");

//          //线程下载完毕后 擦屁股的操作

//          if(idfile.exists()){

//              idfile.delete();

//          }

           

            synchronized (TestMutileDownload.class) {

                TestMutileDownload.threadcount++;

                if(TestMutileDownload.threadcount>=3){

                    System.out.println("所有的线程都执行完毕了");

                    // 擦屁股的操作

                    for(int i=1;i<=3;i++){

                        File deletefile = newFile(i+".txt");

                        System.out.println(i+"删除"+ deletefile.delete());

                    }

                }

            }

           

           

        } catch (Exception e) {

            e.printStackTrace();

        }

       

    }  

}

三、android的多线程下载:

1、设置布局:

<?xmlversion="1.0" encoding="utf-8"?>

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="fill_parent"

   android:layout_height="fill_parent"

    android:orientation="vertical">

 

    <TextView

       android:layout_width="fill_parent"

       android:layout_height="wrap_content"

        android:text="多线程断点下载器" />

 

    <EditText

        android:id="@+id/et_path"

       android:layout_width="fill_parent"

       android:layout_height="wrap_content"

       android:text="http://192.168.1.247:8080/youdao.exe"

        android:hint="请输入下载文件的路径" />

 

    <LinearLayout

        android:layout_width="fill_parent"

       android:layout_height="wrap_content"

       android:gravity="center_horizontal" //水平居中

       android:orientation="horizontal" >

 

        <Button

           android:id="@+id/bt_download"

           android:layout_width="wrap_content"

           android:layout_height="wrap_content"

            android:text="下载" />

 

        <Button

            android:id="@+id/bt_stop"

           android:layout_width="wrap_content"

           android:layout_height="wrap_content"

            android:text="暂停" />

    </LinearLayout>

 

    <ProgressBar //进度条组件

        android:id="@+id/pb"

       style="?android:attr/progressBarStyleHorizontal"//进度条的样式.

       android:layout_width="fill_parent"

       android:layout_height="wrap_content" />

 

    <TextView

        android:id="@+id/tv_progress"

       android:layout_width="fill_parent"

       android:layout_height="wrap_content"

        android:text="进度" />

 

</LinearLayout>

 

2、业务代码:

 

public class DemoActivityextends Activity implements OnClickListener {

    protected static final int ERROR = 404;

    public static final int DOWNLOAD_FINISH = 200;

    private EditText et_path;

    private Button bt_download;

    private Button bt_stop;

    private ProgressBar pb;

    private TextView tv_progress;

    public static int threadcount ;

    public int total; // 当前下载的进度

    public int totallen ; // 总的文件大小

   

    private Handler handler = new Handler(){

 

        @Override

        public voidhandleMessage(Message msg) {

            // TODO Auto-generated method stub

            super.handleMessage(msg);

            if(msg.what==ERROR){

                Toast.makeText(getApplicationContext(),"获取文件长度失败", 0).show();

                bt_download.setClickable(true);

                bt_download.setEnabled(true);

                return;

            }

            if(msg.what==DOWNLOAD_FINISH){

                bt_download.setClickable(true);

                bt_download.setEnabled(true);

                return;

            }

            int process = total*100/totallen;

            String strprocess = "当前进度"+process+"%";

            tv_progress.setText(strprocess);

        }

       

    };

   

   

   

    public boolean flag;

    @Override

    public void onCreate(BundlesavedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        et_path = (EditText)this.findViewById(R.id.et_path);

        bt_download = (Button)this.findViewById(R.id.bt_download);

        bt_stop = (Button)this.findViewById(R.id.bt_stop);

        pb = (ProgressBar)this.findViewById(R.id.pb);

        tv_progress = (TextView)this.findViewById(R.id.tv_progress);

     

        //注册按钮的点击事件

        bt_download.setOnClickListener(this);

        bt_stop.setOnClickListener(this);

       

    }

    @Override

    public void onClick(View v) {

        switch (v.getId()) {

        case R.id.bt_stop:

            flag = false;

            bt_download.setClickable(true);

            bt_download.setEnabled(true);

            break;

        case R.id.bt_download:

            final String path = et_path.getText().toString().trim();

            if("".equals(path)){

                Toast.makeText(this,"路径不能为空", 1).show();

                return ;

            }else{

                // 开启子线程 连接服务器 获取文件的大小

                bt_download.setClickable(false);

                bt_download.setEnabled(false);

                new Thread(){

                    public void run() {

                        try {

                            total = 0;

                            flag = true;

                            URL url = new URL(path);

                            HttpURLConnection conn = (HttpURLConnection)url.openConnection();

                            conn.setRequestMethod("GET");

                            conn.setRequestProperty("User-Agent", "Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)");

                            conn.setConnectTimeout(5000); //得到服务器响应状态码的时间

                            // 获取服务器文件的大小

                            totallen = conn.getContentLength();

                            //设置进度条的最大值

                            pb.setMax(totallen);

                            System.out.println("文件大小为"+totallen);

                            // 在本地创建出来一个大小跟服务器文件一样大的资源

                            File file =  newFile(Environment.getExternalStorageDirectory(),getFileName(path));

                            RandomAccessFile randomfile = new RandomAccessFile(file, "rwd");

                            randomfile.setLength(totallen);

                           

                           

                           

                            // 假设只开启3个子线程

                            int blocksize = totallen/3;

                            for(int i = 1;i<=3;i++){

                                threadcount = 0;

                                int startsize =(i-1)*blocksize;

                                int endsize =(i)*blocksize - 1;

                                if(i==3){

                                        endsize = totallen;

                                }

                               

                                new Thread(newDownLoadTask(i, startsize, endsize,path)).start();

                               

                                // 怎么才能知道3个子线程都执行完毕了呢?

                               

                            }

                        } catch (Exception e) {

                            e.printStackTrace();

//                          Toast.makeText(this, "下载出错", 0).show();

                            Message msg = new Message();

                            msg.what = ERROR;

                            handler.sendMessage(msg);

                        }

                    };

                }.start();

               

            }

           

           

            break;

        }

       

    }

   

    class DownLoadTask implements Runnable{

        //线程的id

        private int id;

        //要下载的文件的开始位置

        private int startsize;

        // 要下载的文件的结束位置

        private int endsize;

        //HttpURLConnection.setRequestProperty("Range","bytes=2097152-4194303");

        private String path;

 

        public DownLoadTask(intid, int startsize, int endsize,String path) {

            this.id = id;

            this.startsize = startsize;

            this.endsize = endsize;

            this.path = path;

        }

       

       

       

        @Override

        public void run() {

            try {

                // 每一个线程创建执行的时候 都创建一个id.txt的文件,这个文件用来记录当前线程下载的进度

                File idfile = newFile("/mnt/sdcard/"+id+".txt");

                // 判断是否记录的有下载的位置信息

                if(idfile.exists()){

                    FileInputStream fis = new FileInputStream(idfile);

                    byte[] result = StreamTools.getBytes(fis);

                    String numberstr = new String(result);

                    if(numberstr!=null&&(!"".equals(numberstr))){

                        int startposition =Integer.parseInt( numberstr); // 从文件里面获取到的位置信息

                        if(startposition>startsize){

                            int currentposition = startposition - startsize; // 当前线程已经下载的数据的大小

                            setProgreebarProgress(currentposition);

 

                            handler.sendEmptyMessage(0);

//                          handler.post(r);

                            startsize = startposition; // 重新指定下载的开始位置

                        }

                       

                    }

                }

               

                URL url = new URL(path);

                HttpURLConnection conn =(HttpURLConnection) url.openConnection();

                conn.setRequestMethod("GET");

                conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");

                conn.setConnectTimeout(5000);//得到服务器响应状态码的时间

                // 指定当前线程从服务器上的哪个位置下载文件y

                if(startsize>endsize){

                    startsize=endsize-1;

                }

                conn.setRequestProperty("Range","bytes="+startsize+"-"+endsize);

                System.out.println("线程id="+id+"开始位置"+startsize+"结束位置"+endsize);

                InputStream is =conn.getInputStream();

                File file = newFile(Environment.getExternalStorageDirectory(),getFileName(path));

                RandomAccessFilerandomfile  = new RandomAccessFile(file,"rwd");

                randomfile.seek(startsize);

                //randomfile.write(arg0,arg1, arg2);

                byte[] buffer = newbyte[1024];

                int len = 0;

                int total = 0;

                while( (len =is.read(buffer))!=-1){

                   

                    randomfile.write(buffer, 0, len);

                    // 记录当前 线程下载的数据量  和对应的位置给记录

                    total +=len;

                    setProgreebarProgress(len);

                    handler.sendEmptyMessage(0);

                    FileOutputStream idfos = new FileOutputStream(idfile);

                    idfos.write((startsize+total+"").getBytes()); // 记录当前线程下载的位置信息

                    idfos.flush();

                    idfos.close();

                    if(!flag){

                        return ;

                    }

                   

                }

                randomfile.close();

                is.close();

                System.out.println("线程"+id+"下载完毕");

//              //线程下载完毕后 擦屁股的操作

//              if(idfile.exists()){

//                  idfile.delete();

//              }

               

                synchronized (DemoActivity.this){

                    threadcount ++;

                    if(threadcount>=3){

                        System.out.println("所有的线程都执行完毕了");

                        // 擦屁股的操作

                        for(inti=1;i<=3;i++){

                            File deletefile = newFile("/mnt/sdcard/"+i+".txt");

                            System.out.println(i+"删除"+ deletefile.delete());

                        }

                        //通知主线程 bt_download.setClickable(true);

                        //bt_download.setEnabled(true);

                        Message msg = newMessage();

                        msg.what =DOWNLOAD_FINISH;

                        handler.sendMessage(msg);

                    }

                }

               

               

            } catch (Exception e) {

                e.printStackTrace();

            }

           

        }

    }

   

    private  synchronized  void setProgreebarProgress(int len){

        total += len;

        pb.setProgress(total);

        // 第二种做法 就是每次得到下载的进度后 把数据存到文件里面

    }

   

   

    public String getFileName(String path){

        int start =path.lastIndexOf("/")+1;

        returnpath.substring(start);

       

    }

}

 

只有创建View对象的线程,才可以更新view对象里的内容,

其实所有的view对象都是在主线程里面创建的 ,线程的名字叫main。

所用的与ui相关的界面都是在主线程里面创建的。

 

更新view显示基本原理:

当子线程要更新view里的内容时,就让子线程发送一个消息给主线程,主线程再根据消息的内容进行操作。

主线程里面有消息队列(message queue),可以存放一组消息。并且还有一个轮询器,定期的轮询消息队列,查看是否有消息。如果发现有消息,;轮询器会把消息取出来。

在主线程中创建消息的处理者handler对象,用于处理在消息队列中取出的消息。

1 0
原创粉丝点击