iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android使用多线程实现断点下载
  • 835
分享到

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

多线程断点线程Android 2022-06-06 08:06:02 835人浏览 独家记忆
摘要

多线程下载是加快下载速度的一种方式,通过开启多个线程去执行一个任务..可以使任务的执行速度变快..多线程的任务下载时常都会使用得到..比如说我们手机内部应用宝的下载机制..一定

多线程下载是加快下载速度的一种方式,通过开启多个线程去执行一个任务..可以使任务的执行速度变快..多线程的任务下载时常都会使用得到..比如说我们手机内部应用宝的下载机制..一定是通过使用了多线程创建的下载器..并且这个下载器可以实现断点下载..在任务被强行终止之后..下次可以通过触发按钮来完成断点下载...那么如何实现断点下载这就是一个问题了..

  首先我们需要明确一点就是多线程下载器通过使用多个线程对同一个任务进行下载..但是这个多线程并不是线程的数目越多,下载的速度就越快..当线程增加的很多的时候,单个线程执行效率也会变慢..因此线程的数目需要有一个限度..经过楼主亲自测试..多线程下载同一个任务时,线程的数目5-8个算是比较高效的..当线程的数量超过10个之后,那么多线程的效率反而就变慢了(前提:网速大体相同的时候..)

  那么在实现多线程下载同一个任务的时候我们需要明白其中的道理..下面先看一张附加图..

  这个图其实就很简单的说明了其中的原理..我们将一个任务分成多个部分..然后开启多个线程去下载对应的部分就可以了..那么首先要解决的问题就是如何使各自的线程去下载各自对应的任务,不能越界..那么我们来看看具体的实现过程..首先我们使用普通Java代码去实现..最后将Java代码移植到Android就可以了..


public class Download {
  public static int threadCount = 5;  //线程开启的数量..
  public static void main(String[] args) {
    // TODO Auto-generated method stub
    String path = "Http://192.168.199.172:8080/jdk.exe";
    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);
        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("线程下载位置:"+startIndex+"---"+endIndex);
        }
      }
    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

  这样只是实现了通过连接服务器来获取文件的长度..然后去设置每一个线程下载的开始位置和结束位置..这里只是完成了这些步骤..有了下载的开始位置和结束位置..我们就需要开启线程来完成下载了...因此我们需要自己定义下载的过程...

  首先我们需要明确思路:既然是断点下载..那么如果一旦发生了断点情况..我们在下一次进行下载的时候需要从原来断掉的位置进行下载..已经下载过的位置我们就不需要进行下载了..因此我们需要记载每一次的下载记录..那么有了记录之后..一旦文件下载完成之后..这些记录就需要被清除掉...因此明确了这两个地方的思路就很容易书写了..


