Android使用多线程实现断点下载

2019-12-10 18:32:23于丽

  这样就完成了文件的数据信息的下载..经过测试..一个13M的文件在5个线程共同作用下下载的时间差不多是12秒左右(网速稳定在300k的情况下..带宽越宽..速度就会更快)单个线程下载的时间差不多是15秒左右..这里才缩短了两秒钟的时间..但是我们不要忘记..如果文件过大的话呢?因此楼主亲测了一下一个90M的文件在5个线程同时作用下时间差不多1分20秒左右..而使用一个线程进行下载差不多2分钟左右..这里还是缩短了大量的时间..

 因此根据对比,还是使用多个线程来进行下载更加的好一些..虽然Android里的一般应用不会超过50M左右..但是游戏的话一般差不多能达到100-200M左右..因此使用多线程还是能够提高下载的进度和效率..同样我们可以通过使用线程池的方式去开启线程..最后这些线程交给线程池去管理就可以了..

 在正常的Java项目中我们书写好了下载代码..就可以移植到我们的Android应用程序当中..但是还是有一些地方需要注意..因此在这里去强调一下..

 

package com.example.mutithread;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.text.TextUtils;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

  private EditText et;
  private ProgressBar pb;
  public static int threadCount = 5;
  public static int runningThread=5;
  public int currentProgress=0; //当前进度值..
  private TextView tv;
  
  
  private Handler handler = new Handler(){
    
    @Override
    public void handleMessage(Message msg){
      switch (msg.what) {
      case 1:
        Toast.makeText(getApplicationContext(), msg.obj.toString(), 0).show();
        break;
      case 2:
        break;
      case 3:
        tv.setText("当前进度:"+(pb.getProgress()*100)/pb.getMax());
      default:
        break;
      }
    }
  };
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    et = (EditText) findViewById(R.id.et);
    pb =(ProgressBar) findViewById(R.id.pg);
    tv= (TextView) findViewById(R.id.tv);
  }

  public void downLoad(View v){
    final String path = et.getText().toString().trim();
    if(TextUtils.isEmpty(path)){
      Toast.makeText(this, "下载路径错误", Toast.LENGTH_LONG).show();
      return ;
    }
    
    new Thread(){
//      String path = "http://www.easck.com/jdk.exe";
      public void run(){
        try {
          URL url = new URL(path);
          HttpURLConnection conn = (HttpURLConnection) url.openConnection();
          conn.setRequestMethod("GET");
          conn.setConnectTimeout(5000);
          int status = conn.getResponseCode();
          
          if(status == 200){
            int length = conn.getContentLength();
            System.out.println("文件总长度"+length);
            pb.setMax(length);
            
            
            RandomAccessFile raf = new RandomAccessFile("/sdcard/setup.exe","rwd");
            raf.setLength(length);
            raf.close();
            
            
            //开启5个线程来下载当前资源..
            int blockSize = length/threadCount ;
            
            for(int threadID=1;threadID<=threadCount;threadID++){
              
              int startIndex = (threadID-1)*blockSize;
              int endIndex = threadID*blockSize -1;
              
              if(threadID == threadCount){
                endIndex = length; 
              }
              System.out.println("线程"+threadID+"下载:---"+startIndex+"--->"+endIndex);
              new Thread(new DownLoadThread(threadID, startIndex, endIndex, path)).start() ;
            }
          }
          
          
        } catch (Exception e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      };
      
    }.start();
  }
  /** 
   * 下载线程..
   * */
  public class DownLoadThread implements Runnable{

    private int ThreadID;
    private int startIndex;
    private int endIndex;
    private String path;
    
    public DownLoadThread(int ThreadID,int startIndex,int endIndex,String path){
      this.ThreadID = ThreadID;
      this.startIndex = startIndex;
      this.endIndex = endIndex;
      this.path = path;
    }
    @Override
    public void run() {
      // TODO Auto-generated method stub
      URL url;
      try {    
        //检查是否存在还未下载完成的文件...
        File tempfile = new File("/sdcard/"+ThreadID+".txt");
        if(tempfile.exists() && tempfile.length()>0){
          FileInputStream fis = new FileInputStream(tempfile);
          byte temp[] =new byte[1024];
          int leng = fis.read(temp);
          int downlength = Integer.parseInt(new String(temp,0,leng));
          
          int alreadydown = downlength -startIndex;
          
          currentProgress += alreadydown;//发生断点之后记录下载的文件长度..
          
          startIndex = downlength;
          fis.close();
        }
        
        
        url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
        conn.setConnectTimeout(5000);
        
        //获取响应码..
        int status =conn.getResponseCode();
        
        if(status == 206){
          InputStream in = conn.getInputStream();
          RandomAccessFile raf =new RandomAccessFile("/sdcard/jdk.exe", "rwd");
          
          //文件开始写入..
          raf.seek(startIndex);
          
          int len =0;
          byte[] buffer =new byte[1024];
          
          //已经下载的数据长度..
          int total = 0;
          
          
          while((len = in.read(buffer))!=-1){
            //记录当前数据下载的长度...
            RandomAccessFile file = new RandomAccessFile("/sdcard/"+ThreadID+".txt", "rwd");
            
            raf.write(buffer, 0, len);
            total += len;
            System.out.println("线程"+ThreadID+"total:"+total);
            file.write((total+startIndex+"").getBytes());
            file.close();
            
            synchronized (MainActivity.this) {
              currentProgress += len; //获取当前总进度...
               //progressBar progressDialog可以直接在子线程内部更新UI..由于源码内部进行了特殊的处理..
               pb.setProgress(currentProgress); //更改界面上的进度条进度..
               Message msg =Message.obtain(); //复用以前的消息..避免多次new...
               msg.what = 3;
               handler.sendMessage(msg);
               
            }
          }
          raf.close();
          in.close();
          System.out.println("线程:"+ThreadID+"下载完毕");
        }else{
          System.out.println("线程:"+ThreadID+"下载失败");
        }
        
      } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        Message msg = new Message();
        msg.what = 1;
        msg.obj = e;
        handler.sendMessage(msg);
      }finally{
        synchronized (MainActivity.this) {
          runningThread--;
          
          if(runningThread == 0){
            for(int i=1;i<=threadCount;i++){
              File file = new File("/sdcard/"+i+".txt");
              file.delete();
            }
            Message msg =new Message();
            msg.what = 2;
            msg.obj ="下载完毕";
            handler.sendMessage(msg);
          }
        }  
        
      }
  
    }
    
  }
  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

}