激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Android - Android 使用AsyncTask實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳

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

2022-02-25 15:38殘劍_ Android

本文將詳細(xì)講解如何使用AsyncTask來實(shí)現(xiàn)多線程的斷點(diǎn)續(xù)傳下載功能,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧

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

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

一、實(shí)現(xiàn)原理

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

?
1
2
3
4
5
6
7
8
9
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;//如果整個(gè)文件的大小不為線程個(gè)數(shù)的整數(shù)倍,則最后一個(gè)線程的結(jié)束位置即為文件的總長(zhǎng)度
  }
  ......
 }

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
@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();
  }

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

?
1
2
3
4
5
6
7
8
9
10
//獲取之前下載保存的信息,從之前結(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;
  }
 }

二、完整代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
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)建存儲(chǔ)文件夾
      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ù)待下載文件大小計(jì)算每個(gè)線程下載位置,并創(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;//如果整個(gè)文件的大小不為線程個(gè)數(shù)的整數(shù)倍,則最后一個(gè)線程的結(jié)束位置即為文件的總長(zhǎng)度
   }
   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í)行請(qǐng)求獲取下載輸入流
    response = client.execute(request);
    is = response.getEntity().getContent();
    //創(chuàng)建文件輸出流
    fos = new RandomAccessFile(file, "rw");
    //從文件的size以后的位置開始寫入,其實(shí)也不用,直接往后寫就可以。有時(shí)候多線程下載需要用
    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實(shí)現(xiàn)斷點(diǎn)續(xù)傳》布局文件是一樣的,這里就不貼代碼了。

  以上代碼親測(cè)可用,幾百M(fèi)大文件也沒問題。

三、遇到的坑

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

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

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

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

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

總結(jié)

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

原文鏈接:http://www.cnblogs.com/liuling/p/2015-10-13-01.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久国产综合精品 | 中文字幕在线免费 | 手机免费看一级片 | 成人福利在线看 | 午夜激情视频免费 | 久久艹综合| 黄色的视频免费看 | 蜜桃网站在线 | 成人黄色短视频在线观看 | 成人午夜免费av | 男男成人高潮片免费视频欧美 | 黄色一级毛片免费看 | 91av资源在线 | 国产又粗又爽又深的免费视频 | 成人毛片视频免费看 | 一级尻逼视频 | 日本s级毛片免费观看 | 久久人人做 | 欧美一级毛片免费观看 | 草妞视频 | 欧美成人精品欧美一级乱黄 | 狠狠干天天| 久草在线视频免费播放 | www.99热视频 | 欧日韩在线 | 久久网页| 免费在线一区二区 | 久久成人激情视频 | 在线成人免费观看视频 | 成人免费网站在线观看 | 精品在线观看一区 | 老司机免费福利午夜入口ae58 | 国产精品久久久久久久久久大牛 | 在线免费观看毛片 | 一级免费黄色免费片 | 天天看夜夜爽 | 久久精品免费网站 | 欧美成人精品欧美一级 | 国产69精品久久久久孕妇黑 | 亚洲电影免费观看国语版 | 牛牛a级毛片在线播放 |