Android多線程 斷點下載文件

原理十分簡單: java

一、利用HttpConnection連接地址獲取文件大小 android

二、建立空文件大小與下載文件一致 apache

三、分割文件,指定每一個線程下載的起止位置(byte數組的下標) 數組

四、開啓線程進行下載,實時記錄下載的字節數用以斷點續傳 服務器

五、開啓下載時讀取下載記錄文件獲取記錄,用以更新下載的開始位置 app

注:該實例代碼包含progressbar基本用法,進度條和進度對話框在非UI線程中也可更新,爲特例 dom

佈局文件: ide

<?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();    
			        }    
			
			    });
			}
		}
	}
}
相關文章
相關標籤/搜索