//下载线程的定义..  
public static 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
      try {
        //判断上一次是否下载完毕..如果没有下载完毕需要继续进行下载..这个文件记录了上一次的下载位置..
        File tempfile =new File(threadID+".txt");
        if(tempfile.exists() && tempfile.length()>0){
          FileInputStream fis = new FileInputStream(tempfile);
          byte buffer[] = new byte[1024];
          int leng = fis.read(buffer);
          int downlength = Integer.parseInt(new String(buffer,0,leng));//从上次下载后的位置开始下载..重新拟定开始下载的位置..
          startIndex = downlength;
          fis.close();
        }
        URL url = new URL(path);
        HttpURLConnection conn =(HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setConnectTimeout(5000);
        conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
        int status = conn.getResponseCode();
        //206也表示服务器响应成功..
        if(status == 206){
          //获取服务器返回的I/O流..然后将数据写入文件当中..
          InputStream in = conn.getInputStream();
          //文件写入开始..用来保存当前需要下载的文件..
          RandoMaccessFile raf = new RandomAccessFile("jdk.exe", "rwd");
          raf.seek(startIndex);
          int len = 0;
          byte buf[] =new byte[1024];
          //记录已经下载的长度..
          int total = 0;
          while((len = in.read(buf))!=-1){
            //用于记录当前下载的信息..
            RandomAccessFile file =new RandomAccessFile(threadID+".txt", "rwd");
            total += len;
            file.write((total+startIndex+"").getBytes());
            file.close();
            //将数据写入文件当中..
            raf.write(buf, 0, len);
          }
          in.close();
          raf.close();
        }  
      } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }finally{
        //如果所有的线程全部下载完毕后..也就是任务完成..清除掉所有原来的记录文件..
        runningThread -- ;
        if(runningThread==0){
          for(int i=1;i<threadCount;i++){
            File file = new File(i+".txt");
            file.delete();
          }
        }
      }
    }
  }

  这样就完成了文件的数据信息的下载..经过测试..一个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://192.168.199.172:8080/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;
  }
}

  源代码如上..优化的事情我就不做了..为了方便直接就贴上了..这里定义了一个ProgressBar进度条..一个TextView来同步进度条的下载进度..在Android中我们自然不能够在主线程中去调用耗时间的操作..因此这些耗时的操作我们就通过开启子线程的方式去使用..但是子线程是不能够更新UI界面的..因此我们需要使用到Handler Message机制来完成主界面UI更新的操作.

  但是上面的代码当中我们会发现一个问题..在子线程内部居然更新了ProgressBar操作..其实ProgressBar和ProgressDialog是两个特例..我们可以在子线程内部去更新他们的属性..我们来看一下源代码的实现过程..


 private synchronized void refreshProgress(int id, int progress, boolean fromUser) {
    if (mUiThreadId == Thread.currentThread().getId()) { //如果当前运行的线程和主线程相同..那么更新进度条..
      doRefreshProgress(id, progress, fromUser, true);
    } else { //如果不满足上面说的情况..
      if (mRefreshProgressRunnable == null) {
        mRefreshProgressRunnable = new RefreshProgressRunnable();//那么新建立一个线程..然后执行下面的过程..
      }
      final RefreshData rd = RefreshData.obtain(id, progress, fromUser); //获取消息队列中的消息..
      mRefreshData.add(rd);
      if (mAttached && !mRefreshIsPosted) {
        post(mRefreshProgressRunnable); //主要是这个地方..调用了post方法..将当前运行的线程发送到消息队列当中..那么这个线程就可以在UI中运行了..因此这一步是决定因素..
        mRefreshIsPosted = true;
      }
    }
  }

  正是由于源码内部调用了post方法..将当前的线程放入到消息队列当中..那么UI中的Looper线程就会对这个线程进行处理,那么就表示这个线程是可以被执行在UI当中的..也正是这个因素导致了我们可以在子线程内部更新ProgressBar..但是我们可以看到如果我们想要去更新TextView的时候..我们就需要调用Handler Message机制来完成UI界面的更新了..因此这一块需要我们去注意。
  移植之后代码其实并没有发生太大的变化,这样就可以完成一个在Android中的多线程断点下载器了。

您可能感兴趣的文章:Android 断点下载和自动安装的示例代码android多线程断点下载-带进度条和百分比进度显示效果Android HttpURLConnection断点下载(单线程)Android原生实现多线程断点下载实例代码详解Android中的多线程断点下载Android入门:多线程断点下载详细介绍Android实现断点下载的方法Android实现多线程断点下载的方法Android实现断点多线程下载


--结束END--

本文标题: Android使用多线程实现断点下载

