1、首先实现界面的实现
关于进度条的设置
设置进度条的样式
style="?android:attr/progressBarStyleHorizontal"//设置为线性进度条
页面具体实现代码如下
<RelativeLayout 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"
tools:context="${packageName}.${activityClass}" >
<TextView
android:id="@+id/tv_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginTop="14dp"
android:text="@string/tv_fileName"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ProgressBar
android:id="@+id/pb"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/ed_url"
/>
<EditText
android:id="@+id/ed_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/tv_url"
android:ems="10"
android:inputType="textUri"
android:text="@string/down_url" />
<Button
android:id="@+id/btn_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/pb"
android:onClick="downLoadFile"
android:text="@string/down_btn" />
<TextView
android:id="@+id/tv_pd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/btn_download"
android:layout_marginTop="20dp"
android:text="@string/tv_pd" />
</RelativeLayout>
2、得到服务器文件大小,
然后在本地设置一个临时文件()服务的文件大小一致
String path="D:\\sun.avi";
File file = new File(path);
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
3、开启线程的数量(每个线程下载的大小(开始与结束位置))
//开启线程数量
threadNum=3
//每个线程长度
//每个线程长度
threadSize = fileSize/threadNum =3
//开始位置
//开始位置
startIndex = (threadNum-1)*3
//结束位置
//结束位置
endIndex =threadNum*threadSize-1
以fileSize=11,
threadNum=3为例
threadNum=1 startIndex = 0 endIndex = 2
threadNum=2 startIndex = 3 endIndex = 5
threadNum=3 startIndex = 6 endIndex = 10
threadNum=2 startIndex = 3 endIndex = 5
threadNum=3 startIndex = 6 endIndex = 10
int threadSize = fileLength/threadNum;
for (int threadId=1;threadId <=3;threadId++) {
int startIndex=(threadId-1)*threadSize;//开始位置
int endIndex = threadId*threadSize-1;
if(threadId==threadNum){//最后一个线程
endIndex=fileLength;
}
System.out.println("当前线程--"+threadId+"开始位置---"+startIndex+"结束位置---"+endIndex+"线程大小---");
//开启线程下载
new DownLoadThread(threadId, startIndex, endIndex, spec).start();
}
4、
使用Http的Range头字段指定每条线程从文件的什么位置开始下载
URL url = new URL(path);//通过path路径构建URL对象
//通过URL对象的打开连接,返回对象
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//设置请求头
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(5000);
//设置下载文件的开始位置和结束位置
httpURLConnection.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
//获取的状态吗
int code = httpURLConnection.getResponseCode();
5、保存文件,使用RandomAccessFile类指定每条线程从本地文件的什么位置开始写入数据
while((len=inputStream.read(buffer))!=-1){
System.out.println("当前线程--"+threadId+"--已经下载了"+(startIndex+total));
@SuppressWarnings("resource")
RandomAccessFile threadfile = new RandomAccessFile(new File("D:\\"+threadId+".txt"),"rwd");
threadfile.writeBytes((startIndex+total)+"");
raf.write(buffer, 0, len);
total+=len;
}
基本实现下载功能后需要进一步改进,实现断点续传的功能
首先得记录下下载断点的位置
判断是否存在sd卡
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){}
使用txt文本记录断点位置,并在进入程序的时候判断是否存在断点文件
File sdDir = Environment.getExternalStorageDirectory();
File pdfile = new File(sdDir, "pd.txt");
FileInputStream is=null;
try {
if (pdfile.exists()) {
//首先判断文件是否存在
is = new FileInputStream(pdfile);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (is!=null) {
String value = StreamTools.streamTostr(is);
String arr[] = value.split(";");
pBar.setMax(Integer.valueOf(arr[0]));//最大值
currentProgress = Integer.valueOf(arr[1]);//当前值
pBar.setProgress(currentProgress);//显示文本
//String percent = arr[1];
tv_pd.setText(arr[2]);
}
实现代码如下
package www.csdn.net.download;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import www.csdn.net.tools.StreamTools;
import android.R.string;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private int threadRunning = 3;
private int threadNum = 3;
private EditText et_url;
private TextView tv_pd;
private ProgressBar pBar;
private int currentProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_url = (EditText) findViewById(R.id.ed_url);
tv_pd = (TextView) findViewById(R.id.tv_pd);
pBar = (ProgressBar) findViewById(R.id.pb);
File sdDir = Environment.getExternalStorageDirectory();
File pdfile = new File(sdDir, "pd.txt");
FileInputStream is=null;
try {
if (pdfile.exists()) {
//首先判断文件是否存在
is = new FileInputStream(pdfile);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (is!=null) {
String value = StreamTools.streamTostr(is);
String arr[] = value.split(";");
pBar.setMax(Integer.valueOf(arr[0]));//最大值
currentProgress = Integer.valueOf(arr[1]);//当前值
pBar.setProgress(currentProgress);//显示文本
//String percent = arr[1];
tv_pd.setText(arr[2]);
}
}
public void downLoadFile(View v) {
// 获取下载路径
final String spec = et_url.getText().toString();
if (TextUtils.isEmpty(spec)) {
Toast.makeText(this, "下载地址不为空", Toast.LENGTH_LONG).show();
} else {
new Thread() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
URL url = new URL(spec);
HttpURLConnection httpURLConnection = (HttpURLConnection) url
.openConnection();
// 设置请求的头文件信息还有时间
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(5000);
httpURLConnection.setReadTimeout(5000);
if (httpURLConnection.getResponseCode() == 200) {
int fileLength = httpURLConnection
.getContentLength();
// 设置进度条最大值
pBar.setMax(fileLength);
// 判断是否存在外部存储设备
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File sdFile = Environment
.getExternalStorageDirectory();
// 获取文件名称
String fileName = spec.substring(spec
.lastIndexOf("/") + 1);
// 创建保存文件
File file = new File(sdFile, fileName);
// 穿件随机可以访问的文件对象
RandomAccessFile accessFile = new RandomAccessFile(
file, "rwd");
// 设置文件大小
accessFile.setLength(fileLength);
accessFile.close();
// 首先计算出每个线程下载的大小 开始位置 结束位置
int threadSize = fileLength / threadNum;
for (int threadId = 1; threadId <= 3; threadId++) {
int startIndex = (threadId - 1)
* threadSize;// 开始位置
int endIndex = threadId * threadSize - 1;
if (threadId == threadNum) {// 最后一个线程
endIndex = fileLength;
}
System.out.println("当前线程--" + threadId
+ "开始位置---" + startIndex
+ "结束位置---" + endIndex + "线程大小---");
// 开启线程下载
new DownLoadThread(threadId, startIndex,
endIndex, spec, fileName).start();
}
} else {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this,
"sd卡不可用", 1).show();
}
});
}
} else {
// 在线程中运行
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MainActivity.this,
"服务器返回错误", 1).show();
;
}
});
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
}
class DownLoadThread extends Thread {
private int threadId;
private int startIndex;
private int endIndex;
private String path;
private String fileName;
public DownLoadThread(int threadId, int startIndex, int endIndex,
String path, String fileName) {
super();
this.threadId = threadId;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.path = path;
this.fileName = fileName;
}
@Override
public void run() {
File sdFile = Environment.getExternalStorageDirectory();
// 获取每个线程下载的记录文件
File recordFile = new File(sdFile, threadId + ".txt");
// 可以通过每个线程去下载文件了。
try {
// 首先从本地文件上读取已经下载文件的开始位置
if (recordFile.exists()) {
// 读取文件的内容
InputStream is = new FileInputStream(recordFile);
// 利用工具类转换
String value = StreamTools.streamTostr(is);
// 获取记录位置
int recordIndex = Integer.parseInt(value);
startIndex = recordIndex;// 记录的位置复制给开始的位置即可
}
URL url = new URL(path);// 通过path路径构建URL对象
// 通过URL对象的打开连接,返回对象
HttpURLConnection httpURLConnection = (HttpURLConnection) url
.openConnection();
// 设置请求头
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(5000);
// 设置下载文件的开始位置和结束位置
httpURLConnection.setRequestProperty("Range", "bytes="
+ startIndex + "-" + endIndex);
// 获取的状态吗
int code = httpURLConnection.getResponseCode();
if (code == 206) {
// 获取每个线程返回的流对象:
InputStream inputStream = httpURLConnection
.getInputStream();
// 定一写入文件的路径
// 根据路径创建文件
File file = new File(sdFile, fileName);
// 根据文件创建她RandomAccessFile对象
RandomAccessFile raf = new RandomAccessFile(file, "rwd");
raf.seek(startIndex);
// 定义读取的长度
int len = 0;
byte buffer[] = new byte[1024 * 1024 * 10];
int total = 0;
// 循环读取
while ((len = inputStream.read(buffer)) != -1) {
System.out.println("当前线程--" + threadId + "--已经下载了"
+ (startIndex + total));
@SuppressWarnings("resource")
RandomAccessFile threadfile = new RandomAccessFile(
new File(sdFile, threadId + ".txt"), "rwd");
threadfile.writeBytes((startIndex + total) + "");
threadfile.close();
raf.write(buffer, 0, len);
total += len;
//解决同步问题
synchronized (MainActivity.this) {
currentProgress += len;
pBar.setProgress(currentProgress);
final String percent = currentProgress*100L/pBar.getMax()+"%";
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
tv_pd.setText("当前进度是:" + percent);
}
});
RandomAccessFile pbfile = new RandomAccessFile(new File(sdFile, "pd.txt"),"rwd");
pbfile.writeBytes(pBar.getMax()+";"+currentProgress+";"+percent);
pbfile.close();
}
}
raf.close();
inputStream.close();
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this,
"当前线程----" + threadId + "下载完毕", 1).show();
}
});
deleteRecordFiles();
} else {
runOnUiThread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "服务器端下载错误", 1)
.show();
}
});
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public synchronized void deleteRecordFiles() {
File sdFile = Environment.getExternalStorageDirectory();
threadRunning--;
if (threadRunning == 0) {
for (int i = 1; i < 3; i++) {
File recordFile = new File(sdFile, i + ".txt");
if (recordFile.exists()) {
recordFile.delete();// 删除掉文件
}
File pdFile = new File(sdFile,"pd.txt");
if (pdFile.exists()) {
pdFile.delete();
}
}
}
}
}
对于多线程下载理解还不是很透彻,在测试的过程中出现了不少bug,比如断点保存时会超出100%,进入下载后退出再进入后悔停止运行,再重新进入后才能下载。望大神们指教。