之前公司里面項目的下載模塊都是使用xUtils提供的,最近看了下xUtils的源碼,它里面也是使用AsyncTask來執行異步任務的,它的下載也包含了斷點續傳的功能。這里我自己也使用AsyncTask也實現了簡單的斷點續傳的功能。
首先說一說AsyncTask吧,先來看看AsyncTask的定義:
1
|
public abstract class AsyncTask<Params, Progress, Result> |
三種泛型類型分別代表“啟動任務執行的輸入參數”、“后臺任務執行的進度”、“后臺計算結果的類型”。在特定場合下,并不是所有類型都被使用,如果沒有被使用,可以用java.lang.Void類型代替。
一個異步任務的執行一般包括以下幾個步驟:
1.execute(Params... params),執行一個異步任務,需要我們在代碼中調用此方法,觸發異步任務的執行。
2.onPreExecute(),在execute(Params... params)被調用后立即執行,一般用來在執行后臺任務前對UI做一些標記。
3.doInBackground(Params... params),在onPreExecute()完成后立即執行,用于執行較為費時的操作,此方法將接收輸入參數和返回計算結果。在執行過程中可以調用publishProgress(Progress... values)來更新進度信息。
4.onProgressUpdate(Progress... values),在調用publishProgress(Progress... values)時,此方法被執行,直接將進度信息更新到UI組件上。
5.onPostExecute(Result result),當后臺操作結束時,此方法將會被調用,計算結果將做為參數傳遞到此方法中,直接將結果顯示到UI組件上。
在使用的時候,有幾點需要格外注意:
1.異步任務的實例必須在UI線程中創建。
2.execute(Params... params)方法必須在UI線程中調用。
3.不要手動調用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。
4.不能在doInBackground(Params... params)中更改UI組件的信息。
5.一個任務實例只能執行一次,如果執行第二次將會拋出異常。
下面是使用AsyncTask實現斷點續傳的代碼:
斷點續傳的思路其實也挺簡單,首先判斷待下載的文件在本地是否存在,如果存在,則表示該文件已經下載過一部分了,只需要獲取文件當前大小即已下載大小,設置給http的header就行了:
1
2
|
Header header_size = new BasicHeader( "Range" , "bytes=" + readedSize + "-" ); request.addHeader(header_size); |
1、布局文件代碼:
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
|
<LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:tools= "http://schemas.android.com/tools" android:layout_width= "match_parent" android:layout_height= "match_parent" android:paddingLeft= "@dimen/activity_horizontal_margin" android:paddingRight= "@dimen/activity_horizontal_margin" android:paddingTop= "@dimen/activity_vertical_margin" android:paddingBottom= "@dimen/activity_vertical_margin" tools:context= ".AsyncTaskDemoActivity" android:orientation= "vertical" > <Button android:id= "@+id/begin" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "開始下載" /> <Button android:id= "@+id/end" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:text= "暫停下載" /> <ProgressBar android:id= "@+id/progressbar" style= "?android:attr/progressBarStyleHorizontal" android:layout_width= "fill_parent" android:layout_height= "3dp" android:layout_marginTop= "10dp" android:max= "100" /> </LinearLayout> |
布局比較簡單,就兩個按鈕和一個進度條控件,按鈕控制下載與暫停。
2、斷點續傳核心代碼:
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
|
package com.bbk.lling.myapplication; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.View; import android.widget.ProgressBar; 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.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.net.MalformedURLException; public class AsyncTaskDemoActivity extends Activity { private ProgressBar progressBar; //下載路徑 private String downloadPath = Environment.getExternalStorageDirectory() + File.separator + "download" ; private DownloadAsyncTask downloadTask; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_async_task_demo); progressBar = (ProgressBar) findViewById(R.id.progressbar); //開始下載 findViewById(R.id.begin).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { /** * 一個AsyncTask只能被執行一次,否則會拋異常 * java.lang.IllegalStateException: Cannot execute task: the task is already running. * 如果要重新開始任務的話要重新創建AsyncTask對象 */ if (downloadTask != null && !downloadTask.isCancelled()) { return ; } downloadTask = new DownloadAsyncTask( "http://bbk-lewen.u.qiniudn.com/3d5b1a2c-4986-4e4a-a626-b504a36e600a.flv" ); downloadTask.execute(); } }); //暫停下載 findViewById(R.id.end).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { if (downloadTask != null && downloadTask.getStatus() == AsyncTask.Status.RUNNING) { downloadTask.cancel( true ); } } }); } /** * 下載的AsyncTask */ private class DownloadAsyncTask extends AsyncTask<String, Integer, Long> { private static final String TAG = "DownloadAsyncTask" ; private String mUrl; public DownloadAsyncTask(String url) { this .mUrl = url; } @Override protected Long doInBackground(String... params) { Log.i(TAG, "downloading" ); if (mUrl == null ) { return null ; } HttpClient client = new DefaultHttpClient(); HttpGet request = new HttpGet(mUrl); HttpResponse response = null ; InputStream is = null ; RandomAccessFile fos = null ; OutputStream output = null ; try { //創建存儲文件夾 File dir = new File(downloadPath); if (!dir.exists()) { dir.mkdir(); } //本地文件 File file = new File(downloadPath + File.separator + mUrl.substring(mUrl.lastIndexOf( "/" ) + 1 )); if (!file.exists()){ //創建文件輸出流 output = new FileOutputStream(file); //獲取下載輸入流 response = client.execute(request); is = response.getEntity().getContent(); //寫入本地 file.createNewFile(); byte buffer [] = new byte [ 1024 ]; int inputSize = - 1 ; //獲取文件總大小,用于計算進度 long total = response.getEntity().getContentLength(); int count = 0 ; //已下載大小 while ((inputSize = is.read(buffer)) != - 1 ) { output.write(buffer, 0 , inputSize); count += inputSize; //更新進度 this .publishProgress(( int ) ((count / ( float ) total) * 100 )); //一旦任務被取消則退出循環,否則一直執行,直到結束 if (isCancelled()) { output.flush(); return null ; } } output.flush(); } else { long readedSize = file.length(); //文件大小,即已下載大小 //設置下載的數據位置XX字節到XX字節 Header header_size = new BasicHeader( "Range" , "bytes=" + readedSize + "-" ); request.addHeader(header_size); //執行請求獲取下載輸入流 response = client.execute(request); is = response.getEntity().getContent(); //文件總大小=已下載大小+未下載大小 long total = readedSize + response.getEntity().getContentLength(); //創建文件輸出流 fos = new RandomAccessFile(file, "rw" ); //從文件的size以后的位置開始寫入,其實也不用,直接往后寫就可以。有時候多線程下載需要用 fos.seek(readedSize); //這里用RandomAccessFile和FileOutputStream都可以,只是使用FileOutputStream的時候要傳入第二哥參數true,表示從后面填充 // output = new FileOutputStream(file, true); byte buffer [] = new byte [ 1024 ]; int inputSize = - 1 ; int count = ( int )readedSize; while ((inputSize = is.read(buffer)) != - 1 ) { fos.write(buffer, 0 , inputSize); // output.write(buffer, 0, inputSize); count += inputSize; this .publishProgress(( int ) ((count / ( float ) total) * 100 )); if (isCancelled()) { // output.flush(); return null ; } } // output.flush(); } } catch (MalformedURLException e) { Log.e(TAG, e.getMessage()); } catch (IOException e) { Log.e(TAG, e.getMessage()); } finally { try { if (is != null ) { is.close(); } 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 " ); Toast.makeText(AsyncTaskDemoActivity. this , "開始下載" , Toast.LENGTH_SHORT).show(); super .onPreExecute(); } @Override protected void onProgressUpdate(Integer... values) { super .onProgressUpdate(values); Log.i(TAG, "downloading " + values[ 0 ]); //更新界面進度條 progressBar.setProgress(values[ 0 ]); } @Override protected void onPostExecute(Long aLong) { Log.i(TAG, "download success " + aLong); Toast.makeText(AsyncTaskDemoActivity. this , "下載結束" , Toast.LENGTH_SHORT).show(); super .onPostExecute(aLong); } } } |
這樣簡單的斷點續傳功能就實現了,這里只是單個線程的,如果涉及多個線程同時下載,那復雜很多,后面再琢磨琢磨。
源碼下載:https://github.com/liuling07/MultiTaskAndThreadDownload
總結
以上所述是小編給大家介紹的Android 使用AsyncTask實現斷點續傳,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:http://www.cnblogs.com/liuling/p/2015-10-10-01.html