Android通過HTTP協議實現斷點續傳下載
FileDownloader.java package cn.itcast.net.download;import java.io.File; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import cn.itcast.service.FileService;
import android.content.Context; import android.util.Log; /**
import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL;
import android.util.Log;
public class DownloadThread extends Thread {
private static final String TAG = "DownloadThread";
private File saveFile;
private URL downUrl;
private int block;
/ 下載開始位置 /
private int threadId = -1;
private int downLength;
private boolean finish = false;
private FileDownloader downloader;
public DownloadThread(FileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) {
this.downUrl = downUrl;
this.saveFile = saveFile;
this.block = block;
this.downloader = downloader;
this.threadId = threadId;
this.downLength = downLength;
}
@Override
public void run() {
if(downLength < block){//未下載完成
try {
HttpURLConnection http = (HttpURLConnection) downUrl.openConnection();
http.setConnectTimeout(5 * 1000);
http.setRequestMethod("GET");
http.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
http.setRequestProperty("Accept-Language", "zh-CN");
http.setRequestProperty("Referer", downUrl.toString());
http.setRequestProperty("Charset", "UTF-8");
int startPos = block * (threadId - 1) + downLength;//開始位置
int endPos = block * threadId -1;//結束位置
http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//設置獲取實體數據的范圍
http.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
http.setRequestProperty("Connection", "Keep-Alive");
InputStream inStream = http.getInputStream();
byte[] buffer = new byte[1024];
int offset = 0;
print("Thread " + this.threadId + " start download from position "+ startPos);
RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd");
threadfile.seek(startPos);
while ((offset = inStream.read(buffer, 0, 1024)) != -1) {
threadfile.write(buffer, 0, offset);
downLength += offset;
downloader.update(this.threadId, downLength);
downloader.append(offset);
}
threadfile.close();
inStream.close();
print("Thread " + this.threadId + " download finish");
this.finish = true;
} catch (Exception e) {
this.downLength = -1;
print("Thread "+ this.threadId+ ":"+ e);
}
}
}
private static void print(String msg){
Log.i(TAG, msg);
}
/**
* 下載是否完成
* @return
*/
public boolean isFinish() {
return finish;
}
/**
* 已經下載的內容大小
* @return 如果返回值為-1,代表下載失敗
*/
public long getDownLength() {
return downLength;
}
}</pre>
DownloadProgressListener.java
package cn.itcast.net.download;public interface DownloadProgressListener { public void onDownloadSize(int size); }</pre>
DBOpenHelper.java
package cn.itcast.service;import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper;
public class DBOpenHelper extends SQLiteOpenHelper { private static final String DBNAME = "itcast.db"; private static final int VERSION = 1;
public DBOpenHelper(Context context) { super(context, DBNAME, null, VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS filedownlog (id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, downlength INTEGER)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS filedownlog"); onCreate(db); }
}</pre>
FileService.java
package cn.itcast.service;import java.util.HashMap; import java.util.Map;
import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; /**
業務bean / public class FileService { private DBOpenHelper openHelper;
public FileService(Context context) {
openHelper = new DBOpenHelper(context);
} /**
- 獲取每條線程已經下載的文件長度
- @param path
- @return
*/
public Map<Integer, Integer> getData(String path){
SQLiteDatabase db = openHelper.getReadableDatabase();
Cursor cursor = db.rawQuery("select threadid, downlength from filedownlog where downpath=?", new String[]{path});
Map<Integer, Integer> data = new HashMap<Integer, Integer>();
while(cursor.moveToNext()){
} cursor.close(); db.close(); return data; } /**data.put(cursor.getInt(0), cursor.getInt(1));
- 保存每條線程已經下載的文件長度
- @param path
- @param map
*/
public void save(String path, Map<Integer, Integer> map){//int threadid, int position
SQLiteDatabase db = openHelper.getWritableDatabase();
db.beginTransaction();
try{
}finally{for(Map.Entry<Integer, Integer> entry : map.entrySet()){ db.execSQL("insert into filedownlog(downpath, threadid, downlength) values(?,?,?)", new Object[]{path, entry.getKey(), entry.getValue()}); } db.setTransactionSuccessful();
} db.close(); } /**db.endTransaction();
- 實時更新每條線程已經下載的文件長度
- @param path
- @param map
*/
public void update(String path, Map<Integer, Integer> map){
SQLiteDatabase db = openHelper.getWritableDatabase();
db.beginTransaction();
try{
}finally{for(Map.Entry<Integer, Integer> entry : map.entrySet()){ db.execSQL("update filedownlog set downlength=? where downpath=? and threadid=?", new Object[]{entry.getValue(), path, entry.getKey()}); } db.setTransactionSuccessful();
} db.close(); } /**db.endTransaction();
- 當文件下載完成后,刪除對應的下載記錄
- @param path */ public void delete(String path){ SQLiteDatabase db = openHelper.getWritableDatabase(); db.execSQL("delete from filedownlog where downpath=?", new Object[]{path}); db.close(); }
}</pre>
DownloadActivity.java
package cn.itcast.download;import java.io.File;
import cn.itcast.net.download.DownloadProgressListener; import cn.itcast.net.download.FileDownloader;
import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast;
public class DownloadActivity extends Activity { private EditText downloadpathText; private TextView resultView; private ProgressBar progressBar; //當Handler被創建會關聯到創建它的當前線程的消息隊列,該類用于往消息隊列發送消息 //消息隊列中的消息由當前線程內部進行處理 private Handler handler = new Handler(){
@Override public void handleMessage(Message msg) { switch (msg.what) { case 1: progressBar.setProgress(msg.getData().getInt("size")); float num = (float)progressBar.getProgress()/(float)progressBar.getMax(); int result = (int)(num*100); resultView.setText(result+ "%"); if(progressBar.getProgress()==progressBar.getMax()){ Toast.makeText(DownloadActivity.this, R.string.success, 1).show(); } break; case -1: Toast.makeText(DownloadActivity.this, R.string.error, 1).show(); break; } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); downloadpathText = (EditText) this.findViewById(R.id.downloadpath); progressBar = (ProgressBar) this.findViewById(R.id.downloadbar); resultView = (TextView) this.findViewById(R.id.result); Button button = (Button) this.findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String path = downloadpathText.getText().toString(); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ download(path, Environment.getExternalStorageDirectory()); }else{ Toast.makeText(DownloadActivity.this, R.string.sdcarderror, 1).show(); } } }); } //主線程(UI線程) //業務邏輯正確,但是該程序運行的時候有問題 //對于顯示控件的界面更新只是由UI線程負責,如果是在非UI線程更新控件的屬性值,更新后的顯示界面不會反映到屏幕上 private void download(final String path, final File savedir) { new Thread(new Runnable() { @Override public void run() { FileDownloader loader = new FileDownloader(DownloadActivity.this, path, savedir, 3); progressBar.setMax(loader.getFileSize());//設置進度條的最大刻度為文件的長度 try { loader.download(new DownloadProgressListener() { @Override public void onDownloadSize(int size) {//實時獲知文件已經下載的數據長度 Message msg = new Message(); msg.what = 1; msg.getData().putInt("size", size); handler.sendMessage(msg);//發送消息 } }); } catch (Exception e) { handler.obtainMessage(-1).sendToTarget(); } } }).start(); }
}</pre>
項目源碼下載