国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android 使用AsyncTask實現(xiàn)多線程斷點續(xù)傳

2019-10-22 18:10:24
字體:
供稿:網(wǎng)友

前面一篇博客《AsyncTask實現(xiàn)斷點續(xù)傳》講解了如何實現(xiàn)單線程下的斷點續(xù)傳,也就是一個文件只有一個線程進(jìn)行下載。

    對于大文件而言,使用多線程下載就會比單線程下載要快一些。多線程下載相比單線程下載要稍微復(fù)雜一點,本博文將詳細(xì)講解如何使用AsyncTask來實現(xiàn)多線程的斷點續(xù)傳下載。

一、實現(xiàn)原理

  多線程下載首先要通過每個文件總的下載線程數(shù)(我這里設(shè)定5個)來確定每個線程所負(fù)責(zé)下載的起止位置。

 long blockLength = mFileLength / DEFAULT_POOL_SIZE;  for (int i = 0; i < DEFAULT_POOL_SIZE; i++) {   long beginPosition = i * blockLength;//每條線程下載的開始位置   long endPosition = (i + 1) * blockLength;//每條線程下載的結(jié)束位置   if (i == (DEFAULT_POOL_SIZE - 1)) {    endPosition = mFileLength;//如果整個文件的大小不為線程個數(shù)的整數(shù)倍,則最后一個線程的結(jié)束位置即為文件的總長度   }   ......  }

  這里需要注意的是,文件大小往往不是線程個數(shù)的整數(shù)倍,所以最后一個線程的結(jié)束位置需要設(shè)置為文件長度。

  確定好每個線程的下載起止位置之后,需要設(shè)置http請求頭來下載文件的指定位置:

  //設(shè)置下載的數(shù)據(jù)位置beginPosition字節(jié)到endPosition字節(jié)  Header header_size = new BasicHeader("Range", "bytes=" + beginPosition + "-" + endPosition);  request.addHeader(header_size);

  以上是多線程下載的原理,但是還要實現(xiàn)斷點續(xù)傳需要在每次暫停之后記錄每個線程已下載的大小,下次繼續(xù)下載時從上次下載后的位置開始下載。一般項目中都會存數(shù)據(jù)庫中,我這里為了簡單起見直接存在了SharedPreferences中,已下載url和線程編號作為key值。

@Override  protected void onPostExecute(Long aLong) {   Log.i(TAG, "download success ");   //下載完成移除記錄   mSharedPreferences.edit().remove(currentThreadIndex).commit();  }  @Override  protected void onCancelled() {   Log.i(TAG, "download cancelled ");   //記錄已下載大小current   mSharedPreferences.edit().putLong(currentThreadIndex, current).commit();  }

下載的時候,首先獲取已下載位置,如果已經(jīng)下載過,就從上次下載后的位置開始下載:

 //獲取之前下載保存的信息,從之前結(jié)束的位置繼續(xù)下載  //這里加了判斷file.exists(),判斷是否被用戶刪除了,如果文件沒有下載完,但是已經(jīng)被用戶刪除了,則重新下載  long downedPosition = mSharedPreferences.getLong(currentThreadIndex, 0);  if(file.exists() && downedPosition != 0) {   beginPosition = beginPosition + downedPosition;   current = downedPosition;   synchronized (mCurrentLength) {    mCurrentLength += downedPosition;   }  }

二、完整代碼

package com.bbk.lling.multithreaddownload;import android.app.Activity;import android.content.Context;import android.content.SharedPreferences;import android.os.AsyncTask;import android.os.Bundle;import android.os.Environment;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.View;import android.widget.ProgressBar;import android.widget.TextView;import android.widget.Toast;import org.apache.http.Header;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 org.apache.http.message.BasicHeader;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.io.RandomAccessFile;import java.net.MalformedURLException;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Executor;import java.util.concurrent.Executors;public class MainActivity extends Activity { private static final String TAG = "MainActivity"; private static final int DEFAULT_POOL_SIZE = 5; private static final int GET_LENGTH_SUCCESS = 1; //下載路徑 private String downloadPath = Environment.getExternalStorageDirectory() +   File.separator + "download";// private String mUrl = "http://ftp.neu.edu.cn/mirrors/eclipse/technology/epp/downloads/release/juno/SR2/eclipse-java-juno-SR2-linux-gtk-x86_64.tar.gz"; private String mUrl = "http://p.gdown.baidu.com/c4cb746699b92c9b6565cc65aa2e086552651f73c5d0e634a51f028e32af6abf3d68079eeb75401c76c9bb301e5fb71c144a704cb1a2f527a2e8ca3d6fe561dc5eaf6538e5b3ab0699308d13fe0b711a817c88b0f85a01a248df82824ace3cd7f2832c7c19173236"; private ProgressBar mProgressBar; private TextView mPercentTV; SharedPreferences mSharedPreferences = null; long mFileLength = 0; Long mCurrentLength = 0L; private InnerHandler mHandler = new InnerHandler(); //創(chuàng)建線程池 private Executor mExecutor = Executors.newCachedThreadPool(); private List<DownloadAsyncTask> mTaskList = new ArrayList<DownloadAsyncTask>(); @Override protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  mProgressBar = (ProgressBar) findViewById(R.id.progressbar);  mPercentTV = (TextView) findViewById(R.id.percent_tv);  mSharedPreferences = getSharedPreferences("download", Context.MODE_PRIVATE);  //開始下載  findViewById(R.id.begin).setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {    new Thread() {     @Override     public void run() {      //創(chuàng)建存儲文件夾      File dir = new File(downloadPath);      if (!dir.exists()) {       dir.mkdir();      }      //獲取文件大小      HttpClient client = new DefaultHttpClient();      HttpGet request = new HttpGet(mUrl);      HttpResponse response = null;      try {       response = client.execute(request);       mFileLength = response.getEntity().getContentLength();      } catch (Exception e) {       Log.e(TAG, e.getMessage());      } finally {       if (request != null) {        request.abort();       }      }      Message.obtain(mHandler, GET_LENGTH_SUCCESS).sendToTarget();     }    }.start();   }  });  //暫停下載  findViewById(R.id.end).setOnClickListener(new View.OnClickListener() {   @Override   public void onClick(View v) {    for (DownloadAsyncTask task : mTaskList) {     if (task != null && (task.getStatus() == AsyncTask.Status.RUNNING || !task.isCancelled())) {      task.cancel(true);     }    }    mTaskList.clear();   }  }); } /**  * 開始下載  * 根據(jù)待下載文件大小計算每個線程下載位置,并創(chuàng)建AsyncTask  */ private void beginDownload() {  mCurrentLength = 0L;  mPercentTV.setVisibility(View.VISIBLE);  mProgressBar.setProgress(0);  long blockLength = mFileLength / DEFAULT_POOL_SIZE;  for (int i = 0; i < DEFAULT_POOL_SIZE; i++) {   long beginPosition = i * blockLength;//每條線程下載的開始位置   long endPosition = (i + 1) * blockLength;//每條線程下載的結(jié)束位置   if (i == (DEFAULT_POOL_SIZE - 1)) {    endPosition = mFileLength;//如果整個文件的大小不為線程個數(shù)的整數(shù)倍,則最后一個線程的結(jié)束位置即為文件的總長度   }   DownloadAsyncTask task = new DownloadAsyncTask(beginPosition, endPosition);   mTaskList.add(task);   task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mUrl, String.valueOf(i));  } } /**  * 更新進(jìn)度條  */ synchronized public void updateProgress() {  int percent = (int) Math.ceil((float)mCurrentLength / (float)mFileLength * 100);//  Log.i(TAG, "downloading " + mCurrentLength + "," + mFileLength + "," + percent);  if(percent > mProgressBar.getProgress()) {   mProgressBar.setProgress(percent);   mPercentTV.setText("下載進(jìn)度:" + percent + "%");   if (mProgressBar.getProgress() == mProgressBar.getMax()) {    Toast.makeText(MainActivity.this, "下載結(jié)束", Toast.LENGTH_SHORT).show();   }  } } @Override protected void onDestroy() {  for(DownloadAsyncTask task: mTaskList) {   if(task != null && task.getStatus() == AsyncTask.Status.RUNNING) {    task.cancel(true);   }   mTaskList.clear();  }  super.onDestroy(); } /**  * 下載的AsyncTask  */ private class DownloadAsyncTask extends AsyncTask<String, Integer , Long> {  private static final String TAG = "DownloadAsyncTask";  private long beginPosition = 0;  private long endPosition = 0;  private long current = 0;  private String currentThreadIndex;  public DownloadAsyncTask(long beginPosition, long endPosition) {   this.beginPosition = beginPosition;   this.endPosition = endPosition;  }  @Override  protected Long doInBackground(String... params) {   Log.i(TAG, "downloading");   String url = params[0];   currentThreadIndex = url + params[1];   if(url == null) {    return null;   }   HttpClient client = new DefaultHttpClient();   HttpGet request = new HttpGet(url);   HttpResponse response = null;   InputStream is = null;   RandomAccessFile fos = null;   OutputStream output = null;   try {    //本地文件    File file = new File(downloadPath + File.separator + url.substring(url.lastIndexOf("/") + 1));    //獲取之前下載保存的信息,從之前結(jié)束的位置繼續(xù)下載    //這里加了判斷file.exists(),判斷是否被用戶刪除了,如果文件沒有下載完,但是已經(jīng)被用戶刪除了,則重新下載    long downedPosition = mSharedPreferences.getLong(currentThreadIndex, 0);    if(file.exists() && downedPosition != 0) {     beginPosition = beginPosition + downedPosition;     current = downedPosition;     synchronized (mCurrentLength) {      mCurrentLength += downedPosition;     }    }    //設(shè)置下載的數(shù)據(jù)位置beginPosition字節(jié)到endPosition字節(jié)    Header header_size = new BasicHeader("Range", "bytes=" + beginPosition + "-" + endPosition);    request.addHeader(header_size);    //執(zhí)行請求獲取下載輸入流    response = client.execute(request);    is = response.getEntity().getContent();    //創(chuàng)建文件輸出流    fos = new RandomAccessFile(file, "rw");    //從文件的size以后的位置開始寫入,其實也不用,直接往后寫就可以。有時候多線程下載需要用    fos.seek(beginPosition);    byte buffer [] = new byte[1024];    int inputSize = -1;    while((inputSize = is.read(buffer)) != -1) {     fos.write(buffer, 0, inputSize);     current += inputSize;     synchronized (mCurrentLength) {      mCurrentLength += inputSize;     }     this.publishProgress();     if (isCancelled()) {      return null;     }    }   } catch (MalformedURLException e) {    Log.e(TAG, e.getMessage());   } catch (IOException e) {    Log.e(TAG, e.getMessage());   } finally{    try{     /*if(is != null) {      is.close();     }*/     if (request != null) {      request.abort();     }     if(output != null) {      output.close();     }     if(fos != null) {      fos.close();     }    } catch(Exception e) {     e.printStackTrace();    }   }   return null;  }  @Override  protected void onPreExecute() {   Log.i(TAG, "download begin ");   super.onPreExecute();  }  @Override  protected void onProgressUpdate(Integer... values) {   super.onProgressUpdate(values);   //更新界面進(jìn)度條   updateProgress();  }  @Override  protected void onPostExecute(Long aLong) {   Log.i(TAG, "download success ");   //下載完成移除記錄   mSharedPreferences.edit().remove(currentThreadIndex).commit();  }  @Override  protected void onCancelled() {   Log.i(TAG, "download cancelled ");   //記錄已下載大小current   mSharedPreferences.edit().putLong(currentThreadIndex, current).commit();  }  @Override  protected void onCancelled(Long aLong) {   Log.i(TAG, "download cancelled(Long aLong)");   super.onCancelled(aLong);   mSharedPreferences.edit().putLong(currentThreadIndex, current).commit();  } } private class InnerHandler extends Handler {  @Override  public void handleMessage(Message msg) {   switch (msg.what) {    case GET_LENGTH_SUCCESS :     beginDownload();     break;   }   super.handleMessage(msg);  } }}

布局文件和前面一篇博客《AsyncTask實現(xiàn)斷點續(xù)傳》布局文件是一樣的,這里就不貼代碼了。

  以上代碼親測可用,幾百M大文件也沒問題。

三、遇到的坑

  問題描述:在使用上面代碼下載http://ftp.neu.edu.cn/mirrors/eclipse/technology/epp/downloads/release/juno/SR2/eclipse-java-juno-SR2-linux-gtk-x86_64.tar.gz文件的時候,不知道為什么暫停時候執(zhí)行AsyncTask.cancel(true)來取消下載任務(wù),不執(zhí)行onCancel()函數(shù),也就沒有記錄該線程下載的位置。并且再次點擊下載的時候,5個Task都只執(zhí)行了onPreEexcute()方法,壓根就不執(zhí)行doInBackground()方法。而下載其他文件沒有這個問題。

  這個問題折騰了我好久,它又沒有報任何異常,調(diào)試又調(diào)試不出來。看AsyncTask的源碼、上stackoverflow也沒有找到原因。看到這個網(wǎng)站(https://groups.google.com/forum/#!topic/android-developers/B-oBiS7npfQ)時,我還真以為是AsyncTask的一個bug。

  百番周折,問題居然出現(xiàn)在上面代碼239行(這里已注釋)。不知道為什么,執(zhí)行這一句的時候,線程就阻塞在那里了,所以doInBackground()方法一直沒有結(jié)束,onCancel()方法當(dāng)然也不會執(zhí)行了。同時,因為使用的是線程池Executor,線程數(shù)為5個,點擊取消之后5個線程都阻塞了,所以再次點擊下載的時候只執(zhí)行了onPreEexcute()方法,沒有空閑的線程去執(zhí)行doInBackground()方法。真是巨坑無比有木有。。。

  雖然問題解決了,但是為什么有的文件下載執(zhí)行到is.close()的時候線程會阻塞而有的不會?這還是個謎。如果哪位大神知道是什么原因,還望指點指點!

源碼下載:https://github.com/liuling07/MultiTaskAndThreadDownload

總結(jié)

以上所述是小編給大家介紹的Android 使用AsyncTask實現(xiàn)多線程斷點續(xù)傳,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對VEVB武林網(wǎng)網(wǎng)站的支持!


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 灵山县| 龙口市| 杭州市| 建瓯市| 汤原县| 土默特左旗| 新巴尔虎右旗| 台南市| 巨野县| 城市| 海伦市| 洪泽县| 平舆县| 永川市| 湟源县| 新宾| 怀安县| 郎溪县| 微山县| 珲春市| 大渡口区| 鄂州市| 宣化县| 渑池县| 额济纳旗| 浠水县| 亚东县| 明水县| 辽源市| 富民县| 红桥区| 武汉市| 金川县| 博罗县| 德昌县| 吉首市| 磐石市| 泾川县| 罗平县| 千阳县| 农安县|