理解:将服务器的一个资源分成多个部分,每个线程负责下载其中一个部分,然后设置每个线程对同一个文件开始写入的位置,当所有线程下载完成后,本地文件便是一个完整的文件了。
需要的权限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
activity_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="下载" />
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true" />
</RelativeLayout>
MainActivity.java :
package com.xie.app;
import java.io.BufferedInputStream;
import java.io.File;
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 java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener {
// 下载按钮
private Button download;
// 下载进度条
private ProgressBar progress;
// 所有下载任务
private List<MyDownloadTask> tasks;
/** 下载地址 */
public static final String URL_DOWNLOAD = "http://192.168.1.104:8080/WebServer/music/handin.ape";
/** 线程数量 */
public static final int TASK_COUNT = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
download = (Button) findViewById(R.id.download);
download.setOnClickListener(this);
progress = (ProgressBar) findViewById(R.id.progressBar);
}
@Override
public void onClick(View v) {
if (v == download) { // 下载
try {
URL url = new URL(URL_DOWNLOAD);
// 获取资源总长度
int totalLength = url.openConnection().getContentLength();
// 设置进度条总长度
progress.setMax(totalLength);
// 每条线程应该下载的长度
int perLength = totalLength % TASK_COUNT == 0 ? totalLength
/ TASK_COUNT : totalLength / TASK_COUNT + 1;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState()) && isHaveConnection()) { // 如果sd卡可用并且网络可用
// 创建文件
File file = new File(
Environment.getExternalStorageDirectory(),
"handin.ape");
if (file.exists()) {
file.delete();
}
file.createNewFile();
// 初始化线程分支集合
tasks = new ArrayList<MyDownloadTask>(TASK_COUNT);
// 循环创建线程并下载
for (int i = 0; i < TASK_COUNT; i++) {
MyDownloadTask task = new MyDownloadTask(perLength * i,
perLength * (i + 1), url, file);
task.execute();
// 添加进入任务列表中
tasks.add(task);
}
} else {
Toast.makeText(this, "内存卡不可用", Toast.LENGTH_SHORT).show();
}
} catch (MalformedURLException e) {
} catch (IOException e) {
}
}
}
/**
* 判断网络是否可用
*
* @return true可用 false不可用
*/
public boolean isHaveConnection() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null) {
return info.isAvailable();
}
return false;
}
/**
* 下载任务
*
* @author lx
*/
class MyDownloadTask extends AsyncTask<Void, Void, Void> {
// 开始位置
private int begin;
// 结束位置
private int end;
// 下载的地址
private URL url;
// 保存的文件对象
private File file;
public MyDownloadTask(int begin, int end, URL url, File file) {
super();
this.begin = begin;
this.end = end;
this.url = url;
this.file = file;
}
// 当前线程进度情况
private int current;
public int getCurrent() {
return current;
}
@Override
protected Void doInBackground(Void... params) {
try {
HttpURLConnection con = (HttpURLConnection) url
.openConnection();
// 设置下载区间
con.setRequestProperty("Range", "bytes=" + begin + "-" + end);
InputStream in = con.getInputStream();
BufferedInputStream input = new BufferedInputStream(in);
// 创建文件输出,"rwd":读写删除权限,其它类型看提示说明
RandomAccessFile output = new RandomAccessFile(file, "rwd");
// 设置开始写入的位置
output.seek(begin);
// 单次写的byte数组
byte[] bs = new byte[1024 * 1024];
// 单次写的长度
int length = 0;
while ((length = input.read(bs)) != -1) {
output.write(bs, 0, length);
// 当前分支进度增加
current += length;
publishProgress();
}
// 关闭,将缓冲区写入文件
input.close();
output.close();
} catch (IOException e) {
}
return null;
}
@Override
protected void onProgressUpdate(Void... values) {
// 当前总进度
int total = 0;
for (MyDownloadTask task : tasks) { // 循环获取每一条分支的进度
total += task.getCurrent();
}
progress.setProgress(total);
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(Void result) {
if (progress.getProgress() == progress.getMax()) { // 判断进度条是否满
Toast.makeText(MainActivity.this, "下载成功!", Toast.LENGTH_SHORT)
.show();
}
super.onPostExecute(result);
}
}
}