Android多线程 断点下载文件

原理十分简单:

1、利用HttpConnection链接地址获取文件大小

2、创建空文件大小与下载文件一致

3、分割文件,指定每个线程下载的起止位置(byte数组的下标)

4、开启线程进行下载,实时记录下载的字节数用以断点续传

5、开启下载时读取下载记录文件获取记录,用以更新下载的开始位置

注:该实例代码包含progressbar基本用法,进度条和进度对话框在非UI线程中也可更新,为特例

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/widget32"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:hint="请输入地址"
        android:text="http://192.168.1.100:8080/test.exe"
        android:textSize="18sp" />

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="download"
        android:text="点击下载" />

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_percent"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="进度:" />

</LinearLayout>

主Activity:

package com.example.severalthread;

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

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity
{
	private String TAG = "MAIN";
	private EditText et_path;
	private TextView tv_percent;
	public static ProgressBar pb;
	private static int runningThread = 3;
	static final int threadCount = 3;
	public static int currentPercent = 0;

	private static double filelen=0;
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		et_path = (EditText) findViewById(R.id.et);
		pb = (ProgressBar) findViewById(R.id.progressBar1);
		tv_percent=(TextView) findViewById(R.id.tv_percent);
		
	}

	@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 view) throws Exception
	{
		// 链接服务器 获取文件长度 在本地创建大小一致的临时文件
		final String path = et_path.getText().toString();

		if (TextUtils.isEmpty(path))
		{
			Toast.makeText(this, "请输入地址", Toast.LENGTH_SHORT).show();
			return;
		} else
		{
			new Thread()
			{
				@Override
				public void run()
				{

					try
					{
						URL url = new URL(path);
						HttpURLConnection conn = (HttpURLConnection) url.openConnection();
						conn.setConnectTimeout(5000);
						conn.setRequestMethod("GET");
						if (200 == conn.getResponseCode())
						{
							// 文件长度
							int len = conn.getContentLength();
							filelen=len;
							// 进度条设置最大值
							pb.setMax(len);

							Log.i(TAG, "文件长度=" + len);
							try
							{
								// 客户端创建一样大小的临时文件使用 RandomAccessFile
								RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/setup.exe", "rwd");
								raf.setLength(len);
								raf.close();

								// 设定为三个线程
								// 平均每个线程下载的大小
								int blockSize = len / threadCount;
								for (int threadid = 1; threadid <= threadCount; threadid++)
								{
									int startIndex = (threadid - 1) * blockSize;
									int endIndex = threadid * blockSize - 1;
									if (threadid == threadCount)
									{
										// 最后一个线程下载到末尾
										endIndex = len;
									}
									Log.i(TAG, "线程" + threadid + ",开始" + startIndex + "结束" + endIndex);
									new DownloadThread(threadid, startIndex, endIndex, path).start();
								}
							} catch (Exception e)
							{
								Toast.makeText(MainActivity.this, "服务器错误", Toast.LENGTH_SHORT).show();
							}
						}

					} catch (Exception e1)
					{
						e1.printStackTrace();
					}

				};

			}.start();

		}
	}

	public class DownloadThread extends Thread
	{
		private int threadid;
		private int startindex;
		private int endindex;
		private String path;

		public DownloadThread(int id, int start, int end, String path)
		{
			this.threadid = id;
			this.startindex = start;
			this.endindex = end;
			this.path = path;
		}

		@Override
		public void run()
		{
			String downloadInfoPath = Environment.getExternalStorageDirectory().getPath() + "/";
			try
			{
				// 检查是否存在记录下载长度文件
				File temFile = new File(downloadInfoPath + threadid + ".txt");
				int downloadlen = 0;
				if (temFile.exists() && temFile.length() > 0)
				{
					FileInputStream fis = new FileInputStream(downloadInfoPath + threadid + ".txt");
					byte[] temp = new byte[1024];
					int leng = fis.read(temp);
					String endedlength = new String(temp, 0, leng);
					downloadlen = Integer.parseInt(endedlength);
					currentPercent+=downloadlen;
					startindex += downloadlen;
					System.out.println("更新为" + startindex);
					fis.close();
				}

				HttpClient client = new DefaultHttpClient();
				HttpGet httpGet = new HttpGet(path);
				httpGet.setHeader("Range", "bytes=" + startindex + "-" + endindex);
				HttpResponse response = client.execute(httpGet);
				int code = response.getStatusLine().getStatusCode();
				if (206 == code)
				{
					InputStream is = response.getEntity().getContent();
					RandomAccessFile raf = new RandomAccessFile(downloadInfoPath + "setup.exe", "rwd");
					// 定位随机写文件时候在那个位置开始写
					raf.seek(startindex);

					int len = 0;
					byte[] buff = new byte[1024];
					int total = downloadlen;// 已经下载的数据长度 用于断点续传

					while ((len = is.read(buff)) != -1)
					{
						RandomAccessFile info = new RandomAccessFile(downloadInfoPath + threadid + ".txt", "rwd");
						raf.write(buff, 0, len);
						total += len;
						info.write(String.valueOf(total).getBytes());
						info.close();
						synchronized (MainActivity.this)
						{
							currentPercent += len;
							//进度条和进度条对话框特殊 可直接在UI线程外更新UI
							pb.setProgress(currentPercent);
							runOnUiThread(new Runnable()    
						    {    
						        public void run()    
						        {    
						        	tv_percent.setText("当前进度为:"+String.format("%.2f" ,currentPercent/filelen*100)+"%");  
						        }    
						
						    });
							
							System.out.println("total="+currentPercent);
						}

					}
					is.close();
					raf.close();
					System.out.println("线程" + threadid + "结束!Code=" + code);
				}

			} catch (Exception e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally
			{
				checkEnd(downloadInfoPath);
			}
		}

		private synchronized void checkEnd(String downloadInfoPath)
		{
			runningThread--;
			if (runningThread == 0)
			{
				for (int i = 1; i <= threadCount; i++)
				{// 需要在整个文件下载完成后在删除
					File deleteFile = new File(downloadInfoPath + i + ".txt");
					deleteFile.delete();
				}
				System.out.println("文件下载完毕");
				
				runOnUiThread(new Runnable()    
			    {    
			        public void run()    
			        {    
			            Toast.makeText(getApplicationContext(), "下载完成",    
			                    Toast.LENGTH_LONG).show();    
			        }    
			
			    });
			}
		}
	}
}

 

你可能感兴趣的:(多线程,android,下载,断点续传,进度条)