前端时间公司发新的版本,可是版本升级时候下载apk的时候速度特别的慢,不知道是不是服务器的原因
领导问有没有什么解决办法,于是我就写了个多线程断点续传的功能。(其实多线程对于提速帮助不大)
第一次没有下载完,用户没有耐心了,退出应用,甚至把整个应用都干掉了,在下次打开应用的时候可以继续上一次的下载
注释都在代码中,拿下去可以直接当作工具类使用,关于异常的处理都有注释
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import com.easipass.tool.weather.NetUtils;
/***
* 实现多线程断点下载
* @author 徐初龙
*/
public class BreakpointDownloader {
private static final String DIR_PATH = Environment.getExternalStorageDirectory() + "/"; // 下载目录
private static final int THREAD_AMOUNT = 3; // 总线程数
private URL url; // 目标下载地址
private File dataFile; // 本地文件
private File tempFile; // 用来存储每个线程下载的进度的临时文件
private long threadLen; // 每个线程要下载的长度
private long totalFinish; // 总共完成了多少
private long totalLen; // 服务端文件总长度
public static long backtotalLen;
private long begin; // 用来记录开始下载时的时间
private Handler handler;
private Context context;
private boolean allthreadisrun = true;
public BreakpointDownloader(int newCode, String address, Handler handler,Context context) throws IOException {
url = new URL(address); // 记住下载地址
dataFile = new File(DIR_PATH, Config.UPDATE_SAVENAME.replace(".apk", newCode + ".apk")); // 截取地址中的文件名, 创建本地文件
tempFile = new File(dataFile.getAbsolutePath() + ".temp"); // 在本地文件所在文件夹中创建临时文件
this.context = context;
File oldDataFile = new File(DIR_PATH, Config.UPDATE_SAVENAME.replace(".apk", newCode-1 + ".apk"));
File oldTempFile = new File(oldDataFile.getAbsolutePath() + ".temp");
if(oldDataFile.exists())
oldDataFile.delete();
if(oldTempFile.exists())
oldTempFile.delete();
this.handler = handler;
}
public void download() throws IOException {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(10000);
totalLen = conn.getContentLength(); // 获取服务端发送过来的文件长度
threadLen = (totalLen + THREAD_AMOUNT - 1) / THREAD_AMOUNT; // 计算每个线程要下载的长度
Message msg = new Message();
if(totalLen<0){
msg.getData().putLong("totalLen", backtotalLen);
}else{
backtotalLen = totalLen;
msg.getData().putLong("totalLen", totalLen);
}
msg.what = 1;
handler.sendMessage(msg); // 发送文件总长度
if (!dataFile.exists()) { // 如果本地文件不存在
try{
RandomAccessFile raf = new RandomAccessFile(dataFile, "rws"); // 在本地创建文件
raf.setLength(totalLen); // 设置文件的大小和服务端相同
raf.close();
}catch(Exception e){
e.printStackTrace();
}
}
if (!tempFile.exists()) { // 如果临时文件不存在
RandomAccessFile raf = new RandomAccessFile(tempFile, "rws"); // 创建临时文件, 用来记录每个线程已下载多少
for (int i = 0; i < THREAD_AMOUNT; i++) // 按照线程数循环
raf.writeLong(0); // 写入每个线程的开始位置(都是从0开始)
raf.close();
}
for (int i = 0; i < THREAD_AMOUNT; i++) // 按照线程数循环
new DownloadThread(i).start(); // 开启线程, 每个线程将会下载一部分数据到本地文件中
begin = System.currentTimeMillis(); // 记录开始时间
}
private class DownloadThread extends Thread {
private int id; // 用来标记当前线程是下载任务中的第几个线程
public DownloadThread(int id) {
this.id = id;
}
boolean flag = true;
public void run() {
boolean isrun = false; //每个线程都有自己的标识位,用来控制线程的重启
while(allthreadisrun && !isrun){
//判断网络连接是否可用,不可用直接退出
if (!NetUtils.getNetworkIsAvailable(context)) {
handler.sendEmptyMessage(3);
return;
}
try {
isrun = true;
if(tempFile.exists()&&dataFile.exists()){
RandomAccessFile tempRaf = new RandomAccessFile(tempFile, "rws"); // 用来记录下载进度的临时文件
tempRaf.seek(id * 8); // 将指针移动到当前线程的位置(每个线程写1个long值, 占8字节)
long threadFinish = tempRaf.readLong(); // 读取当前线程已完成了多少
//判断 如果当前线程已经完成了下载,则退出该线程
if(threadFinish == threadLen){
// System.out.println("线程"+id+"已经退出");
tempRaf.close();
totalFinish +=threadFinish;
if (totalFinish >= totalLen && totalFinish == (totalLen + THREAD_AMOUNT - 1)) { // 如果已完成长度等于服务端文件长度(代表下载完成)
System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));
tempFile.delete(); // 删除临时文件
}
return;
}
synchronized(BreakpointDownloader.this) { // 多个下载线程之间同步
totalFinish += threadFinish; // 统计所有线程总共完成了多少
}
// System.out.println("totalFinish="+totalFinish);
Message msg = new Message();
msg.getData().putLong("totalFinish", totalFinish);
msg.what = 2;
handler.sendMessage(msg);
long start = id * threadLen + threadFinish; // 计算当前线程的起始位置
long end = id * threadLen + threadLen - 1; // 计算当前线程的结束位置
System.out.println("线程" + id + ": " + start + "-" + end);
// System.out.println("------->url"+url);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(10000);
conn.setRequestProperty("Range", "bytes=" + start + "-" + end); // 设置当前线程下载的范围
InputStream in = conn.getInputStream(); // 获取连接的输入流
RandomAccessFile dataRaf = new RandomAccessFile(dataFile, "rws"); // 装载数据的本地文件(可以理解为输出流)
dataRaf.seek(start); // 设置当前线程保存数据的位置
byte[] buffer = new byte[1024 * 100]; // 每次拷贝100KB
int len;
while (totalFinish<totalLen && (len = in.read(buffer)) != -1) {
dataRaf.write(buffer, 0, len); // 从服务端读取数据, 写到本地文件
threadFinish += len; // 每次写入数据之后, 统计当前线程完成了多少
tempRaf.seek(id * 8); // 将临时文件的指针指向当前线程的位置
tempRaf.writeLong(threadFinish); // 将当前线程完成了多少写入到临时文件
synchronized(BreakpointDownloader.this) { // 多个下载线程之间同步
totalFinish += len; // 统计所有线程总共完成了多少
Message msg3 = new Message();
msg3.getData().putLong("totalFinish", totalFinish);
msg3.what = 2;
handler.sendMessage(msg3); // 发送当前进度
// System.out.println("下载进度-----》"+totalFinish+"/"+totalLen);
}
}
dataRaf.close();
tempRaf.close();
}
} catch (IOException e) {
e.printStackTrace();
// System.out.println("1 ---》这里异常了");
if (!NetUtils.getNetworkIsAvailable(context)) {
// System.out.println("已经发送消息");
handler.sendEmptyMessage(3);
return;
}
try {
isrun = true;
if(tempFile.exists()&&dataFile.exists()){
RandomAccessFile tempRaf = new RandomAccessFile(tempFile, "rws"); // 用来记录下载进度的临时文件
tempRaf.seek(id * 8); // 将指针移动到当前线程的位置(每个线程写1个long值, 占8字节)
long threadFinish = tempRaf.readLong(); // 读取当前线程已完成了多少
//判断 如果当前线程已经完成了下载,则退出该线程
if(threadFinish == threadLen){
System.out.println("线程"+id+"已经退出");
tempRaf.close();
totalFinish +=threadFinish;
if (totalFinish >= totalLen && totalFinish == (totalLen + THREAD_AMOUNT - 1)) { // 如果已完成长度等于服务端文件长度(代表下载完成)
System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));
tempFile.delete(); // 删除临时文件
}
return;
}
synchronized(BreakpointDownloader.this) { // 多个下载线程之间同步
totalFinish += threadFinish; // 统计所有线程总共完成了多少
}
long start = id * threadLen + threadFinish; // 计算当前线程的起始位置
long end = id * threadLen + threadLen - 1; // 计算当前线程的结束位置
System.out.println("线程" + id + ": " + start + "-" + end);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(10000);
conn.setRequestProperty("Range", "bytes=" + start + "-" + end); // 设置当前线程下载的范围
InputStream in = conn.getInputStream(); // 获取连接的输入流
RandomAccessFile dataRaf = new RandomAccessFile(dataFile, "rws"); // 装载数据的本地文件(可以理解为输出流)
dataRaf.seek(start); // 设置当前线程保存数据的位置
byte[] buffer = new byte[1024 * 100]; // 每次拷贝100KB
int len;
while (totalFinish<totalLen && (len = in.read(buffer)) != -1) {
dataRaf.write(buffer, 0, len); // 从服务端读取数据, 写到本地文件
threadFinish += len; // 每次写入数据之后, 统计当前线程完成了多少
tempRaf.seek(id * 8); // 将临时文件的指针指向当前线程的位置
tempRaf.writeLong(threadFinish); // 将当前线程完成了多少写入到临时文件
synchronized(BreakpointDownloader.this) { // 多个下载线程之间同步
totalFinish += len; // 统计所有线程总共完成了多少
Message msg4 = new Message();
msg4.getData().putLong("totalFinish", totalFinish);
msg4.what = 2;
handler.sendMessage(msg4); // 发送当前进度
// System.out.println("下载进度-----》"+totalFinish+"/"+totalLen);
}
}
dataRaf.close();
tempRaf.close();
}
}catch(Exception e2){
e2.printStackTrace();
isrun = false;
System.out.println("2 ---》这里异常了");
if(flag) {
flag = false;
handler.sendEmptyMessage(3);
allthreadisrun = false;
}
}
}
}
if (totalFinish == totalLen || totalFinish == (totalLen + THREAD_AMOUNT - 1)) { // 如果已完成长度等于服务端文件长度(代表下载完成)
System.out.println("下载完成, 耗时: " + (System.currentTimeMillis() - begin));
tempFile.delete(); // 删除临时文件
}else if(dataFile.length()<totalLen){
//未下载完成
System.out.println("下载未完成");
allthreadisrun = false;
handler.sendEmptyMessage(3);
}
}
}
}
和大家分享一下
分享到:
相关推荐
android多线程断点续传 android多线程断点续传 android多线程断点续传
Android多线程断点续传下载+在线播放音乐
AndroidDownloadDemo》Android断点续传下载 AndroidMultiDownloadDemo》Android多线程断点续传下载
android多线程断点续传下载源代码,有需要的朋友可以下载
《Android多线程断点续传下载网络上的音/视频等各种文件》多线程,断点续传,各种网络上的数据,下载网络上的数据时基本功!加油了各位!
Android 多线程断点续传下载,利用线程池减小线程开销,http://mp.blog.csdn.net/postedit?ref=toolbar
Android多线程断点续传下载,可以应付各种网络中断,应用退出,支持暂停、进度条
Android应用源码之Android多线程断点续传下载+在线播放音乐.rar
Android多线程断点续传(Library)项目 绝对可用
Android多线程断点续传下载+在线播放音乐.rar
Android多线程断点续传实现
Android 多线程断点续传 下载 可直接运行
Android多线程断点续传下载器,通过Http协议进行下载,自己进行封装,使用简单方便
Android多线程断点续传下载+在线播放音乐.zip源码资源下载Android多线程断点续传下载+在线播放音乐.zip源码资源下载
Android多线程断点续传下载;涉及数据库保存每个线程的下载数量。断点续传机制。基本的多线程下载机制。
itcast传智播客android多线程断点续传下载代码 可运行的