原理十分簡單: 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(); } }); } } } }