之前公司里面項目的下載模塊都是使用xUtils提供的,最近看了下xUtils的源碼,它里面也是使用AsyncTask來執(zhí)行異步任務(wù)的,它的下載也包含了斷點續(xù)傳的功能。這里我自己也使用AsyncTask也實現(xiàn)了簡單的斷點續(xù)傳的功能。
首先說一說AsyncTask吧,先來看看AsyncTask的定義:
1
|
public abstract class AsyncTask<Params, Progress, Result> |
三種泛型類型分別代表“啟動任務(wù)執(zhí)行的輸入?yún)?shù)”、“后臺任務(wù)執(zhí)行的進度”、“后臺計算結(jié)果的類型”。在特定場合下,并不是所有類型都被使用,如果沒有被使用,可以用java.lang.Void類型代替。
一個異步任務(wù)的執(zhí)行一般包括以下幾個步驟:
1.execute(Params... params),執(zhí)行一個異步任務(wù),需要我們在代碼中調(diào)用此方法,觸發(fā)異步任務(wù)的執(zhí)行。
2.onPreExecute(),在execute(Params... params)被調(diào)用后立即執(zhí)行,一般用來在執(zhí)行后臺任務(wù)前對UI做一些標記。
3.doInBackground(Params... params),在onPreExecute()完成后立即執(zhí)行,用于執(zhí)行較為費時的操作,此方法將接收輸入?yún)?shù)和返回計算結(jié)果。在執(zhí)行過程中可以調(diào)用publishProgress(Progress... values)來更新進度信息。
4.onProgressUpdate(Progress... values),在調(diào)用publishProgress(Progress... values)時,此方法被執(zhí)行,直接將進度信息更新到UI組件上。
5.onPostExecute(Result result),當后臺操作結(jié)束時,此方法將會被調(diào)用,計算結(jié)果將做為參數(shù)傳遞到此方法中,直接將結(jié)果顯示到UI組件上。
在使用的時候,有幾點需要格外注意:
1.異步任務(wù)的實例必須在UI線程中創(chuàng)建。
2.execute(Params... params)方法必須在UI線程中調(diào)用。
3.不要手動調(diào)用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。
4.不能在doInBackground(Params... params)中更改UI組件的信息。
5.一個任務(wù)實例只能執(zhí)行一次,如果執(zhí)行第二次將會拋出異常。
下面是使用AsyncTask實現(xiàn)斷點續(xù)傳的代碼:
斷點續(xù)傳的思路其實也挺簡單,首先判斷待下載的文件在本地是否存在,如果存在,則表示該文件已經(jīng)下載過一部分了,只需要獲取文件當前大小即已下載大小,設(shè)置給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、斷點續(xù)傳核心代碼:
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只能被執(zhí)行一次,否則會拋異常 * java.lang.IllegalStateException: Cannot execute task: the task is already running. * 如果要重新開始任務(wù)的話要重新創(chuàng)建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 { //創(chuàng)建存儲文件夾 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()){ //創(chuàng)建文件輸出流 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 )); //一旦任務(wù)被取消則退出循環(huán),否則一直執(zhí)行,直到結(jié)束 if (isCancelled()) { output.flush(); return null ; } } output.flush(); } else { long readedSize = file.length(); //文件大小,即已下載大小 //設(shè)置下載的數(shù)據(jù)位置XX字節(jié)到XX字節(jié) Header header_size = new BasicHeader( "Range" , "bytes=" + readedSize + "-" ); request.addHeader(header_size); //執(zhí)行請求獲取下載輸入流 response = client.execute(request); is = response.getEntity().getContent(); //文件總大小=已下載大小+未下載大小 long total = readedSize + response.getEntity().getContentLength(); //創(chuàng)建文件輸出流 fos = new RandomAccessFile(file, "rw" ); //從文件的size以后的位置開始寫入,其實也不用,直接往后寫就可以。有時候多線程下載需要用 fos.seek(readedSize); //這里用RandomAccessFile和FileOutputStream都可以,只是使用FileOutputStream的時候要傳入第二哥參數(shù)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 , "下載結(jié)束" , Toast.LENGTH_SHORT).show(); super .onPostExecute(aLong); } } } |
這樣簡單的斷點續(xù)傳功能就實現(xiàn)了,這里只是單個線程的,如果涉及多個線程同時下載,那復(fù)雜很多,后面再琢磨琢磨。
源碼下載:https://github.com/liuling07/MultiTaskAndThreadDownload
總結(jié)
以上所述是小編給大家介紹的Android 使用AsyncTask實現(xiàn)斷點續(xù)傳,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!
原文鏈接:http://www.cnblogs.com/liuling/p/2015-10-10-01.html