教你如何在 Android 使用多线程下载文件
教你如何在 Android 使用多线程下载文件
===============================================
前言
在 Android 日常开发中,我们会经常遇到下载文件需求,这里我们也可以用系统自带的 api `DownloadManager` 来解决这个问题,当然我们也可以自己来写。在这里我将教大家如何在 Android 使用多线程下载文件。
实现原理
- 获取目标文件的文件大小
- 根据线程的个数以及文件大小来分配每个线程下载文件的大小
如:文件大小:9M 线程个数:3,那么每条线程下载的大小为 3M。
在这里给出计算公式:blockSize=totalSize%countThread==0?totalSize/countThread:totalSize/countThread+1 ----blockSize 为每个线程下载的大小 totalSize 文件大小 countThread 线程个数
3.开启线程下载(这里要处理比较多的事)
具体实现
1.获取文件的大小
这一步比较简单我直接给出代码:
URL url = null;
HttpURLConnection http = null;
try {
url = new URL(this.apk_url);
http = (HttpURLConnection) url
.openConnection();
http.setConnectTimeout(5 * 1000);
http.setReadTimeout(5 * 1000);
http.setRequestMethod("GET");
if (http.getResponseCode() == 200) {
this.filesize = http.getContentLength();//文件大小
} else {
this.filesize = -1;
}
} catch (Exception e) {
e.printStackTrace();
this.filesize = -1;
} finally {
http.disconnect();
}
2.分配线程
既然要对各个线程分配对应的下载大小,我们就有必要知道各个线程对应的信息,那么我么先来定义 bean 类来表示这些信息
package com.h.kidbot.download;
public class DownLoadInfo {
private int threadid;//线程id
private long startpos;//下载的起始位置
private long endpos;//下载的结束位置
private long block;//每条下载的大小
private long downpos;//该条线程已经下载的大小
private String downloadurl;//下载地址
public int getThreadid() {
return threadid;
}
public void setThreadid(int threadid) {
this.threadid = threadid;
}
public long getStartpos() {
return startpos;
}
public void setStartpos(long startpos) {
this.startpos = startpos;
}
public long getEndpos() {
return endpos;
}
public void setEndpos(long endpos) {
this.endpos = endpos;
}
public long getBlock() {
return block;
}
public void setBlock(long block) {
this.block = block;
}
public long getDownpos() {
return downpos;
}
public void setDownpos(long downpos) {
this.downpos = downpos;
}
public String getDownloadurl() {
return downloadurl;
}
public void setDownloadurl(String downloadurl) {
this.downloadurl = downloadurl;
}
}
定义好了这个类我们就可以根据刚才获取的文件大小来分配单个线程文件下载的大小了,为了方便起见呢 我就设置一条线程吧!
for (int i = 0; i < this.threadcount; i++) {
DownLoadInfo info = new DownLoadInfo();
long startpos = 0, endpos = 0;
if (i == this.threadcount - 1) {
startpos = i * block;
endpos = this.filesize - 1;
} else {
startpos = i * block;
endpos = (i + 1) * block - 1;
}
info.setBlock(block);
info.setDownpos(0);
info.setStartpos(startpos);
info.setEndpos(endpos);
info.setDownloadurl(this.apk_url);
info.setThreadid(i);
DownDbUtils.insert(this.context, info);
infos.add(info);
info = null;
}
得到每条线程对应的数据之后,我们就可以开启线程啦!下面的做法我和一般的不一样 因为我没有用到 RandomAccessFile 这个类,而是直接用 File + FileOutputStream 这两个类来实现的,原因呢 我发现 RandomAccessFile 这个类的性能非常的差,非常的差,非常的差!重要的是说三遍!因为这原因,我在我司的平板是下载文件的速度很慢很慢!都要哭了!

