歡迎訪問個人博客:www.yyxxk.com
android多線程斷點下載,帶進度條和百分比顯示,斷點下載的臨時數據保存到SD卡的文本文檔中,建議可以保存到本地數據庫中,這樣可以提高存取效率,從而提高系統性能。
效果:
打開軟件:
下載中:
下載完畢:
附代碼如下:
1 package com.yy.multiDownloadOfBreakPoint; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.InputStream; 6 import java.io.RandomAccessFile; 7 import java.net.HttpURLConnection; 8 import java.net.URL; 9 10 import android.app.Activity; 11 import android.os.Bundle; 12 import android.os.Handler; 13 import android.os.Message; 14 import android.text.TextUtils; 15 import android.view.View; 16 import android.widget.EditText; 17 import android.widget.ProgressBar; 18 import android.widget.TextView; 19 import android.widget.Toast; 20 /** 21 * 多線程斷點下載實例 22 * @author YUANYUAN 23 * 24 */ 25 public class MainActivity extends Activity { 26 //下載所使用的線程數 27 protected static final int threadCount = 3; 28 //下載完畢的標記 29 public static final int downloadOver = 1; 30 //更新下載進度標記 31 public static final int UPDATE_PROGRESS = 0; 32 //下載資源的路徑輸入框 33 private EditText et_path; 34 //下載的進度條 35 private ProgressBar pb; 36 //進度顯示 37 private TextView tv_pb; 38 //當前累計下載的數據 39 int curDownCount=0; 40 //當前活動的下載線程數 41 protected static int activeThread; 42 //加入消息處理機制 43 private Handler handler=new Handler(){ 44 @Override 45 public void handleMessage(Message msg) { 46 switch (msg.what) { 47 case downloadOver: 48 Toast.makeText(MainActivity.this, "文件已下載完畢!", Toast.LENGTH_LONG).show(); 49 tv_pb.setText("下載完成"); 50 break; 51 case UPDATE_PROGRESS: 52 //更新進度顯示 53 tv_pb.setText("當前進度:"+(pb.getProgress()*100/pb.getMax())+"%"); 54 break; 55 default: 56 break; 57 } 58 } 59 }; 60 61 @Override 62 protected void onCreate(Bundle savedInstanceState) { 63 super.onCreate(savedInstanceState); 64 setContentView(R.layout.activity_main); 65 et_path=(EditText) findViewById(R.id.et_path); 66 et_path.setText("http://192.168.2.114:8080/sqlite.exe"); 67 pb=(ProgressBar) findViewById(R.id.pb); 68 tv_pb=(TextView) findViewById(R.id.tv_pb); 69 } 70 71 /** 72 * 開始下載 73 * @param view 74 */ 75 public void down(View view){ 76 //獲取下載資源的路徑 77 final String path=et_path.getText().toString().trim(); 78 //判斷資源路徑是否為空 79 if (TextUtils.isEmpty(path)) { 80 Toast.makeText(this, "請輸入下載資源的路徑", Toast.LENGTH_LONG).show(); 81 return; 82 } 83 //開啟一個線程進行下載 84 new Thread(){ 85 public void run() { 86 try { 87 //構造URL地址 88 URL url=new URL(path); 89 //打開連接 90 HttpURLConnection conn=(HttpURLConnection) url.openConnection(); 91 //設置請求超時的時間 92 conn.setConnectTimeout(5000); 93 //設置請求方式 94 conn.setRequestMethod("GET"); 95 //獲取相應碼 96 int code=conn.getResponseCode(); 97 if (code==200) {//請求成功 98 //獲取請求數據的長度 99 int length=conn.getContentLength(); 100 //設置進度條的最大值 101 pb.setMax(length); 102 //在客戶端創建一個跟服務器文件大小相同的臨時文件 103 RandomAccessFile raf=new RandomAccessFile("sdcard/setup.exe", "rwd"); 104 //指定臨時文件的長度 105 raf.setLength(length); 106 raf.close(); 107 //假設3個線程去下載資源 108 //平均每一個線程要下載的文件的大小 109 int blockSize=length/threadCount; 110 for (int threadId = 1; threadId <= threadCount; threadId++) { 111 //當前線程下載數據的開始位置 112 int startIndex=blockSize*(threadId-1); 113 //當前線程下載數據的結束位置 114 int endIndex=blockSize*threadId-1; 115 //確定最后一個線程要下載數據的最大位置 116 if (threadId==threadCount) { 117 endIndex=length; 118 } 119 //顯示下載數據的區間 120 System.out.println("線程【"+threadId+"】開始下載:"+startIndex+"---->"+endIndex); 121 //開啟下載的子線程 122 new DownloadThread(path, threadId, startIndex, endIndex).start(); 123 //當前下載活動的線程數加1 124 activeThread++; 125 System.out.println("當前活動的線程數:"+activeThread); 126 } 127 128 }else{//請求失敗 129 System.out.println("服務器異常,下載失敗!"); 130 } 131 } catch (Exception e) { 132 e.printStackTrace(); 133 System.out.println("服務器異常,下載失敗!"); 134 } 135 }; 136 }.start(); 137 138 } 139 /** 140 * 下載文件的子線程 每一個文件都下載對應的數據 141 * @author YUANYUAN 142 * 143 */ 144 public class DownloadThread extends Thread{ 145 private String path; 146 private int threadId; 147 private int startIndex; 148 private int endIndex; 149 150 /** 151 * 構造方法 152 * @param path 下載文件的路徑 153 * @param threadId 下載文件的線程 154 * @param startIndex 下載文件開始的位置 155 * @param endIndex 下載文件結束的位置 156 */ 157 public DownloadThread(String path, int threadId, int startIndex, 158 int endIndex) { 159 this.path = path; 160 this.threadId = threadId; 161 this.startIndex = startIndex; 162 this.endIndex = endIndex; 163 } 164 165 166 167 @Override 168 public void run() { 169 //構造URL地址 170 try { 171 172 File tempFile=new File("sdcard/"+threadId+".txt"); 173 //檢查記錄是否存在,如果存在讀取數據,設置真實下載開始的位置 174 if (tempFile.exists()) { 175 FileInputStream fis=new FileInputStream(tempFile); 176 byte[] temp=new byte[1024]; 177 int length=fis.read(temp); 178 //讀取到已經下載的位置 179 int downloadNewIndex=Integer.parseInt(new String(temp, 0, length)); 180 //計算出已經下載的數據長度 181 int areadyDown=downloadNewIndex-startIndex; 182 //累加已經下載的數據量 183 curDownCount+=areadyDown; 184 //設置進度條已經下載的數據量 185 pb.setProgress(curDownCount); 186 //設置重新開始下載的開始位置 187 startIndex=downloadNewIndex; 188 fis.close(); 189 //顯示真實下載數據的區間 190 System.out.println("線程【"+threadId+"】真實開始下載數據區間:"+startIndex+"---->"+endIndex); 191 } 192 193 URL url = new URL(path); 194 HttpURLConnection conn=(HttpURLConnection) url.openConnection(); 195 conn.setConnectTimeout(5000); 196 conn.setRequestMethod("GET"); 197 //設置請求屬性,請求部分資源 198 conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex); 199 int code=conn.getResponseCode(); 200 if (code==206) {//下載部分資源,正常返回的狀態碼為206 201 InputStream is=conn.getInputStream();//已經設置了請求的位置,所以返回的是對應的部分資源 202 //構建隨機訪問文件 203 RandomAccessFile raf=new RandomAccessFile("sdcard/setup.exe", "rwd"); 204 //設置 每一個線程隨機寫文件開始的位置 205 raf.seek(startIndex); 206 //開始寫文件 207 int len=0; 208 byte[] buffer=new byte[1024]; 209 //該線程已經下載數據的長度 210 int total=0; 211 212 while((len=is.read(buffer))!=-1){//讀取輸入流 213 //記錄當前線程已下載數據的長度 214 RandomAccessFile file=new RandomAccessFile("sdcard/"+threadId+".txt","rwd"); 215 raf.write(buffer,0,len);//寫文件 216 total+=len;//更新該線程已下載數據的總長度 217 System.out.println("線程【"+threadId+"】已下載數據:"+(total+startIndex)); 218 //將已下載數據的位置記錄寫入到文件 219 file.write((startIndex+total+"").getBytes()); 220 //累加已經下載的數據量 221 curDownCount+=len; 222 //更新進度條【進度條的更新可以在非UI線程直接更新,具體見底層源代碼】 223 pb.setProgress(curDownCount); 224 225 //更新下載進度 226 Message msg=Message.obtain(); 227 msg.what=UPDATE_PROGRESS; 228 handler.sendMessage(msg); 229 230 file.close(); 231 } 232 is.close(); 233 raf.close(); 234 //提示下載完畢 235 System.out.println("線程【"+threadId+"】下載完畢"); 236 } 237 } catch (Exception e) { 238 e.printStackTrace(); 239 System.out.println("線程【"+threadId+"】下載出現異常!!"); 240 }finally{ 241 //活動的線程數減少 242 activeThread--; 243 if (activeThread==0) { 244 for (int i = 1; i <= threadCount; i++) { 245 File tempFile=new File("sdcard/"+i+".txt"); 246 tempFile.delete(); 247 } 248 System.out.println("下載完畢,已清除全部臨時文件"); 249 //界面消息提示下載完畢 250 Message msg=new Message(); 251 msg.what=downloadOver; 252 handler.sendMessage(msg); 253 } 254 } 255 256 } 257 } 258 }