Android 多线程断点下载(非原创)
1.服务器的CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源,这里在客户端开启多个线程来从服务器下载资源
2.fragment_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <!-- 点击下载按钮 --> <Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="download"
android:text="开始下载" />
<!-- 进度条 --> <ProgressBar
android:id="@+id/pb_bar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- 显示进度 如: 5% --> <TextView
android:id="@+id/tv_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#CC5599"
android:textSize="24sp" /> </LinearLayout>
3.MainActivity.java
package com.example.phonemultithreaddownload; import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL; import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView; public class MainActivity extends Activity {
// 文件名字
String fileName = "ESurfing_V2.1.exe";
// 路径
String path = "http://192.168.1.66:8080/" + fileName;
// 线程的个数,越多下载的越快
int threadCount = 3;
// 线程下载完毕的个数
private static int finishThreadCount = 0;
// 进度条
ProgressBar pb_bar;
// 显示进度
TextView tv_progress;
// 当前下载的进度
int totalProgress = 0;
// 消息处理器
Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
// 刷新TextView,注意:pb_bar.getProgress返回的值会超出int范围,结果可能为负数,需要强转为long
tv_progress.setText((long) pb_bar.getProgress() * 100 / pb_bar.getMax() + " %");
};
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
//加载进度条
pb_bar = (ProgressBar) findViewById(R.id.pb_bar);
//加载进度文本
tv_progress = (TextView) findViewById(R.id.tv_progress);
} public void download(View v) {// 下载
Thread t = new Thread() {
@Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5000);
con.setReadTimeout(5000);
con.setRequestMethod("GET");
if (con.getResponseCode() == 200) {
// 拿到请求资源文件的大小
int length = con.getContentLength();
// 设置进度条最大值
pb_bar.setMax(length);
File file = new File(fileName);
// 生成临时文件
RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/" + file, "rwd");
//设置临时文件的大小
raf.setLength(length);
raf.close();
//求出每个线程平均下载的字节数
int size = length / threadCount;
for (int i = 0; i < threadCount; i++) {
//开始的位置
int startIndex = i * size;
//结束的位置
int endIndex = i * size + size - 1;
//如果是最后一个线程,开始的位置确定,但是结束的位置是不确定的,直接设为文件的长度即可
if (i == threadCount - 1) {
endIndex = length;
}
System.out.println("ID : " + i + startIndex + "~" + endIndex + " , " + (endIndex - startIndex + 1));
//创建线程并启动
new DownLoadThread(startIndex, endIndex, i + 1, path).start();
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
};
t.start();
} class DownLoadThread extends Thread {
//开始下载的位置
private int startIndex;
//结束下载的位置
private int endIndex;
//线程的ID
private int threadId;
//下载的资源路径
private String path; public DownLoadThread(int startIndex, int endIndex, int threadId, String path) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
this.path = path;
} @Override
public void run() {
try {
URL url = new URL(path);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setConnectTimeout(5000);
con.setReadTimeout(5000);
con.setRequestMethod("GET");
/*
* 先判断是否保存了上次下载的字节数
*/
File progessFile = new File(Environment.getExternalStorageDirectory(), threadId + ".txt");
if (progessFile.exists()) {
// 如果文件存在,说明不是第一次下载,修改下载的开始位置
BufferedReader br = new BufferedReader(new FileReader(progessFile));
//读取一行
int newIndex = Integer.parseInt(br.readLine());
//修改开始的位置
startIndex = startIndex + newIndex;
//修改进度条的值
totalProgress += newIndex;
System.out.println(threadId + " , 上次下载了 " + newIndex);
br.close();
}
// 设置本次http所请求的区间
con.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
// 请求部分数据的响应码是206
if (con.getResponseCode() == 206) {
RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/" + fileName, "rwd");
// 把文件的写入位置移动至startIndex位置
raf.seek(startIndex);
InputStream is = con.getInputStream();
int len = -1;
int total = 0;
byte[] buf = new byte[1024];
while ((len = is.read(buf)) != -1) {
//将读取到的数据i写入文件中
raf.write(buf, 0, len);
//记录该线程一共下载了多少
total += len;
System.out.println("线程" + threadId + "下载了" + total);
//更改进度条的值
totalProgress += len;
// 设置进度
pb_bar.setProgress(totalProgress);
// 显示进度,发送空消息,携带进度的值
handler.sendEmptyMessage(totalProgress);
RandomAccessFile processRaf = new RandomAccessFile(progessFile, "rwd");
processRaf.write((total + "").getBytes());
processRaf.close();
}
System.out.println(threadId + "下载完成了------------------------------------------");
raf.close();
// 删除缓存文件
finishThreadCount++;
System.out.println("准备删除文件" + threadId + " finishThreadCount = " + finishThreadCount);
synchronized (DownLoadThread.class) {
if (finishThreadCount == threadCount) {
for (int i = 0; i < threadCount; i++) {
File tempFile = new File(Environment.getExternalStorageDirectory(), i + 1 + ".txt");
System.out.println("删除" + (i + 1) + ".txt文件");
tempFile.delete();
}
finishThreadCount = 0;
// 资源下载完毕,有可能出现99%的情况,手动改为100%
pb_bar.setProgress(pb_bar.getMax());
}
}
}
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} }
4.添加权限,网络权限和网SD卡写入的权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
5.效果如下:
Android 多线程断点下载(非原创)的更多相关文章
- Android 学习之--android多线程断点下载
我们平时都用"迅雷"下载软件,当下载到一半的时候突然断网,下次开启的时候能够从上次下载的地方继续下载,而且下载速度很快,那么这是怎么做到的呢! 其实它的“快”其实就是多线程的下载实 ...
- Android多线程断点下载
到华为后,信息管理特别严格,文件不能外发.所以好久都没写博客了,今天周日,老婆非要我学习.就闲来无事,写一篇博客,呵呵-- 前段时间,项目中提到了断点下载apk并静默安装的需求.本打算用应用市场成熟的 ...
- Android 多线程断点下载
package com.itheima.mutiledownloader; import java.io.BufferedReader; import java.io.File; import jav ...
- Android多线程断点下载的代码流程解析
Step 1:创建一个用来记录线程下载信息的表 创建数据库表,于是乎我们创建一个数据库的管理器类,继承SQLiteOpenHelper类 重写onCreate()与onUpgrade()方法 DBOp ...
- 我的Android进阶之旅------>Android基于HTTP协议的多线程断点下载器的实现
一.首先写这篇文章之前,要了解实现该Android多线程断点下载器的几个知识点 1.多线程下载的原理,如下图所示 注意:由于Android移动设备和PC机的处理器还是不能相比,所以开辟的子线程建议不要 ...
- Android(java)学习笔记216:多线程断点下载的原理(Android实现)
之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载: 1.新建一个Android工程: (1)其中我们先实现布局 ...
- Android(java)学习笔记159:多线程断点下载的原理(Android实现)
之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载: 1. 新建一个Android工程: (1)其中我们先实现布 ...
- andoid 多线程断点下载
本示例介绍在Android平台下通过HTTP协议实现断点续传下载. 我们编写的是Andorid的HTTP协议多线程断点下载应用程序.直接使用单线程下载HTTP文件对我们来说是一件非常简单的事.那么,多 ...
- 33、多线程断点下载的实现&界面的更新
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...
随机推荐
- js获取上传文件扩展名
File_box.value.substring(File_box.value.lastIndexOf(".") + 1);
- C#图像处理(3):在图像上加条形码
引入Aspose.BarCode用来生成条形码,方法如下: /// <summary> /// 生成条码 /// </summary> /// <param name=& ...
- 将 varchar 值转换为 JDBC 数据类型 DATE 时发生错误。
问题是: 我是这样解决的 : 网上的 转型方法 并不好使 ,我想了想 可能是由于返回值是String 我 就成功的解决错误了 ..下面是关于原理的讲解肯定方法不唯一 至于错误,的产生,这个肯 ...
- JavaScript的数据类型转换
首先,由于JavaScript是弱类型语言(弱类型的语言的东西没有明显的类型,他能随着环境的不同,自动变换类型而强类型则没这样的规定,不同类型间的操作有严格定义,只有相同类型的变量才能操作,虽然系统也 ...
- 512M内存机器如何用好Mysql
购买阿里云512M内存ECS后,mysql有时候会自动关闭,停止运行 解决办法: a,优化mysql配置,因为自己安装的是mysql 5.6,而从5.6开始,mysql安装包中不再包含my-small ...
- ibatis 更改resultmap后 java.sql.SQLException: Column 'del_status' not found.
当在resultmap中增加字段后,查询语句也必须增加相应字段,否则会报错, java.sql.SQLException: Column 'del_status' not found. 因为查询结果与 ...
- [iOS常见问题] 关于使用QQ做第三方登录的问题!
[iOS常见问题] 关于使用QQ做第三方登录的问题! 注意:QQ本身没有授权功能,所以想要使用QQ做第三方登录必须通过QQ空间来实现! 第一步:集成ShareSDK(步骤同集成分享的一样,如果已经集成 ...
- CoreData (三)备
NSFetchedResultsController 什么是NSFetchedResultsController NSFetchedResultsController是一个让人爱恨交加的一个类.如果使 ...
- Codeforces Round #281 (Div. 2) 解题报告
题目地址:http://codeforces.com/contest/493 A题 写完后就交了,然后WA了,又读了一遍题,没找出错误后就开始搞B题了,后来回头重做的时候才发现,球员被红牌罚下场后还可 ...
- LeetCode_Set Matrix Zeroes
Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in place. 很挫的一个想 ...