本文链接: https://www.lsjlt.com/news/25382.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android实现多线程断点下载
    目录QDownload1、如何使用1.1、导入依赖1.2、初始化下载组件1.3、核心控制器DownloadManager1.4、监听下载进度1.5、下载相关的操作1.6、应用市场ap...
    99+
    2024-04-02
  • Android原生实现多线程断点下载实例代码
    各位父老乡亲,我单汉三又回来了,今天为大家带来一个用原生的安卓写的多线程断点下载Demo。通过本文你可以学习到: SQLite的基本使用,数据库的增删改查。 Handler的消息处理与更新UI。 Service(主要用于下载)的进阶与...
    99+
    2023-05-31
    android 断点 下载
  • Java实现多线程下载和断点续传
    java的多线程下载能够明显提升下载的速度,平时我们用的迅雷软件之所以能够下载那么快,就是使用了多线程;当用户在下载的过程中,有断电或断网的可能,当用户再次点击下载时,应该让用户接着...
    99+
    2024-04-02
  • Android实现多线程断点续传
    本文实例为大家分享了Android实现多线程断点续传的具体代码,供大家参考,具体内容如下 多线程下载涉及到的知识点: 1、Service的使用:我们在Service中去下载文件;2、...
    99+
    2024-04-02
  • 怎么在Android中实现一个多线程断点续传下载功能
    本篇文章给大家分享的是有关怎么在Android中实现一个多线程断点续传下载功能,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1、布局实现具体布局内容如下:<LinearL...
    99+
    2023-05-30
    android
  • Android中怎么利用FTP实现多线程断点续传下载上传功能
    Android中怎么利用FTP实现多线程断点续传下载上传功能,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。FTP下载原理FTP单线程断点续传FTP和传统的HTT...
    99+
    2023-05-30
    android
  • 怎么在Android中利用多线程实现一个断点续传下载功能
    怎么在Android中利用多线程实现一个断点续传下载功能?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。原理其实断点续传的原理很简单,从字面上理解,所谓断点续传就是从停止的地方重...
    99+
    2023-05-31
    android 多线程 roi
  • 怎么在Android应用中实现一个网络多线程断点续传下载功能
    怎么在Android应用中实现一个网络多线程断点续传下载功能?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。原理多线程下载的原理就是将要下载的文件分成若干份,其中...
    99+
    2023-05-31
    android 断点续传 多线程
  • android原生实现多线程断点续传功能
    本文实例为大家分享了android实现多线程断点续传功能的具体代码,供大家参考,具体内容如下 需求描述: 输入一个下载地址,和要启动的线程数量,点击下载 利用多线程将文件下载到手机端...
    99+
    2024-04-02
  • android怎么实现多线程断点续传功能
    这篇文章主要介绍了android怎么实现多线程断点续传功能,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。具体内容如下布局:<xml version="...
    99+
    2023-05-30
    android 多线程
  • android中的多线程下载怎么利用AsyncTask实现
    android中的多线程下载怎么利用AsyncTask实现?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。01 效果图02 核心类 - DownloadTask.classp...
    99+
    2023-05-31
    android asynctask 多线程
  • iOS使用NSURLConnection实现断点续传下载
    本文实例为大家分享了iOS使用NSURLConnection实现断点续传下载的具体代码,供大家参考,具体内容如下 一.断点续传的原理 断点续传的原理:每次在想服务器请求下载数据的同时...
    99+
    2024-04-02
  • 使用 go 实现多线程下载器的方法
    目录1.多线程下载原理2.构造一个下载器2.1 为下载器提供初始化方法3.实现下载综合调度逻辑3.1 下载文件分段3.2 子线程下载函数4. 保存下载文件函数5.完整代码本篇文章我们...
    99+
    2024-04-02
  • Android入门之使用OKHttp多线程下载文件
    目录简介课程目标OkHttp的同步调用例子OkHttp的异步调用例子多线程并行下载文件需要解决的几个核心问题全代码前端后端简介 OkHttp是一个神器。OkHttp分为异步、同步两种...
    99+
    2023-01-03
    Android OKHttp下载文件 Android 下载文件 Android OKHttp
  • 如何使用vbs实现支持断点下载
    这篇文章主要介绍了如何使用vbs实现支持断点下载,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。并附上VBS代码的解析,不懂的朋友可以配合微软的SCRIPT56.CHM文档自学...
    99+
    2023-06-08
  • Python 实现多线程文件下载
    #!/root/.pyenv/shims/python # -*- coding: UTF-8 -*- import sys import requests import threading import datetime #传入的命令行参...
    99+
    2023-01-31
    多线程 文件 Python
  • golang实现多协程下载文件(支持断点续传)
    引言 写这篇文章主要是周末休息太无聊,看了看别人代码,发现基本上要么是多协程下载文件要么就只有单协程的断点续传,所以就试了试有进度条的多协程下载文件(支持断点续传) packa...
    99+
    2024-04-02
  • 怎么在Android中实现一个多线程下载功能
    怎么在Android中实现一个多线程下载功能?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。布局      &nb...
    99+
    2023-05-30
    android 多线程
  • iOS怎么使用NSURLConnection实现断点续传下载
    本篇内容介绍了“iOS怎么使用NSURLConnection实现断点续传下载”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一.断点续传的原理...
    99+
    2023-06-30
  • Android应用中的断点下载功能怎么利用HttpURLConnection实现
    Android应用中的断点下载功能怎么利用HttpURLConnection实现?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。HttpCilent 跟 HttpURLConne...
    99+
    2023-05-31
    android httpurlconnection roi
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作