Android实现多线程下载文件的方法

 更新时间:2015年10月16日 11:59:40   作者:一叶飘舟  
这篇文章主要介绍了Android实现多线程下载文件的方法,以实例形式较为详细的分析了Android多线程文件传输及合并等操作的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

本文实例讲述了Android实现多线程下载文件的方法。分享给大家供大家参考。具体如下:

多线程下载大概思路就是通过Range 属性实现文件分段,然后用RandomAccessFile 来读写文件,最终合并为一个文件

首先看下效果图:

创建工程 ThreadDemo

首先布局文件 threaddemo.xml

<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:orientation="vertical" 
  android:layout_width="fill_parent" 
  android:layout_height="fill_parent" 
  > 
<TextView  
  android:layout_width="fill_parent"  
  android:layout_height="wrap_content"  
  android:text="下载地址" 
  /> 
<TextView 
  android:id="@+id/downloadurl" 
  android:layout_width="fill_parent"  
  android:layout_height="wrap_content"  
  android:lines="5" 
  /> 
<TextView  
  android:layout_width="fill_parent"  
  android:layout_height="wrap_content"  
  android:text="线程数" 
  /> 
<EditText 
  android:id="@+id/downloadnum" 
  android:layout_width="fill_parent"  
  android:layout_height="wrap_content"  
  /> 
<ProgressBar 
  android:id="@+id/downloadProgressBar" 
  android:layout_width="fill_parent"  
  style="?android:attr/progressBarStyleHorizontal" 
  android:layout_height="wrap_content"  
  /> 
<TextView 
  android:id="@+id/downloadinfo" 
  android:layout_width="fill_parent"  
  android:layout_height="wrap_content"  
  android:text="下载进度 0" 
  /> 
<Button 
  android:id="@+id/downloadbutton" 
  android:layout_width="wrap_content"  
  android:layout_height="wrap_content"  
  android:text="开始下载" 
  /> 
</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:orientation="vertical" android:layout_width="fill_parent"  android:layout_height="fill_parent"  >
<TextView   android:layout_width="fill_parent"   android:layout_height="wrap_content" android:text="下载地址"  />
<TextViewandroid:id="@+id/downloadurl"android:layout_width="fill_parent" android:layout_height="wrap_content" android:lines="5"/>
<TextView   android:layout_width="fill_parent"   android:layout_height="wrap_content" android:text="线程数"  />
<EditTextandroid:id="@+id/downloadnum"android:layout_width="fill_parent" android:layout_height="wrap_content" />
<ProgressBarandroid:id="@+id/downloadProgressBar"android:layout_width="fill_parent" style="?android:attr/progressBarStyleHorizontal"  android:layout_height="wrap_content" />
<TextViewandroid:id="@+id/downloadinfo"android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="下载进度 0"/>
<Buttonandroid:id="@+id/downloadbutton"android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="开始下载"/>
</LinearLayout> 

主界面 Acitivity

