欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2018年05月29日 15:26:47   作者:殘劍_  
本文將詳細講解如何使用AsyncTask來實現(xiàn)多線程的斷點續(xù)傳下載功能,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧

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

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

一、實現(xiàn)原理

  多線程下載首先要通過每個文件總的下載線程數(shù)(我這里設(shè)定5個)來確定每個線程所負責(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));
  }
 }
 /**
  * 更新進度條
  */
 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("下載進度:" + 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);
   //更新界面進度條
   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()方法當然也不會執(zhí)行了。同時,因為使用的是線程池Executor,線程數(shù)為5個,點擊取消之后5個線程都阻塞了,所以再次點擊下載的時候只執(zhí)行了onPreEexcute()方法,沒有空閑的線程去執(zhí)行doInBackground()方法。真是巨坑無比有木有。。。

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

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

總結(jié)

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

相關(guān)文章

  • Flutter常用的布局和事件示例詳解

    Flutter常用的布局和事件示例詳解

    這篇文章主要給大家介紹了關(guān)于Flutter常用的布局和事件的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Flutter具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Kotlin?ContentProvider使用方法詳解

    Kotlin?ContentProvider使用方法詳解

    ContentProvider內(nèi)容提供者,主要用于再不同的應(yīng)用程序之前實現(xiàn)數(shù)據(jù)共享的功能,它提供了一套完整的機制,允許一個程序訪問另外一個程序的數(shù)據(jù),同時還能保證數(shù)據(jù)的安全性
    2022-12-12
  • AndriodStudio利用ListView和數(shù)據(jù)庫實現(xiàn)簡單學(xué)生管理

    AndriodStudio利用ListView和數(shù)據(jù)庫實現(xiàn)簡單學(xué)生管理

    這篇文章主要為大家詳細介紹了AndriodStudio利用ListView和數(shù)據(jù)庫實現(xiàn)簡單學(xué)生管理,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Android ScrollView實現(xiàn)下拉彈回動畫效果

    Android ScrollView實現(xiàn)下拉彈回動畫效果

    這篇文章主要為大家詳細介紹了Android ScrollView實現(xiàn)下拉彈回動畫效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • Android RecyclerView滑動刪除和拖動排序

    Android RecyclerView滑動刪除和拖動排序

    這篇文章主要介紹了Android RecyclerView滑動刪除和拖動排序的相關(guān)資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-07-07
  • Android打印機--小票打印格式及模板設(shè)置實例代碼

    Android打印機--小票打印格式及模板設(shè)置實例代碼

    這篇文章主要介紹了Android打印機--小票打印格式及模板設(shè)置實例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-04-04
  • Android EditText每4位自動添加空格效果

    Android EditText每4位自動添加空格效果

    這篇文章主要給大家介紹了關(guān)于Android EditText每4位自動添加空格效果的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用EditText具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • Android幾種多渠道打包的步驟詳解

    Android幾種多渠道打包的步驟詳解

    在不同的應(yīng)用市場可能有不同的統(tǒng)計需求,需要為每個應(yīng)用市場發(fā)布一個安裝包,這里就引出了Android的多渠道打包。這篇文章主要介紹了Android幾種多渠道打包,需要的朋友可以參考下
    2019-09-09
  • Android中微信搶紅包助手的實現(xiàn)詳解

    Android中微信搶紅包助手的實現(xiàn)詳解

    本篇文章主要介紹了Android中微信搶紅包助手的實現(xiàn)詳解,通過利用AccessibilityService輔助服務(wù),監(jiān)測屏幕內(nèi)容,如監(jiān)聽狀態(tài)欄的信息,屏幕跳轉(zhuǎn)等,以此來實現(xiàn)自動拆紅包的功能,有興趣的可以了解一下。
    2017-02-02
  • Android序列化實現(xiàn)接口Serializable與Parcelable詳解

    Android序列化實現(xiàn)接口Serializable與Parcelable詳解

    我們使用 Intent 傳遞數(shù)據(jù)的時候,putExtra() 所支持的數(shù)據(jù)類型事有限的,當需要傳遞自定義對象的時候就需要序列化。Serializable更簡單但是會把整個對象進行序列化因此效率比Parcelable低一些
    2022-12-12

最新評論