package com.example.httpdownloadtest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* @Class Name : DownLoadFileActivity
* @Description:
* @Author yuanlf
* @Date 2014年6月6日 下午2:55:35
*/
public class DownLoadFileActivity extends Activity {
private Context context;
private ProgressBar downPrBar;
private TextView loadProcessNumber;
private String fileName;
private String url;
private final String tag = getClass().getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_down_file);
context = this;
initData();
initUI();
}
private void initUI() {
downPrBar = (ProgressBar) findViewById(R.id.downPrBar);
loadProcessNumber = (TextView) findViewById(R.id.loadProcessNumber);
showDownLoadPrompt();
}
private void initData() {
fileName = "陌陌.apk";
url = "http://gdown.baidu.com/data/wisegame/fbd69031ba7434e0/momo_206.apk";
}
private AlertDialog alertDialog;
/**
* @Description: 显示图书下载提示对话框
*/
private void showDownLoadPrompt() {
LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
View view = LayoutInflater.from(this).inflate(R.layout.common_prompt_two_button_layout,
null);
alertDialog = new AlertDialog.Builder(this).create();
alertDialog.setCanceledOnTouchOutside(false);
alertDialog.show();
alertDialog.setCancelable(false);
alertDialog.setContentView(view, params);
TextView titletip = (TextView) view.findViewById(R.id.titleTipTv);
titletip.setText("下载");
TextView content = (TextView) view.findViewById(R.id.contents);
String message = fileName + "是否需要下载?";
content.setText(message);
Button confirmBtn = (Button) view.findViewById(R.id.confirmBtn);
confirmBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
try {
alertDialog.dismiss();
downPrBar.setVisibility(View.VISIBLE);
new Thread() {
@Override
public void run() {
super.run();
try {
download(url, 3);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
} catch (Exception e) {
e.printStackTrace();
}
}
});
Button cancelBtn = (Button) view.findViewById(R.id.cancelBtn);
cancelBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
alertDialog.dismiss();
}
});
}
/**
* 从路径中获取文件名称
*
* @param path 下载路径
* @return
*/
public static String getFilename(String path) {
return path.substring(path.lastIndexOf('/') + 1);
}
/**
* 下载文件
*
* @param path 下载路径
* @param threadsize 线程数
*/
public void download(String path, int threadsize) throws Exception {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5 * 1000);
sizeCount = conn.getContentLength();// 获取要下载的文件的长度
Log.d(tag, "总大小:" + sizeCount);
File saveFile = EnvironmentShare.getDownLoadFile(fileName);
RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");
accessFile.setLength(sizeCount);// 设置本地文件的长度和下载文件相同
accessFile.close();
// 计算每条线程下载的数据长度
int block = sizeCount % threadsize == 0 ? sizeCount / threadsize : sizeCount / threadsize
+ 1;
for (int threadid = 0; threadid < threadsize; threadid++) {
new DownloadThread(url, saveFile, block, threadid).start();
}
}
private final class DownloadThread extends Thread {
private URL url;
private File saveFile;
private int block;// 每条线程下载的数据长度
private int threadid;// 线程id
public DownloadThread(URL url, File saveFile, int block, int threadid) {
this.url = url;
this.saveFile = saveFile;
this.block = block;
this.threadid = threadid;
}
@Override
public void run() {
// 计算开始位置公式:线程id*每条线程下载的数据长度= ?
// 计算结束位置公式:(线程id +1)*每条线程下载的数据长度-1 =?
int startposition = threadid * block;
int endposition = (threadid + 1) * block - 1;
try {
RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");
accessFile.seek(startposition);// 设置从什么位置开始写入数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5 * 1000);
conn.setRequestProperty("Range", "bytes=" + startposition + "-" + endposition);
InputStream inStream = conn.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
int count = 0;
while ((len = inStream.read(buffer)) != -1) {
accessFile.write(buffer, 0, len);
count += len;
setDownSizes(len);
int downProcess = (int) (downedSizes * 100) / sizeCount;
if (downProcess > showProcess) {
showProcess = downProcess;
updateProgressBarHandler.sendEmptyMessage(Flag_UpadateProgressBar);
}
}
inStream.close();
accessFile.close();
Log.d(tag, "线程id:" + threadid + "下载完成,大小:" + count + "总大小" + downedSizes);
} catch (Exception e) {
e.printStackTrace();
updateProgressBarHandler.sendEmptyMessage(Flag_DownloadError);
Log.e(tag, "下载文件时异常", e);
}
}
}
/**
* @Description: 同步增加已下载长度,避免多线程操作导致增加异常
* @param len
*/
private synchronized void setDownSizes(int len) {
downedSizes += len;
}
/** 已下载长度 **/
private int downedSizes = 0;
/** 要下载文件总长度 **/
private int sizeCount;
/** 已显示长度 **/
private int showProcess = 0;
/** 更新进度条标识 **/
private final int Flag_UpadateProgressBar = 69;
/** 下载错误标识 **/
private final int Flag_DownloadError = 96;
private Handler updateProgressBarHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case Flag_UpadateProgressBar:
downPrBar.setProgress(showProcess);
loadProcessNumber.setText(fileName + "已下载:" + showProcess + "%");
if (showProcess == 100) {
finish();
Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
}
break;
case Flag_DownloadError:
Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
finish();
break;
default:
break;
}
};
};
}
1. 预算每个线程下载的字节大小,能整除去商,不能整除则为商+1,这样最后一条线程以外的线程会下载的比平均值多1个字节,但是最后一个线程会下载剩余(小于平均值)的长度,详细见下段代码:
// 计算每条线程下载的数据长度
int block = sizeCount % threadsize == 0 ? sizeCount / threadsize : sizeCount / threadsize + 1;
2. 获取每条线程实际下载的大小:
// 计算开始位置公式:线程id*每条线程下载的数据长度= ?
// 计算结束位置公式:(线程id +1)*每条线程下载的数据长度-1 =?
int startposition = threadid * block;
int endposition = (threadid + 1) * block - 1;
try {
RandomAccessFile accessFile = new RandomAccessFile(saveFile, "rwd");
accessFile.seek(startposition);// 设置从什么位置开始写入数据
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5 * 1000);
conn.setRequestProperty("Range", "bytes=" + startposition + "-" + endposition);
如果文件总大小和线程总数的商不能整除,那么上一步预算的block会比平均值大,
conn.setRequestProperty("Range", "bytes=" + startposition + "-" + endposition);
这段代码会返回该线程实际下载的大小,这样所有线程下载的总长度就和实际文件长度相等,保证文件的完整性。
3. 计算已下载的总长度:
/**
* @Description: 同步增加已下载长度,避免多线程操作导致增加异常
* @param len
*/
private synchronized void setDownSizes(int len) {
downedSizes += len;
}
如果不用同步锁的话,因为多线程操作,已下载总长度增加时长度会丢失。
4. 下载进度大于已显示进度时再更新UI:
int downProcess = (int) (downedSizes * 100) / sizeCount;
if (downProcess > showProcess) {
showProcess = downProcess;
updateProgressBarHandler.sendEmptyMessage(Flag_UpadateProgressBar);
}
这样,避免了UI被多次无用刷新。
测试Project代码下载:http://download.csdn.net/detail/lufengdie/7487821
其他参考地址:http://blog.csdn.net/wanglong0537/article/details/6411565
http://gundumw100.iteye.com/blog/906072
http://www.cnblogs.com/hanyonglu/archive/2012/02/20/2358801.html