public class ThreadDownloadDemo extends Activity { 
  private TextView downloadurl; 
  private EditText downloadnum; 
  private Button downloadbutton; 
  private ProgressBar downloadProgressBar; 
  private TextView downloadinfo; 
  private int downloadedSize = 0; 
  private int fileSize = 0; 
  private long downloadtime; 
  @Override 
  public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.threaddemo); 
    downloadurl = (TextView) findViewById(R.id.downloadurl); 
    downloadurl.setText("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3"); 
    downloadnum = (EditText) findViewById(R.id.downloadnum); 
    downloadinfo = (TextView) findViewById(R.id.downloadinfo); 
    downloadbutton = (Button) findViewById(R.id.downloadbutton); 
    downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar); 
    downloadProgressBar.setVisibility(View.VISIBLE); 
    downloadProgressBar.setMax(100); 
    downloadProgressBar.setProgress(0); 
    downloadbutton.setOnClickListener(new OnClickListener() { 
      public void onClick(View v) { 
        download(); 
        downloadtime = SystemClock.currentThreadTimeMillis(); 
      } 
    }); 
  } 
  private void download() { 
    // 获取SD卡目录  
    String dowloadDir = Environment.getExternalStorageDirectory() 
        + "/threaddemodownload/"; 
    File file = new File(dowloadDir); 
    //创建下载目录  
    if (!file.exists()) { 
      file.mkdirs(); 
    } 
    //读取下载线程数,如果为空,则单线程下载  
    int downloadTN = Integer.valueOf("".equals(downloadnum.getText() 
        .toString()) ? "1" : downloadnum.getText().toString()); 
    String fileName = "hetang.mp3"; 
    //开始下载前把下载按钮设置为不可用  
    downloadbutton.setClickable(false); 
    //进度条设为0  
    downloadProgressBar.setProgress(0); 
    //启动文件下载线程  
    new downloadTask("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3", Integer 
        .valueOf(downloadTN), dowloadDir + fileName).start(); 
  } 
  Handler handler = new Handler() { 
    @Override 
    public void handleMessage(Message msg) { 
      //当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息  
      int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue(); 
      if (progress == 100) { 
        downloadbutton.setClickable(true); 
        downloadinfo.setText("下载完成!"); 
        Dialog mdialog = new AlertDialog.Builder(ThreadDownloadDemo.this) 
          .setTitle("提示信息") 
          .setMessage("下载完成,总用时为:"+(SystemClock.currentThreadTimeMillis()-downloadtime)+"毫秒") 
          .setNegativeButton("确定", new DialogInterface.OnClickListener(){ 
            @Override 
            public void onClick(DialogInterface dialog, int which) { 
              dialog.dismiss(); 
            } 
          }) 
          .create(); 
        mdialog.show(); 
      } else { 
        downloadinfo.setText("当前进度:" + progress + "%"); 
      } 
      downloadProgressBar.setProgress(progress); 
    } 
  }; 
  public class downloadTask extends Thread { 
    private int blockSize, downloadSizeMore; 
    private int threadNum = 5; 
    String urlStr, threadNo, fileName; 
    public downloadTask(String urlStr, int threadNum, String fileName) { 
      this.urlStr = urlStr; 
      this.threadNum = threadNum; 
      this.fileName = fileName; 
    } 
    @Override 
    public void run() { 
      FileDownloadThread[] fds = new FileDownloadThread[threadNum]; 
      try { 
        URL url = new URL(urlStr); 
        URLConnection conn = url.openConnection(); 
        //防止返回-1  
        InputStream in = conn.getInputStream(); 
        //获取下载文件的总大小  
        fileSize = conn.getContentLength(); 
        Log.i("bb", "======================fileSize:"+fileSize); 
        //计算每个线程要下载的数据量  
        blockSize = fileSize / threadNum; 
        // 解决整除后百分比计算误差  
        downloadSizeMore = (fileSize % threadNum); 
        File file = new File(fileName); 
        for (int i = 0; i < threadNum; i++) { 
          Log.i("bb", "======================i:"+i); 
          //启动线程,分别下载自己需要下载的部分  
          FileDownloadThread fdt = new FileDownloadThread(url, file, i * blockSize, (i + 1) * blockSize - 1); 
          fdt.setName("Thread" + i); 
          fdt.start(); 
          fds[i] = fdt; 
        } 
        boolean finished = false; 
        while (!finished) { 
          // 先把整除的余数搞定  
          downloadedSize = downloadSizeMore; 
          finished = true; 
          for (int i = 0; i < fds.length; i++) { 
            downloadedSize += fds[i].getDownloadSize(); 
            if (!fds[i].isFinished()) { 
              finished = false; 
            } 
          } 
          handler.sendEmptyMessage(0); 
          //线程暂停一秒  
          sleep(1000); 
        } 
      } catch (Exception e) { 
        e.printStackTrace(); 
      } 
    } 
  } 
} 
public class ThreadDownloadDemo extends Activity {
private TextView downloadurl;
private EditText downloadnum;
private Button downloadbutton;
private ProgressBar downloadProgressBar;
private TextView downloadinfo;
private int downloadedSize = 0;
private int fileSize = 0;
private long downloadtime;
@Overridepublic void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.threaddemo);
downloadurl = (TextView) findViewById(R.id.downloadurl);
downloadurl.setText("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3");
downloadnum = (EditText) findViewById(R.id.downloadnum);
downloadinfo = (TextView) findViewById(R.id.downloadinfo);
downloadbutton = (Button) findViewById(R.id.downloadbutton);
downloadProgressBar = (ProgressBar) findViewById(R.id.downloadProgressBar);
downloadProgressBar.setVisibility(View.VISIBLE);
downloadProgressBar.setMax(100);
downloadProgressBar.setProgress(0);
downloadbutton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {download();
downloadtime = SystemClock.currentThreadTimeMillis();
}
}
);
}
private void download() {// 获取SD卡目录
String dowloadDir = Environment.getExternalStorageDirectory()+ "/threaddemodownload/";
File file = new File(dowloadDir);
//创建下载目录
if (!file.exists()) {file.mkdirs();
}//读取下载线程数,如果为空,则单线程下载
int downloadTN = Integer.valueOf("".equals(downloadnum.getText().toString()) ? "1" : downloadnum.getText().toString());
String fileName = "hetang.mp3";
//开始下载前把下载按钮设置为不可用
downloadbutton.setClickable(false);
//进度条设为0
downloadProgressBar.setProgress(0);
//启动文件下载线程
new downloadTask("http://file16.top100.cn/201105110911/AA5CC27CBE34DEB50A194581D1300881/Special_323149/%E8%8D%B7%E5%A1%98%E6%9C%88%E8%89%B2.mp3", Integer.valueOf(downloadTN), dowloadDir + fileName).start();
}
Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {
//当收到更新视图消息时,计算已完成下载百分比,同时更新进度条信息
int progress = (Double.valueOf((downloadedSize * 1.0 / fileSize * 100))).intValue();
if (progress == 100) {downloadbutton.setClickable(true);
downloadinfo.setText("下载完成!");
Dialog mdialog = new AlertDialog.Builder(ThreadDownloadDemo.this).setTitle("提示信息").setMessage("下载完成,总用时为:"+(SystemClock.currentThreadTimeMillis()-downloadtime)+"毫秒").setNegativeButton("确定", new DialogInterface.OnClickListener(){@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();
}}).create();
mdialog.show();
} else {
downloadinfo.setText("当前进度:" + progress + "%");
}
downloadProgressBar.setProgress(progress);
}
}; 
public class downloadTask extends Thread {private int blockSize, downloadSizeMore;
private int threadNum = 5;
String urlStr, threadNo, fileName;
public downloadTask(String urlStr, int threadNum, String fileName) {
this.urlStr = urlStr;
this.threadNum = threadNum;
this.fileName = fileName;
}
@Overridepublic void run() {
FileDownloadThread[] fds = new FileDownloadThread[threadNum];
try {URL url = new URL(urlStr);
URLConnection conn = url.openConnection();
//防止返回-1
InputStream in = conn.getInputStream();
//获取下载文件的总大小
fileSize = conn.getContentLength();
Log.i("bb", "======================fileSize:"+fileSize);
//计算每个线程要下载的数据量
blockSize = fileSize / threadNum;
// 解决整除后百分比计算误差
downloadSizeMore = (fileSize % threadNum);
File file = new File(fileName);
for (int i = 0; i < threadNum; i++) {
Log.i("bb", "======================i:"+i);
//启动线程,分别下载自己需要下载的部分
FileDownloadThread fdt = new FileDownloadThread(url, file, i * blockSize, (i + 1) * blockSize - 1);
fdt.setName("Thread" + i);
fdt.start();
fds[i] = fdt;
}
boolean finished = false;
while (!finished) {
// 先把整除的余数搞定
downloadedSize = downloadSizeMore;
finished = true;
for (int i = 0; i < fds.length; i++) {
downloadedSize += fds[i].getDownloadSize();
if (!fds[i].isFinished()) {
finished = false;
}
}
handler.sendEmptyMessage(0);
//线程暂停一秒sleep(1000);
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
} 

这里启动线程将文件分割为几个部分,每一个部分再启动一个线程去下载数据
下载文件的线程

public class FileDownloadThread extends Thread{ 
  private static final int BUFFER_SIZE=1024; 
  private URL url; 
  private File file; 
  private int startPosition; 
  private int endPosition; 
  private int curPosition; 
  //标识当前线程是否下载完成  
  private boolean finished=false; 
  private int downloadSize=0; 
  public FileDownloadThread(URL url,File file,int startPosition,int endPosition){ 
    this.url=url; 
    this.file=file; 
    this.startPosition=startPosition; 
    this.curPosition=startPosition; 
    this.endPosition=endPosition; 
  } 
  @Override 
  public void run() { 
    BufferedInputStream bis = null; 
    RandomAccessFile fos = null;                         
    byte[] buf = new byte[BUFFER_SIZE]; 
    URLConnection con = null; 
    try { 
      con = url.openConnection(); 
      con.setAllowUserInteraction(true); 
      //设置当前线程下载的起止点  
      con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition); 
      Log.i("bb", Thread.currentThread().getName()+" bytes=" + startPosition + "-" + endPosition); 
      //使用java中的RandomAccessFile 对文件进行随机读写操作  
      fos = new RandomAccessFile(file, "rw"); 
      //设置写文件的起始位置  
      fos.seek(startPosition); 
      bis = new BufferedInputStream(con.getInputStream());  
      //开始循环以流的形式读写文件  
      while (curPosition < endPosition) { 
        int len = bis.read(buf, 0, BUFFER_SIZE);         
        if (len == -1) { 
          break; 
        } 
        fos.write(buf, 0, len); 
        curPosition = curPosition + len; 
        if (curPosition > endPosition) { 
          downloadSize+=len - (curPosition - endPosition) + 1; 
        } else { 
          downloadSize+=len; 
        } 
      } 
      //下载完成设为true  
      this.finished = true; 
      bis.close(); 
      fos.close(); 
    } catch (IOException e) { 
      e.printStackTrace(); 
    } 
  } 
  public boolean isFinished(){ 
    return finished; 
  } 
  public int getDownloadSize() { 
    return downloadSize; 
  } 
} 
public class FileDownloadThread extends Thread{
private static final int BUFFER_SIZE=1024;
private URL url;
private File file;
private int startPosition;
private int endPosition;
private int curPosition;//标识当前线程是否下载完成
private boolean finished=false;
private int downloadSize=0;
public FileDownloadThread(URL url,File file,int startPosition,int endPosition){
this.url=url;
this.file=file;
this.startPosition=startPosition;
this.curPosition=startPosition;
this.endPosition=endPosition;
}
@Overridepublic void run() {
BufferedInputStream bis = null;
RandomAccessFile fos = null;
byte[] buf = new byte[BUFFER_SIZE];
URLConnection con = null;
try {
con = url.openConnection();
con.setAllowUserInteraction(true);
//设置当前线程下载的起止点
con.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);
Log.i("bb", Thread.currentThread().getName()+" bytes=" + startPosition + "-" + endPosition);
//使用java中的RandomAccessFile 对文件进行随机读写操作
fos = new RandomAccessFile(file, "rw");
//设置写文件的起始位置
fos.seek(startPosition);
bis = new BufferedInputStream(con.getInputStream());
//开始循环以流的形式读写文件
while (curPosition < endPosition) {
int len = bis.read(buf, 0, BUFFER_SIZE);
if (len == -1) {
break;
}
fos.write(buf, 0, len);
curPosition = curPosition + len;
if (curPosition > endPosition) {
downloadSize+=len - (curPosition - endPosition) + 1;
} else {
downloadSize+=len;
}
}
//下载完成设为true
this.finished = true;
bis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean isFinished(){return finished;}
public int getDownloadSize() {return downloadSize;}
}

这里通过RandomAccessFile 的seek方法定位到相应的位置 并实时记录下载量
当然这里需要联网和访问SD卡 所以要加上相应的权限

<uses-permission android:name="android.permission.INTERNET" /> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> 
<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

这样就OK了 下面可以看看断点续传的问题了。有待测试~~

希望本文所述对大家的Android程序设计有所帮助。

相关文章

  • Android判断app是否在后台运行

    Android判断app是否在后台运行

    这篇文章主要为大家介绍了Android判断app是否在后台运行的实现流程及代码实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-11-11
  • Android利用滑动菜单框架实现滑动菜单效果

    Android利用滑动菜单框架实现滑动菜单效果

    这篇文章主要介绍了Android实现滑动菜单特效之滑动菜单框架完全解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • Android开发学习笔记 Gallery和GridView浅析

    Android开发学习笔记 Gallery和GridView浅析

    这篇文章主要介绍了Android开发学习笔记 Gallery和GridView浅析,需要的朋友可以参考下
    2014-11-11
  • Android列表动图展示的实现策略

    Android列表动图展示的实现策略

    这篇文章主要给大家介绍了关于Android列表动图展示的实现策略的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • android 应用内部悬浮可拖动按钮简单实现代码

    android 应用内部悬浮可拖动按钮简单实现代码

    本篇文章主要介绍了android 应用内部悬浮可拖动按钮简单实现代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • Android Studio实现带三角函数对数运算功能的高级计算器

    Android Studio实现带三角函数对数运算功能的高级计算器

    这篇文章主要为大家详细介绍了Android Studio实现带三角函数对数运算功能的高级计算器,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Android中音视频合成的几种方案详析

    Android中音视频合成的几种方案详析

    随着音视频领域的火热,在很多领域(教育,游戏,娱乐,体育,跑步,餐饮,音乐等)尝试做音视频功能,下面这篇文章主要给大家介绍了关于Android中音视频合成的几种方案的相关资料,需要的朋友可以参考借鉴,下面随着小编来一起看看吧。
    2017-12-12
  • Android 使用URLConnection下载音频文件的方法

    Android 使用URLConnection下载音频文件的方法

    有时候我们会需要下载音频文件。这里提供一种思路,将在线音频文件通过流写到本地文件中。需要的朋友可以参考下
    2019-09-09
  • Android编程使用AlarmManager设置闹钟的方法

    Android编程使用AlarmManager设置闹钟的方法

    这篇文章主要介绍了Android编程使用AlarmManager设置闹钟的方法,结合具体实例分析了Android基于AlarmManager实现闹钟功能的设置、取消、显示等相关操作技巧,需要的朋友可以参考下
    2017-03-03
  • Android Studio中使用jni进行opencv开发的环境配置方法

    Android Studio中使用jni进行opencv开发的环境配置方法

    今天小编就为大家分享一篇Android Studio中使用jni进行opencv开发的环境配置方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08

最新评论