转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46883927
说到Android中的文件下载,Android API中明确要求将耗时的操作放到一个子线程中执行,文件的下载无疑是需要耗费时间的,所以要将文件的下载放到子线程中执行。下面,我们一起来实现一个Android中利用多线程下载文件的小例子。
在这个小例子中我以下载有道词典为例,在网上下载有道词典的安装包,在eclipse中新建项目web,将下载的有道词典安装包放置在WebContent目录下,并将项目发布到Tomcat中,具体如下图所示
界面上自上而下放置一个TextView,用来提示文本框中输入的信息,一个文本框用来输入网络中下载文件的路径,一个Button按钮,点击下载文件,一个ProgressBar显示下载进度,一个TextView显示下载的百分比。具体布局内容如下:
新建自定义ProgressBarListener监听器接口,这个接口中定义两个方法,void getMax(int length)用来获取下载文件的长度,void getDownload(int length);用来获取每次下载的长度,这个方法中主要是在多线程中调用,子线程中获取到的数据传递到这两个接口方法中,然后在这两个接口方法中通过Handler将相应的长度信息传递到主线程,更新界面显示信息,具体代码实现如下:
package com.example.inter;
/**
* 自定义进度条监听器
* @author liuyazhuang
*
*/
public interface ProgressBarListener {
/**
* 获取文件的长度
* @param length
*/
void getMax(int length);
/**
* 获取每次下载的长度
* @param length
*/
void getDownload(int length);
}
这里通过继承Thread的方式来实现自定义线程操作,在这个类中主要是实现文件的下载操作,在这个类中,定义了一系列与下载有关的实例变量来控制下载的数据,同时通过自定义监听器ProgressBarListener中的void getDownload(int length)方法来跟新界面显示的进度信息。
具体实现如下:
package com.example.download;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import com.example.inter.ProgressBarListener;
/**
* 自定义线程类
* @author liuyazhuang
*
*/
public class DownloadThread extends Thread {
//下载的线程id
private int threadId;
//下载的文件路径
private String path;
//保存的文件
private File file;
//下载的进度条更新的监听器
private ProgressBarListener listener;
//每条线程下载的数据量
private int block;
//下载的开始位置
private int startPosition;
//下载的结束位置
private int endPosition;
public DownloadThread(int threadId, String path, File file, ProgressBarListener listener, int block) {
this.threadId = threadId;
this.path = path;
this.file = file;
this.listener = listener;
this.block = block;
this.startPosition = threadId * block;
this.endPosition = (threadId + 1) * block - 1;
}
@Override
public void run() {
super.run();
try {
//创建RandomAccessFile对象
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
//跳转到开始位置
accessFile.seek(startPosition);
URL url = new URL(path);
//打开http链接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置超时时间
conn.setConnectTimeout(5000);
//指定请求方式为GET方式
conn.setRequestMethod("GET");
//指定下载的位置
conn.setRequestProperty("Range", "bytes="+startPosition + "-" + endPosition);
//不用再去判断状态码是否为200
InputStream in = conn.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
while((len = in.read(buffer)) != -1){
accessFile.write(buffer, 0, len);
//更新下载进度
listener.getDownload(len);
}
accessFile.close();
in.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
这个类主要是对下载过程的管理,包括下载设置下载后文件要保存的位置,计算多线程中每个线程的数据下载量等等。
具体实现如下:
package com.example.download;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.Environment;
import com.example.inter.ProgressBarListener;
/**
* 文件下载管理器
* @author liuyazhuang
*
*/
public class DownloadManager {
//下载线程的数量
private static final int TREAD_SIZE = 3;
private File file;
/**
* 下载文件的方法
* @param path:下载文件的路径
* @param listener:自定义的下载文件监听接口
* @throws Exception
*/
public void download(String path, ProgressBarListener listener) throws Exception{
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if(conn.getResponseCode() == 200){
int filesize = conn.getContentLength();
//设置进度条的最大长度
listener.getMax(filesize);
//创建一个和服务器大小一样的文件
file = new File(Environment.getExternalStorageDirectory(), this.getFileName(path));
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
accessFile.setLength(filesize);
//要关闭RandomAccessFile对象
accessFile.close();
//计算出每条线程下载的数据量
int block = filesize % TREAD_SIZE == 0 ? (filesize / TREAD_SIZE) : (filesize / TREAD_SIZE +1 );
//开启线程下载
for(int i = 0; i < TREAD_SIZE; i++){
new DownloadThread(i, path, file, listener, block).start();
}
}
}
/**
* 截取路径中的文件名称
* @param path:要截取文件名称的路径
* @return:截取到的文件名称
*/
private String getFileName(String path){
return path.substring(path.lastIndexOf("/") + 1);
}
}
在这个类中首先,找到页面中的各个控件,实现Button按钮的onClick事件,在onClick事件中开启一个线程进行下载操作,同时子线程中获取到的数据,通过handler与Message机制传递到主线程,更新界面显示。
具体实现如下:
package com.example.multi;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.example.download.DownloadManager;
import com.example.inter.ProgressBarListener;
/**
* MainActivity整个应用程序的入口
* @author liuyazhuang
*
*/
public class MainActivity extends Activity {
protected static final int ERROR_DOWNLOAD = 0;
protected static final int SET_PROGRESS_MAX = 1;
protected static final int UPDATE_PROGRESS = 2;
private EditText ed_path;
private ProgressBar pb;
private TextView tv_info;
private DownloadManager manager;
//handler操作
private Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case ERROR_DOWNLOAD:
//提示用户下载失败
Toast.makeText(MainActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
break;
case SET_PROGRESS_MAX:
//得到最大值
int max = (Integer) msg.obj;
//设置进度条的最大值
pb.setMax(max);
break;
case UPDATE_PROGRESS:
//获取当前下载的长度
int currentprogress = pb.getProgress();
//获取新下载的长度
int len = (Integer) msg.obj;
//计算当前总下载长度
int crrrentTotalProgress = currentprogress + len;
pb.setProgress(crrrentTotalProgress);
//获取总大小
int maxProgress = pb.getMax();
//计算百分比
float value = (float)currentprogress / (float)maxProgress;
int percent = (int) (value * 100);
//显示下载的百分比
tv_info.setText("下载:"+percent+"%");
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.ed_path = (EditText) super.findViewById(R.id.ed_path);
this.pb = (ProgressBar) super.findViewById(R.id.pb);
this.tv_info = (TextView) super.findViewById(R.id.tv_info);
this.manager = new DownloadManager();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void download(View v){
final String path = ed_path.getText().toString();
//下载
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try {
manager.download(path, new ProgressBarListener() {
@Override
public void getMax(int length) {
// TODO Auto-generated method stub
Message message = new Message();
message.what = SET_PROGRESS_MAX;
message.obj = length;
mHandler.sendMessage(message);
}
@Override
public void getDownload(int length) {
// TODO Auto-generated method stub
Message message = new Message();
message.what = UPDATE_PROGRESS;
message.obj = length;
mHandler.sendMessage(message);
}
});
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
Message message = new Message();
message.what = ERROR_DOWNLOAD;
mHandler.sendMessage(message);
}
}
}).start();
}
}
最后,别忘了给应用授权,这里要用到Android联网授权和向SD卡中写入文件的权限。
具体实现如下:
提醒:大家可以到http://download.csdn.net/detail/l1028386804/8899957 链接来获取完整的代码示例