android多线程断点下载-带进度条和百分比进度显示效果

android多线程断点下载,带进度条和百分比显示,断点下载的临时数据保存到SD卡的文本文档中,建议可以保存到本地数据库中,这样可以提高存取效率,从而提高系统性能。

效果:

打开软件:

下载中:

下载完毕:

附代码如下:

package com.yy.multiDownloadOfBreakPoint;

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.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
/**
 * 多线程断点下载实例
 * @author YUANYUAN
 *
 */
public class MainActivity extends Activity {
  //下载所使用的线程数
  protected static final int threadCount = 3;
  //下载完毕的标记
  public static final int downloadOver = 1;
  //更新下载进度标记
  public static final int UPDATE_PROGRESS = 0;
  //下载资源的路径输入框
  private EditText et_path;
  //下载的进度条
  private ProgressBar pb;
  //进度显示
  private TextView tv_pb;
  //当前累计下载的数据
  int curDownCount=0;
  //当前活动的下载线程数
  protected static int activeThread;
  //加入消息处理机制
  private Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg) {
      switch (msg.what) {
      case downloadOver:
        Toast.makeText(MainActivity.this, "文件已下载完毕!", Toast.LENGTH_LONG).show();
        tv_pb.setText("下载完成");
        break;
      case UPDATE_PROGRESS:
        //更新进度显示
        tv_pb.setText("当前进度:"+(pb.getProgress()*100/pb.getMax())+"%");
        break;
      default:
        break;
      }
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    et_path=(EditText) findViewById(R.id.et_path);
    et_path.setText("http://192.168.2.114:8080/sqlite.exe");
    pb=(ProgressBar) findViewById(R.id.pb);
    tv_pb=(TextView) findViewById(R.id.tv_pb);
  }

  /**
   * 开始下载
   * @param view
   */
  public void down(View view){
    //获取下载资源的路径
    final String path=et_path.getText().toString().trim();
    //判断资源路径是否为空
    if (TextUtils.isEmpty(path)) {
      Toast.makeText(this, "请输入下载资源的路径", Toast.LENGTH_LONG).show();
      return;
    }
    //开启一个线程进行下载
    new Thread(){
      public void run() {
        try {
          //构造URL地址
          URL url=new URL(path);
          //打开连接
          HttpURLConnection conn=(HttpURLConnection) url.openConnection();
          //设置请求超时的时间
          conn.setConnectTimeout(5000);
          //设置请求方式
          conn.setRequestMethod("GET");
          //获取相应码
          int code=conn.getResponseCode();
          if (code==200) {//请求成功
            //获取请求数据的长度
            int length=conn.getContentLength();
            //设置进度条的最大值
            pb.setMax(length);
            //在客户端创建一个跟服务器文件大小相同的临时文件
            RandomAccessFile raf=new RandomAccessFile("sdcard/setup.exe", "rwd");
            //指定临时文件的长度
            raf.setLength(length);
            raf.close();
            //假设3个线程去下载资源
            //平均每一个线程要下载的文件的大小
            int blockSize=length/threadCount;
            for (int threadId = 1; threadId <= threadCount; threadId++) {
              //当前线程下载数据的开始位置
              int startIndex=blockSize*(threadId-1);
              //当前线程下载数据的结束位置
              int endIndex=blockSize*threadId-1;
              //确定最后一个线程要下载数据的最大位置
              if (threadId==threadCount) {
                endIndex=length;
              }
              //显示下载数据的区间
              System.out.println("线程【"+threadId+"】开始下载:"+startIndex+"---->"+endIndex);
              //开启下载的子线程
              new DownloadThread(path, threadId, startIndex, endIndex).start();
              //当前下载活动的线程数加1
              activeThread++;
              System.out.println("当前活动的线程数:"+activeThread);
            }

          }else{//请求失败
            System.out.println("服务器异常,下载失败!");
          }
        } catch (Exception e) {
          e.printStackTrace();
          System.out.println("服务器异常,下载失败!");
        }
      };
    }.start();

  }
  /**
   * 下载文件的子线程 每一个文件都下载对应的数据
   * @author YUANYUAN
   *
   */
  public class DownloadThread extends Thread{
    private String path;
    private int threadId;
    private int startIndex;
    private int endIndex;

    /**
     * 构造方法
     * @param path 下载文件的路径
     * @param threadId 下载文件的线程
     * @param startIndex 下载文件开始的位置
     * @param endIndex 下载文件结束的位置
     */
    public DownloadThread(String path, int threadId, int startIndex,
        int endIndex) {
      this.path = path;
      this.threadId = threadId;
      this.startIndex = startIndex;
      this.endIndex = endIndex;
    }

    @Override
    public void run() {
      //构造URL地址
      try {

        File tempFile=new File("sdcard/"+threadId+".txt");
        //检查记录是否存在,如果存在读取数据,设置真实下载开始的位置
        if (tempFile.exists()) {
          FileInputStream fis=new FileInputStream(tempFile);
          byte[] temp=new byte[1024];
          int length=fis.read(temp);
          //读取到已经下载的位置
          int downloadNewIndex=Integer.parseInt(new String(temp, 0, length));
          //计算出已经下载的数据长度
          int areadyDown=downloadNewIndex-startIndex;
          //累加已经下载的数据量
          curDownCount+=areadyDown;
          //设置进度条已经下载的数据量
          pb.setProgress(curDownCount);
          //设置重新开始下载的开始位置
          startIndex=downloadNewIndex;
          fis.close();
          //显示真实下载数据的区间
          System.out.println("线程【"+threadId+"】真实开始下载数据区间:"+startIndex+"---->"+endIndex);
        }

        URL url = new URL(path);
        HttpURLConnection conn=(HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setRequestMethod("GET");
        //设置请求属性,请求部分资源
        conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
        int code=conn.getResponseCode();
        if (code==206) {//下载部分资源,正常返回的状态码为206
          InputStream is=conn.getInputStream();//已经设置了请求的位置,所以返回的是对应的部分资源
          //构建随机访问文件
          RandomAccessFile raf=new RandomAccessFile("sdcard/setup.exe", "rwd");
          //设置 每一个线程随机写文件开始的位置
          raf.seek(startIndex);
          //开始写文件
          int len=0;
          byte[] buffer=new byte[1024];
          //该线程已经下载数据的长度
          int total=0;

          while((len=is.read(buffer))!=-1){//读取输入流
            //记录当前线程已下载数据的长度
            RandomAccessFile file=new RandomAccessFile("sdcard/"+threadId+".txt","rwd");
            raf.write(buffer,0,len);//写文件
            total+=len;//更新该线程已下载数据的总长度
            System.out.println("线程【"+threadId+"】已下载数据:"+(total+startIndex));
            //将已下载数据的位置记录写入到文件
            file.write((startIndex+total+"").getBytes());
            //累加已经下载的数据量
            curDownCount+=len;
            //更新进度条【进度条的更新可以在非UI线程直接更新,具体见底层源代码】
            pb.setProgress(curDownCount);

            //更新下载进度
            Message msg=Message.obtain();
            msg.what=UPDATE_PROGRESS;
            handler.sendMessage(msg);

            file.close();
          }
          is.close();
          raf.close();
          //提示下载完毕
          System.out.println("线程【"+threadId+"】下载完毕");
        }
      } catch (Exception e) {
        e.printStackTrace();
        System.out.println("线程【"+threadId+"】下载出现异常!!");
      }finally{
        //活动的线程数减少
        activeThread--;
        if (activeThread==0) {
          for (int i = 1; i <= threadCount; i++) {
            File tempFile=new File("sdcard/"+i+".txt");
            tempFile.delete();
          }
          System.out.println("下载完毕,已清除全部临时文件");
          //界面消息提示下载完毕
          Message msg=new Message();
          msg.what=downloadOver;
          handler.sendMessage(msg);
        }
      }

    }
  }
}