当然之后我了解了一下 可以用 RandomAccessFile+ nio 来提升文件的写入速度!!
好啦!现在开始介绍下载类啦
public class DownLoadThread extends Thread {
private String apkurl;//下载地址
private long startpos;//起始地址
private long endpos;//结束地址
private long downpos;//已经下载的大小
private String apkpath;//保存地址
private long block;//每块大小
private int threadid;//线程ID
private boolean finish = false; // 是否已经下载完成
private boolean error = false; // 是否出错
private Context context;
private DownLoader loader;
private int downstate;//下载状态
public static final int PAUSE = 2;//暂停
public static final int RUNNING = 1;//正在下载
public static final int STOP = 0;//停止
public DownLoadThread(Context context, String apkurl, long startpos, long endpos, long downpos, String apkpath, long block, int threadid, DownLoader loader) {
this.context = context;
this.apkurl = apkurl;
this.startpos = startpos;
this.endpos = endpos;
this.downpos = downpos;
this.apkpath = apkpath;
this.block = block;
this.threadid = threadid;
this.loader = loader;
this.downstate = RUNNING;
}
public DownLoadThread() {
}
@Override
public void run() {
File file=null;
FileOutputStream fout=null;
InputStream in = null;
if (downpos < block) {
try {
URL url = new URL(apkurl);
HttpURLConnection http = (HttpURLConnection) url
.openConnection();
http.setConnectTimeout(5 * 1000);
http.setReadTimeout(5 * 1000);
http.setRequestMethod("GET");
http.setRequestProperty(
"Accept",
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x- shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
http.setRequestProperty("Accept-Language", "zh-CN");
http.setRequestProperty("Referer", url.toString());
http.setRequestProperty("Charset", "UTF-8");
http.setRequestProperty("Connection", "Keep-Alive");
long startPos = startpos + downpos;
long endPos = endpos;
http.setRequestProperty("Range", "bytes=" + startPos + "-");// 设置获取实体数据的范围
file=new File(apkpath);
if (file.length()>0){
fout=new FileOutputStream(file,true);
}else{
fout=new FileOutputStream(file);
}
byte[] bytes = new byte[2048];
int len = 0;
in = http.getInputStream();
LogUtils.e("开始");
while ((len = in.read(bytes, 0, bytes.length)) != -1) {
if (PAUSE == this.downstate || STOP == this.downstate) {
DownDbUtils.update(this.context, this.threadid, this.apkurl, downpos);
break;
}
fout.write(bytes,0,len);
downpos += len;//已下载的大小
this.loader.setDownlength(len);
}
DownDbUtils.update(this.context, this.threadid, this.apkurl, downpos);
if (!DeviceUtils.isNet(context)) {
this.finish = false;
this.downstate=PAUSE;
} else {
this.finish = true;
}
} catch (Exception e) {
DownDbUtils.update(this.context, this.threadid, this.apkurl, downpos);
LogUtils.e(e.toString());
e.printStackTrace();
downpos = -1;
this.error = true;
this.finish = false;
} finally {
closeIO(in,fout);
}
}
}
//得到每条线程已经下载的大小
public long getDownpos() {
return downpos;
}
public int getDownstate() {
return downstate;
}
public void setDownstate(int downstate) {
this.downstate = downstate;
}
//是否下载完成
public boolean isFinish() {
return finish;
}
public void setFinish(boolean finish) {
this.finish = finish;
}
//是否下载出错
public boolean isError() {
return error;
}
public void setError(boolean error) {
this.error = error;
}
public void setDownpos(long downpos) {
this.downpos = downpos;
}
//关闭流
public static void closeIO(Closeable... closeables) {
if (null == closeables || closeables.length <= 0) {
return;
}
for (Closeable cb : closeables) {
try {
if (null == cb) {
continue;
}
cb.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这里大家得了解下 http 中这个 Range 这个字段的含义:用户请求头中,指定第一个字节的位置和最后一个字节的位置,如(Range:200-300)! 这样就可以指定下载文件的位置了呢!到了这里核心的部分已经说完了!
其他
上面已经把核心的都说完了,其实还有其他的可以说呢:
- 实现断点下载(保存下载的长度,用数据库或者文件保存都可以)
- 多线程下载的管理 (需要读者实现管理器了)
- 可以把下载这个模块放到另外一个进程中,这样可以是主进程更加的流畅。当然这涉及到了进程见通信的问题啦
- 一般下载的时候都会有下载进度条,这里要注意下更新的频率的问题,在
listview中更新太快会造成页面卡顿的哦!
教你如何在 Android 使用多线程下载文件的更多相关文章
- Android之——多线程下载演示样例
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46883927 一.概述 说到Android中的文件下载.Android API中明 ...
- java 多线程下载文件 以及URLConnection和HttpURLConnection的区别
使用 HttpURLConnection 实现多线程下载文件 注意GET大写//http public class MultiThreadDownload { public static void m ...
- Android利用Http下载文件
Android利用Http下载文件 一.场景 下载存文本文件和下载如mp3等大容量的文件 界面 二.代码编写 1.AndroidMainfest.xml中配置 主要是解决网络权限和写SDCard的权限 ...
- Python之FTP多线程下载文件之分块多线程文件合并
Python之FTP多线程下载文件之分块多线程文件合并 欢迎大家阅读Python之FTP多线程下载系列之二:Python之FTP多线程下载文件之分块多线程文件合并,本系列的第一篇:Python之FTP ...
- Python之FTP多线程下载文件之多线程分块下载文件
Python之FTP多线程下载文件之多线程分块下载文件 Python中的ftplib模块用于对FTP的相关操作,常见的如下载,上传等.使用python从FTP下载较大的文件时,往往比较耗时,如何提高从 ...
- 多线程下载文件,ftp文件服务器
1: 多线程下载文件 package com.li.multiplyThread; import org.apache.commons.lang3.exception.ExceptionUtils; ...
- java 多线程下载文件并实时计算下载百分比(断点续传)
多线程下载文件 多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每一个线程下载的部分组装起来 ...
- Android 通过SOCKET下载文件的方法
本文实例讲述了Android通过SOCKET下载文件的方法.分享给大家供大家参考,具体如下: 服务端代码 import java.io.BufferedInputStream; import java ...
- java 网络编程基础 InetAddress类;URLDecoder和URLEncoder;URL和URLConnection;多线程下载文件示例
什么是IPV4,什么是IPV6: IPv4使用32个二进制位在网络上创建单个唯一地址.IPv4地址由四个数字表示,用点分隔.每个数字都是十进制(以10为基底)表示的八位二进制(以2为基底)数字,例如: ...
随机推荐
- IntelliJ IDEA类头注释和方法注释
The template is editable. Along with static text, code and comments, you can also use predefined var ...
- [Asp.net]Calendar+JqueryUi实现日程管理(右键菜单,添加,编辑,删除,源码)
引言 出差终于回来了,这篇文章算是这个月的博客的开篇吧. 上篇文章:[Asp.net]Calendar+JqueryUi实现日程管理——添加日程 上篇文章主要贴了一些该项目的界面,这里面,将主要代码也 ...
- Node Redis 小试
Redis 是一个高性能的 key-value 数据库,为了保证效率,数据都是缓存在内存中,在执行频繁而又复杂的数据库查询条件时,可以使用 Redis 缓存一份查询结果,以提升应用性能. 背景 如果一 ...
- vue组件调用(全局调用和局部调用)
当用vue-cli创建一个项目后, 创建项目的方法: https://www.cnblogs.com/fps2tao/p/9376847.html 编写了组件怎么,在其他组件中调用了? 组件listB ...
- QSettings 使用实例 当需要在程序关闭时保存”状态“信息
用户对应用程序经常有这样的要求:要求它能记住它的settings,比如窗口大小,位置,一些别的设置,还有一个经常用的,就是recent files,等等这些都可以通过Qsettings来实现. 我们知 ...
- android.graphics(2) - Path, drawPath, moveTo, lineTo, addRect, addCircle, addOval, addArc, drawText, drawTextOnPath
一.创建路径 canvas中绘制路径利用: void drawPath (Path path, Paint paint) 1.直线路径 void moveTo (float x1, float y1) ...
- struts2内置拦截器和自定义拦截器详解(附源码)
一.Struts2内置拦截器 Struts2中内置类许多的拦截器,它们提供了许多Struts2的核心功能和可选的高级特 性.这些内置的拦截器在struts-default.xml中配置.只有配置了拦截 ...
- SVN 安装后报不是内部或外部命令
SVN安装后报不是内部或外部命令,也不是可运行的程序 解决方法:windows安装svn的时候默认是不安装 svn command line这个东西的,重新打开svn的安装exe,选择modify,将 ...
- UIViewController的生命周期及iOS程序执行顺序 和ios6 处理内存警告
当一个视图控制器被创建,并在屏幕上显示的时候. 代码的执行顺序1. alloc 创建对象,分配空间2.init (initWithN ...
- Django And Django-Rest-Framework 异常记录
1.TypeError: init() takes 1 positional argument but 2 were given