Android通過HTTP協議實現斷點續傳下載

jopen 11年前發布 | 76K 次閱讀 Android Android開發 移動開發

 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()){
       data.put(cursor.getInt(0), cursor.getInt(1));
      
      } cursor.close(); db.close(); return data; } /**
    • 保存每條線程已經下載的文件長度
    • @param path
    • @param map */ public void save(String path, Map<Integer, Integer> map){//int threadid, int position SQLiteDatabase db = openHelper.getWritableDatabase(); db.beginTransaction(); try{
       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();
      
      }finally{
       db.endTransaction();
      
      } db.close(); } /**
    • 實時更新每條線程已經下載的文件長度
    • @param path
    • @param map */ public void update(String path, Map<Integer, Integer> map){ SQLiteDatabase db = openHelper.getWritableDatabase(); db.beginTransaction(); try{
       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();
      
      }finally{
       db.endTransaction();
      
      } db.close(); } /**
    • 當文件下載完成后,刪除對應的下載記錄
    • @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>

項目源碼下載

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!