以上这篇android多线程断点下载-带进度条和百分比进度显示效果就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

时间: 2017-06-04

Android原生实现多线程断点下载实例代码

各位父老乡亲,我单汉三又回来了,今天为大家带来一个用原生的安卓写的多线程断点下载Demo. 通过本文你可以学习到: SQLite的基本使用,数据库的增删改查. Handler的消息处理与更新UI. Service(主要用于下载)的进阶与使用. 原生的json文件解析(多层嵌套). RandomAccessFile的基本使用,可以将文件分段. 基于HttpURLConnection的大文件下载. 上面内容结合,实现多线程,断点下载. Demo是在TV上运行的,图片显示的问题不要纠结了. 文件下载的

Android入门:多线程断点下载详细介绍

本案例在于实现文件的多线程断点下载,即文件在下载一部分中断后,可继续接着已有进度下载,并通过进度条显示进度.也就是说在文件开始下载的同时,自动创建每个线程的下载进度的本地文件,下载中断后,重新进入应用点击下载,程序检查有没有本地文件的存在,若存在,获取本地文件中的下载进度,继续进行下载.当下载完成后,自动删除本地文件. 一.多线程断点下载介绍 所谓的多线程断点下载就是利用多线程下载,并且可被中断,如果突然没电了,重启手机后可以继续下载,而不需要重新下载: 利用的技术有:SQLite存储各个线程的

Android实现多线程断点下载的方法

本文实例讲述了Android实现多线程断点下载的方法.分享给大家供大家参考.具体实现方法如下: package cn.itcast.download; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputSt

详解Android中的多线程断点下载

首先来看一下多线程下载的原理.多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序"拼接"起来就构 成了完整的文件了.这样就大大提高了文件的下载效率.对于文件下载来说,多线程下载是必须要考虑的环节. 多线程下载大致可分为以下几个步骤: 一.获取服务器上的目标文件的大小 显然这一步是需要先访问一下网络,只需要获取到目标文件的总大小即可.目的是为了计算每个线程应该分配的下载任务. 二. 在本地创建一个跟原始

详解Android中图片的三级缓存及实例

详解Android中图片的三级缓存及实例 为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响 特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知 所以提出三级缓存策略,通过网络.本地.内存三级缓存图片,来减少不必要的网络交互,避免浪费流量

详解Android中Handler的内部实现原理

本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文<详解Android中Handler的使用方法>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功

详解Android 中AsyncTask 的使用

详解Android 中AsyncTask 的使用 1.首先我们来看看AsyncTask 的介绍:   Handler 和 AsyncTask 都是android 中用来实现异步任务处理的方式:其中: Handler 实例向 UI 线程发送消息,完成界面更新, 优点:对整个过程控制的比较精细:         缺点:代码相对臃肿,多个任务同时执行时,不易对线程进行精确的控制: AsyncTask :比Handler 更轻量级一些,适用于简单的异步处理: 优点:简单 | 快捷 | 过程可控:    

详解Android中Intent对象与Intent Filter过滤匹配过程

如果对Intent不是特别了解,可以参见博文<详解Android中Intent的使用方法>,该文对本文要使用的action.category以及data都进行了详细介绍.如果想了解在开发中常见Intent的使用,可以参见<Android中Intent习惯用法>. 本文内容有点长,希望大家可以耐心读完. 本文在描述组件在manifest中注册的Intent Filter过滤器时,统一用intent-filter表示. 一.概述 我们知道,Intent是分两种的:显式Intent和隐式

详解 Android中Libgdx使用ShapeRenderer自定义Actor解决无法接收到Touch事件的问题

详解 Android中Libgdx使用ShapeRenderer自定义Actor解决无法接收到Touch事件的问题 今天在项目中实现了一个效果,主要是画一个圆.为了后续使用方便,将这个圆封装在一个自定义Actor(CircleActot)中,后续想显示一个圆的时候,只要创建一个CircleActor中即可. 部分代码如下所示: package com.ef.smallstar.unitmap.widget; import android.content.res.Resources; import

详解Android中的Service

Service简介: Service是被设计用来在后台执行一些需要长时间运行的操作. Android由于允许Service在后台运行,甚至在结束Activity后,因此相对来说,Service相比Activity拥有更高的优先级. 创建Service: 要创建一个最基本的Service,需要完成以下工作:1)创建一个Java类,并让其继承Service 2)重写onCreate()和onBind()方法 其中,onCreate()方法是当该Service被创建时执行的方法,onBind()是该S

详解Android中获取软键盘状态和软键盘高度

详解Android中获取软键盘状态和软键盘高度 应用场景 在Android应用中有时会需要获取软键盘的状态(即软键盘是显示还是隐藏)和软键盘的高度.这里列举了一些可能的应用场景. 场景一 当软键盘显示时,按下返回键应当是收起软键盘,而不是回退到上一个界面,但部分机型在返回键处理上有bug,按下返回键后,虽然软键盘会自动收起,但不会消费返回事件,导致Activity还会收到这次返回事件,执行回退操作,这时就需要判断,如果软键盘刚刚由显示变为隐藏状态,就不执行回退操作. 场景二 当软键盘弹出后,会将

详解Android中fragment和viewpager的那点事儿

在之前的博文<Android 中使用 ViewPager实现屏幕页面切换和页面轮播效果>和<详解Android中Fragment的两种创建方式>以及<Android中fragment与activity之间的交互(两种实现方式)>中我们介绍了ViewPager以及Fragment各自的使用场景以及不同的实现方式. 那如果将他们两结合起来,会不会擦出点火花呢,答案是肯定的.之前在介绍ViewPager时,我们实现了多个ImageView的切换,并配合更新导航原点的状态.那我

详解Android中PopupWindow在7.0后适配的解决

本文介绍了详解Android中PopupWindow在7.0后适配的解决,分享给大家,具体如下: 这里主要记录一次踩坑的经历. 需求:如上图左侧效果,想在按钮的下方弹一个PopupWindow.嗯,很简单一个效果,然当适配7.0后发现这个PopupWindow显示异常,然后网上找到了下面这种方案. 7.0适配方案(但7.1又复现了) // 将popupWindow显示在anchor下方 public void showAsDropDown(PopupWindow popupWindow, Vie