实现多线程下载的大概思路:(假设文件大小是6M,共有三条线程同时执行)
●首先要根据访问的URL路径去调用openConnection方法,得到HttpUrlConnection对象,HttpUrlConnection对象调用它的方法得到下载文件的长度,然后设置本地文件的长度。
点击(此处)折叠或打开
- int length = HttpURLConnection.getContentLength();
- RandomAccessFile file = new RandomAccessFile("calendar_setup.exe","rw");
- file.setLength(length);
RandomAccessFile同时将FileInputStream和FileOutputStream整合到一起,而且支持将从文件任意字节处都或写数据;File类只是将文件作为整体来处理文件,不能读写文件
●根据文件长度和线程数计算每条线程下载的数据长度和下载位置,如文件长度为6M,线程数为3,那么每条线程下载数据的长度为2M,

问题:在下载时如何指定这些线程开始下载的位置?→HTTP协议
●使用HTTP的Range头字段指定每条线程从文件的什么位置开始下载,如指定从文件的2M位置开始下载:
点击(此处)折叠或打开
- HttpURLConnection.setRequestProperty("Range","bytes=2097152-");
设置请求头Range字段,就是bytes=2097152,2MB的字节,比如上图的线程2,在下载的过程中只要设置了这个头,那么它就会从线程1和线程3开始下载
点击(此处)折叠或打开
- HttpURLConnection.setRequestProperty("Range","bytes=2097152-");
●保存文件,使用RandomAccessFile类指定每条线程从本地的什么位置开始写入数据
点击(此处)折叠或打开
- RandomAccessFile file = new RandomAccessFile("calendar_setup.exe","rw");
- file.seek(2097152);
小案例:
●编写布局文件,主要有一个进度条,一个Button,一个EditText
点击(此处)折叠或打开
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="下载"
- />
- <EditText
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:hint="请输入下载路径"
- android:text="http://file1.updrv.com/soft/rili/calendar_setup.exe"
- android:id = "@+id/et_path"/>
- <ProgressBar
- android:id="@+id/bar"
- style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
- <TextView
- android:id="@+id/tv_process"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
- <Button
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="开始下载"
- android:id="@+id/bt"/>
- </LinearLayout>
●编写activity文件,
点击(此处)折叠或打开
- public class DownLoadActivity extends Activity implements OnClickListener {
- //定义相关控件
- ProgressBar bar;
- Button bt;
- TextView tv ;
- EditText et_text;
- boolean flag = true;
- boolean stopflag = false;
- Handler handler = new Handler(){
-
- @Override
- public void handleMessage(Message msg) {
- bar.setProgress(total);
- int max = bar.getMax();
- if (total>=(max-1)) {
- total = max;
- flag = false;
- }
- int result = total*100/max;
- tv.setText("当前进度:"+result+"%");
- super.handleMessage(msg);
- }
-
- };
- int total = 0;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- //初始化相关控件
- bar = (ProgressBar) this.findViewById(R.id.bar);
- bt = (Button)this.findViewById(R.id.bt);
- tv = (TextView)this.findViewById(R.id.tv_process);
- et_text=(EditText)this.findViewById(R.id.et_path);
- bt.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- switch (v.getId()) {
- case R.id.bt:
- if ("开始下载".equals(bt.getText().toString().trim())) {
- bt.setText("暂停");
- stopflag = false;
- }else {
- bt.setText("开始下载");
- stopflag=true;
- }
- new Thread(){
-
- @Override
- public void run() {
- while (flag) {
- try {
- sleep(1000);
- Message message = new Message();
- //通知更新UI
- handler.sendMessage(message);
- } catch (Exception e) {
- // TODO: handle exception
- }
- }
- super.run();
- }
-
- }.start();
- //得到下载路径
- String path = et_text.getText().toString().trim();
- if ("".equals(path)) {
- Toast.makeText(this, "路径不能为空", 0).show();
- return;
- }
- try {
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- //设置请求的相关参数
- conn.setRequestMethod("GET");
- conn.setConnectTimeout(5000);
- conn.setRequestProperty("User-Agent",
- "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
- int code = conn.getResponseCode();
- if (code ==200) {
- int length = conn.getContentLength();
- RandomAccessFile file = new RandomAccessFile("/mnt/sdcard/" + getFileName(path), "rwd");
- //设置文件长度
- file.setLength(length);
- //设置进度条最大值
- bar.setMax(length);
- //建立3条线程下载数据
- int threadNO = 3;
- //每条线程下载数据的大小
- int blocksize = length/threadNO;
-
- for (int i = 0; i <threadNO; i++) {
- int startposition = i*blocksize;//每条线程下载的开始位置
- int endposition = (i+1)*blocksize;//每条线程下载的结束位置
- if (i==(threadNO-1)) {
- endposition = length;//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度
- }
- DownLoadTask task = new DownLoadTask(i,path,startposition,endposition);
- task.start();
- }
- }
- } catch (Exception e) {
- // TODO: handle exception
- Toast.makeText(this,"下载出现异常",0).show();
- }
-
- break;
- }
- }
- class DownLoadTask extends Thread{
- int threadid;
- String path;
- int startposition;
- int endposition;
- public DownLoadTask(int threadid, String path, int startposition,
- int endposition) {
- this.threadid = threadid;
- this.path = path;
- this.startposition = startposition;
- this.endposition = endposition;
- }
- @Override
- public void run() {
- // TODO Auto-generated method stub
- try {
- //为每个线程创建一个文件用于记录暂停下载时当时已下载的数据大小
- File postionfile = new File("/mnt/sdcard/" + threadid + ".txt");
- URL url = new URL(path);
- HttpURLConnection conn = (HttpURLConnection) url.openConnection();
- System.out.println("线程" + threadid + "正在下载 " + "开始位置 : "
- + startposition + "结束位置 " + endposition);
- if (postionfile.exists()) {
- FileInputStream is = new FileInputStream(postionfile);
- //处理流信息得到以下载的字节数大小
- byte[] result = StreamTool.getByte(is);
- String str = new String(result);
- if (!"".equals(str)) {
- //如果postionfile中有记录,则改变开始下载的位置
- int newstartposition = Integer.parseInt(str);
- if (newstartposition>startposition) {
- startposition = newstartposition;
- }
- }
- }
-
- conn.setRequestProperty("Range", "bytes=" + startposition + "-"
- + endposition);
- conn.setRequestMethod("GET");
- conn.setConnectTimeout(5000);
- conn.setRequestProperty("User-Agent",
- "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
- InputStream is = conn.getInputStream();
- RandomAccessFile file = new RandomAccessFile("/mnt/sdcard/"
- + getFileName(path), "rwd");
- file.seek(startposition);
- byte[] buffer = new byte[1024];
- int len = 0;
- int currentPosition = startposition;
- while ((len = is.read(buffer))!=-1) {
- if (stopflag) {
- return;
- }
- file.write(buffer, 0, len);
- synchronized (DownLoadActivity.this) {
- total+=len;
- }
- //记录当前线程以下载的数据
- currentPosition+=len;
- String position = currentPosition+"";
- FileOutputStream out = new FileOutputStream(postionfile);
- //把记录写入文件
- out.write(position.getBytes());
- out.flush();
- out.close();
-
- }
- file.close();
- System.out.println("线程"+threadid+"下载完毕");
- if (postionfile.exists()) {
- postionfile.delete();
- }
- } catch (Exception e) {
- }
- super.run();
- }
- }
- public String getFileName(String path){
- int start = path.lastIndexOf("/")+1;
- return path.substring(start, path.length());
- }
- }
●最后一个就是一个处理输入流的工具类,返回字节数:
点击(此处)折叠或打开
- public class StreamTool {
- public static byte[] getByte(InputStream is) throws Exception{
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- int len = 0;
- byte[] b = new byte[1024];
- while ((len = is.read(b))!=-1) {
- out.write(b, 0, len);
- }
- is.close();
- out.flush();
- return out.toByteArray();
- }
- }
运行后看到^0^

暂停后:
