您的位置:首页 > 移动开发 > Android开发

Android 多线程下载

2016-03-21 20:33 381 查看
多线程下载网上有很多的例子,其中需要注意的就是:

每个线程该分配多大的算法;

通过请求从返回的 getContentLength() 方法获取需要下载的文件大小。

使用 RandomAccessFile 类来创建文件夹。因为该类可以从文件的任何位置开始读写操作,有 seek() 方法。

本例是用 Tomcat 充当后台来下载的。

下载成功后的示意图:



具体代码如下:

MultiThreadDownload.java :

package com.crazy.multidownload;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class MultiThreadDownload {

/* 需要下载资源的地址*/
private String urlStr;

/* 下载的文件*/
private File localFile;

/* 需要下载文件的存放的本地文件夹路径*/
private String dirStr;

/* 存储到本地的文件名*/
private String filename;

/* 开启的线程数量*/
private int threadCount;

/* 下载文件的大小*/
private long fileSize;

private Handler handler;

public MultiThreadDownload(String urlStr, String dirStr,
String filename, int threadCount, Handler handler) {
this.urlStr = urlStr;
this.dirStr = dirStr;
this.filename = filename;
this.threadCount = threadCount;
this.handler = handler;
}

public void download() throws IOException {
createFileByUrl();

/* 计算每个线程需要下载的数据长度*/
long block = fileSize % threadCount == 0 ? fileSize / threadCount
: fileSize / threadCount + 1;

for (int i = 0; i < threadCount; i++) {
long start = i * block;
long end = start + block >= fileSize ? fileSize : start + block - 1;

new DownloadThread(new URL(urlStr), localFile, start, end).start();
}

}

/**
* 根据资源的URL获取资源的大小,以及在本地创建文件
*/
public void createFileByUrl() throws IOException {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(15 * 1000);
conn.setRequestMethod("GET");
conn.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, */*");
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Referer", urlStr);
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty(
"User-Agent",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; " +
".NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30;" +
" .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.connect();

if (conn.getResponseCode() == 200) {
// 根据响应获取文件大小
this.fileSize = conn.getContentLength();

if (fileSize <= 0)
throw new RuntimeException(
"下载文件大小出错 ... ");
File dir = new File(dirStr);
if (!dir.exists())
dir.mkdirs();
this.localFile = new File(dir, filename);

RandomAccessFile raf = new RandomAccessFile(this.localFile, "rw");
raf.setLength(fileSize);
raf.close();
Log.e("tag", "需要下载的文件大小为 :" + this.fileSize + " , 存储位置为: "
+ dirStr + "/" + filename);

} else {
throw new RuntimeException("连接错误 ...");
}
}

private class DownloadThread extends Thread {

/* 下载文件的URI*/
private URL url;

/* 存的本地路径*/
private File localFile;

/* 是否结束*/
private boolean isFinish;

/* 开始的位置*/
private Long startPos;

/* 结束位置*/
private Long endPos;

public DownloadThread(URL url, File savefile, Long startPos, Long endPos) {
this.url = url;
this.localFile = savefile;
this.startPos = startPos;
this.endPos = endPos;
}

@Override
public void run() {
Log.d("tag", Thread.currentThread().getName() + "开始下载...");
try {
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
conn.setConnectTimeout(15 * 1000);
conn.setRequestMethod("GET");
conn.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, */*");
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Referer", url.toString());
conn.setRequestProperty("Charset", "UTF-8");

// 设置获取实体数据的范围
conn.setRequestProperty("Range", "bytes=" + startPos + "-"
+ endPos);

conn.setRequestProperty(
"User-Agent",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; " +
".NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; " +
".NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.connect();

/**
* 代表服务器已经成功处理了部分GET请求
*/
if (conn.getResponseCode() == 206) {
InputStream is = conn.getInputStream();
int len = 0;
byte[] buf = new byte[1024];

RandomAccessFile raf = new RandomAccessFile(localFile,
"rwd");
raf.seek(startPos);
while ((len = is.read(buf)) != -1) {
raf.write(buf, 0, len);
}
raf.close();
is.close();
Log.e("tag", Thread.currentThread().getName()
+ "完成下载  : " + startPos + " -- " + endPos);

setMessageForUI();

this.isFinish = true;
} else {
throw new RuntimeException(
"url 连接错误 ...");
}
} catch (IOException e) {
e.printStackTrace();
}
}

}

private void setMessageForUI(){
new Thread(new Runnable() {
@Override
public void run() {
Message msg = Message.obtain();
msg.what = 0x123;
msg.obj = threadCount;
handler.sendMessage(msg);
}
}).start();
}

}


MainAcivity.java :
package com.crazy.multidownload;

import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

import java.io.IOException;

public class MainActivity extends AppCompatActivity {

private Handler handler;
private int threadCount;
private int count = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

threadCount = 2;
initHandler();
downLoad();
}

private void initHandler(){

handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x123) {
count++;
if (count == threadCount)
Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show();
}
}
};

}

private void downLoad() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
new MultiThreadDownload(
"http://192.168.248.2/zip/multidownload.zip",
Environment.getExternalStorageDirectory() + "/multidownload",
"multidownload.zip",
threadCount,
handler).download();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

}.execute();
}
}


开始下载输出的内容见下图:



从上图中可以看到线程的数量和我们所设定的线程数量是相等的。

完成下载后的输出:



从图中可以看出,下载完成的的输出和线程数量相同都为2,所以为了让其真正完成下载(线程数量和输出数量相等时)时在进行提升,才有了 count 计数器,记录接收到的次数。

最后可以打开 ADM 查看下载的路径:



从上面的几张图片中可以看到,文件的总大小是相同的。

最后不要忘了添加相应的权限:

<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"/>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: