Android使用AsyncTask实现可以断点续传的DownloadManager功能

http://www.it165.net/pro/html/201211/4210.html

最近做项目卡壳了,要做个Android的应用市场,其他方面都还好说,唯独这个下载管理算是给我难住了,究其原因,一是之前没有做过类似的功能,二是这个项目催的着实的急促,以至于都没什么时间能仔细研究这方面的内容,三是我这二把刀的基本功实在是不太扎实啊。不过好在经高人指点,再加上bing以及stackoverflow的帮助,好歹算是有些成果,下面就将这小小的成果分享一下,虽然是使用的AsyncTask来完成,但是个人觉得还是service要更靠谱些,不过那个得等有空儿再研究了。

AsyncTask是何物我就不再赘述了,度娘,谷哥,必应都会告诉你的,不过建议大家看看文章最后参考资料的第二个链接,写的还是非常详细的。我认为它实际上就是个简单的迷你的Handler,反正把一些异步操作扔给它以后,就只需要等着它执行完就齐活了。

那么怎么用这玩意儿实现一个下载管理的功能?大体的思路是这样的:
  1.点击下载按钮以后,除了要让AsyncTask开始执行外,还要把下载的任务放到HashMap里面保存,这样做的好处就是能够在列表页进行管理,比如暂停、继续下载、取消。
  2.下载管理页的列表,使用ScrollView,而非ListView。这样做的好处就是为了能方便的更新ProgressBar进度。

那咱先来说说启动下载任务。

view sourceprint?

01.btnDownload.setOnClickListener(new OnClickListener() {

02.public void onClick(View v) {

03.String url = datas.get(position).get("url");

04.Async asyncTask = null// 下载renwu

05.boolean isHas = false;

06.// 判断当前要下载的这个连接是否已经正在进行,如果正在进行就阻止在此启动一个下载任务

07.for (String urlString : AppConstants.listUrl) {

08.if (url.equalsIgnoreCase(urlString)) {

09.isHas = true;

10.break;

11.}

12.}

13.

14.// 如果这个连接的下载任务还没有开始,就创建一个新的下载任务启动下载,并这个下载任务加到下载列表中

15.if(isHas == false) {

16.asyncTask = new Async();  // 创建新异步

17.asyncTask.setDataMap(datas.get(position));

18.asyncTask.setContext(context);

19.AppConstants.mapTask.put(url, asyncTask);

20.// 当调用AsyncTask的方法execute时,就会去自动调用doInBackground方法

21.asyncTask.executeOnExecutor(Executors.newCachedThreadPool(), url);

22.}

23.}

24.});

这里我为什么写asyncTask.executeOnExecutor(Executors.newCachedThreadPool(), url);而不是asyncTask.execute(url);呢?先卖个关子,回头咱再说。

下面来看看Async里都干了啥。

view sourceprint?

001.package com.test.muldownloadtest.task;

002.

003.import java.io.File;

004.import java.io.IOException;

005.import java.io.InputStream;

006.import java.io.RandomAccessFile;

007.import java.net.HttpURLConnection;

008.import java.net.MalformedURLException;

009.import java.net.URL;

010.import java.util.HashMap;

011.

012.import com.test.muldownloadtest.AppConstants;

013.import com.test.muldownloadtest.bean.DBHelper;

014.

015.import android.content.Context;

016.import android.database.Cursor;

017.import android.database.sqlite.SQLiteDatabase;

018.import android.os.AsyncTask;

019.import android.os.Environment;

020.import android.os.Handler;

021.import android.os.Message;

022.import android.widget.ListView;

023.import android.widget.ProgressBar;

024.import android.widget.TextView;

025.

026.// AsyncTask<Params, Progress, Result> 

027.public class Async extends AsyncTask<String, Integer, String> {

028.

029./* 用于查询数据库 */

030.private DBHelper dbHelper;

031.

032.// 下载的文件的map,也可以是实体Bean

033.private HashMap<String, String> dataMap = null;

034.private Context context;

035.

036.private boolean finished = false;

037.private boolean paused = false;

038.

039.private int curSize = 0;

040.

041.private int length = 0;

042.

043.private Async startTask = null;

044.private boolean isFirst = true;

045.

046.private String strUrl;

047.

048.@Override

049.protected String doInBackground=\‘#\‘" /span>

050.

051.dbHelper = new DBHelper(context);

052.

053.strUrl = Params[0];

054.String name = dataMap.get("name");

055.String appid = dataMap.get("appid");

056.int startPosition = 0;

057.

058.URL url = null;

059.HttpURLConnection httpURLConnection = null;

060.InputStream inputStream = null;

061.RandomAccessFile outputStream = null;

062.// 文件保存路径

063.String path = Environment.getExternalStorageDirectory().getPath();

064.// 文件名

065.String fileName = strUrl.substring(strUrl.lastIndexOf(‘/‘));

066.try {

067.length = getContentLength(strUrl);

068.startPosition = getDownloadedLength(strUrl, name);

069.

070./** 判断是否是第一次启动任务,true则保存数据到数据库并下载,

071.*  false则更新数据库中的数据 start 

072.*/

073.boolean isHas = false;

074.for (String urlString : AppConstants.listUrl) {

075.if (strUrl.equalsIgnoreCase(urlString)) {

076.isHas = true;

077.break;

078.}

079.}

080.if (false == isHas) {

081.saveDownloading(name, appid, strUrl, path, fileName, startPosition, length, 1);

082.}

083.else if (true == isHas) {

084.updateDownloading(curSize, name, strUrl);

085.}

086./** 判断是否是第一次启动任务,true则保存数据到数据库并下载,

087.*  false则更新数据库中的数据 end 

088.*/

089.

090.// 设置断点续传的开始位置

091.url = new URL=\‘#\‘" /span>

092.httpURLConnection = (HttpURLConnection)url.openConnection();

093.httpURLConnection.setAllowUserInteraction(true);

094.httpURLConnection.setRequestMethod("GET");

095.httpURLConnection.setReadTimeout(5000);

096.httpURLConnection.setRequestProperty("User-Agent","NetFox");

097.httpURLConnection.setRequestProperty("Range""bytes=" + startPosition + "-");

098.inputStream = httpURLConnection.getInputStream();

099.

100.File outFile = new File(path+fileName);

101.// 使用java中的RandomAccessFile 对文件进行随机读写操作

102.outputStream = new RandomAccessFile(outFile,"rw");

103.// 设置开始写文件的位置

104.outputStream.seek(startPosition);

105.

106.byte[] buf = new byte[1024*100];

107.int read = 0;

108.curSize = startPosition;

109.while(false == finished) {

110.while(true == paused) {

111.// 暂停下载

112.Thread.sleep(500);

113.}

114.read = inputStream.read(buf);

115.if(read==-1) {

116.break;

117.}

118.outputStream.write(buf,0,read);

119.curSize = curSize+read;

120.// 当调用这个方法的时候会自动去调用onProgressUpdate方法,传递下载进度

121.publishProgress((int)(curSize*100.0f/length));

122.if(curSize == length) {

123.break;

124.}

125.Thread.sleep(500);

126.updateDownloading(curSize, name, strUrl);

127.}

128.if (false == finished) {

129.finished = true;

130.deleteDownloading(strUrl, name);

131.}

132.inputStream.close();

133.outputStream.close();

134.httpURLConnection.disconnect();

135.}

136.catch (MalformedURLException e) {

137.e.printStackTrace();

138.

139.catch (IOException e) {

140.e.printStackTrace();

141.

142.catch (InterruptedException e) {

143.e.printStackTrace();

144.}

145.finally {

146.finished = true;

147.deleteDownloading(strUrl, name);

148.if(inputStream!=null) {

149.try {

150.inputStream.close();

151.if(outputStream!=null) {

152.outputStream.close();

153.}

154.if(httpURLConnection!=null) {

155.httpURLConnection.disconnect();

156.}

157.}

158.catch (IOException e) {

159.e.printStackTrace();

160.}

161.}

162.}

163.// 这里的返回值将会被作为onPostExecute方法的传入参数

164.return strUrl;

165.}

166.

167./**

168.* 暂停下载

169.*/

170.public void pause() {

171.paused = true;

172.}

173.

174./**

175.* 继续下载

176.*/

177.public void continued() {

178.paused = false;

179.}

180.

181./**

182.* 停止下载

183.*/

184.@Override

185.protected void onCancelled() {

186.finished = true;

187.deleteDownloading(dataMap.get("url"), dataMap.get("name"));

188.super.onCancelled();

189.}

190.

191./**

192.* 当一个下载任务成功下载完成的时候回来调用这个方法,

193.* 这里的result参数就是doInBackground方法的返回值

194.*/

195.@Override

196.protected void onPostExecute(String result) {

197.try {

198.String name = dataMap.get("name");

199.System.out.println("name===="+name);

200.// 判断当前结束的这个任务在任务列表中是否还存在,如果存在就移除

201.if (AppConstants.mapTask.containsKey(result)) {

202.if (AppConstants.mapTask.get(result) != null) {

203.finished = true;

204.deleteDownloading(result, name);

205.}

206.}

207.

208.catch (NumberFormatException e) {

209.e.printStackTrace();

210.}

211.super.onPostExecute(result);

212.}

213.

214.@Override

215.protected void onPreExecute() {

216.super.onPreExecute();

217.}

218.

219./**

220.* 更新下载进度,当publishProgress方法被调用的时候就会自动来调用这个方法

221.*/

222.@Override

223.protected void onProgressUpdate(Integer... values) {

224.super.onProgressUpdate(values);

225.}

226.

227.

228./**

229.* 获取要下载内容的长度

230.* @param urlString

231.* @return

232.*/

233.private int getContentLength(String urlString){

234.try {

235.URL url = new URL(urlString);

236.HttpURLConnection connection = (HttpURLConnection) url.openConnection();

237.return connection.getContentLength();

238.

239.catch (MalformedURLException e) {

240.e.printStackTrace();

241.

242.catch (IOException e) {

243.e.printStackTrace();

244.}

245.return 0;

246.}

247.

248./**

249.* 从数据库获取已经下载的长度

250.* @param url

251.* @param name  www.it165.net

252.* @return

253.*/

254.private int getDownloadedLength(String url, String name) {

255.int downloadedLength = 0;

256.SQLiteDatabase db = dbHelper.getReadableDatabase();  

257.String sql = "SELECT downloadBytes FROM fileDownloading WHERE downloadUrl=? AND name=?";  

258.Cursor cursor = db.rawQuery(sql, new String[] { url, name });  

259.while (cursor.moveToNext()) {  

260.downloadedLength = cursor.getInt(0);   

261.}  

262.db.close();  

263.return downloadedLength;  

264.}

265.

266./**

267.* 保存下载的数据

268.* @param name

269.* @param appid

270.* @param url

271.* @param downloadedLength

272.*/

273.private void saveDownloading(String name, String appid, String url, String savePath, String fileName, intdownloadBytes, int totalBytes, int status) {  

274.SQLiteDatabase db = dbHelper.getWritableDatabase();  

275.try {  

276.db.beginTransaction();  

277.String sql = "INSERT INTO fileDownloading(name, appid, downloadUrl, savePath, fileName, downloadBytes, totalBytes, downloadStatus) " +

278."values(?,?,?,?,?,?,?,?)";  

279.db.execSQL(sql, new Object[]{ name, appid, url, savePath, fileName, downloadBytes, totalBytes, status});  

280.db.setTransactionSuccessful();

281.boolean isHas = false;

282.// 判断当前要下载的这个连接是否已经正在进行,如果正在进行就阻止在此启动一个下载任务

283.for (String urlString : AppConstants.listUrl) {

284.if (url.equalsIgnoreCase(urlString)) {

285.isHas = true;

286.break;

287.}

288.}

289.if (false == isHas) {

290.AppConstants.listUrl.add(url);

291.}

292.if (false == isFirst) {

293.AppConstants.mapTask.put(url, startTask);

294.}

295.

296.finally {  

297.db.endTransaction();  

298.db.close();  

299.}  

300.}

301.

302./**

303.* 更新下载数据

304.* @param cursize

305.* @param name

306.* @param url

307.*/

308.private void updateDownloading(int cursize, String name, String url) {

309.SQLiteDatabase db = dbHelper.getWritableDatabase();  

310.try {  

311.db.beginTransaction();  

312.String sql = "UPDATE fileDownloading SET downloadBytes=? WHERE name=? AND downloadUrl=?";  

313.db.execSQL(sql, new String[] { cursize + "", name, url });  

314.db.setTransactionSuccessful();  

315.finally {  

316.db.endTransaction();  

317.db.close();  

318.}  

319.}

320.

321./**

322.* 删除下载数据

323.* @param url

324.* @param name

325.*/

326.private void deleteDownloading(String url, String name) {

327.if (true == finished) {

328.// 删除保存的URL。这个listurl主要是为了在列表中按添加下载任务的顺序进行显示

329.for (int i = 0; i < AppConstants.listUrl.size(); i++) {

330.if (url.equalsIgnoreCase(AppConstants.listUrl.get(i))) {

331.AppConstants.listUrl.remove(i);

332.}

333.}

334.// 删除已经完成的下载任务

335.if (AppConstants.mapTask.containsKey(url)) {

336.AppConstants.mapTask.remove(url);

337.}

338.}

339.SQLiteDatabase db = dbHelper.getWritableDatabase();  

340.String sql = "DELETE FROM fileDownloading WHERE downloadUrl=? AND name=?";  

341.db.execSQL(sql, new Object[] { url, name });  

342.db.close();  

343.

344.

345.public void setDataMap(HashMap<String, String> dataMap) {

346.this.dataMap = dataMap;

347.}

348.

349.public HashMap<String, String> getDataMap() {

350.return dataMap;

351.}

352.

353.public boolean isPaused() {

354.return paused;

355.}

356.

357.public int getCurSize() {

358.return curSize;

359.}

360.

361.public int getLength() {

362.return length;

363.}

364.

365.public void setContext(Context context) {

366.this.context = context;

367.}

368.

369.public Context getContext() {

370.return context;

371.}

372.

373.public void setListView(ListView listView) {

374.this.listView = listView;

375.}

376.}

好了,下载任务已经启动了,接下来就该开始管理了。先说说之前错误的思路,估计大多数的网友可能跟我一样,一想到列表首先想到的就是ListView,这多简单啊,放一个ListView,继承BaseAdapter写个自己的Adapter,然后一展现,完事了,so easy。我也是这么想的,这省事啊,用了以后才发现,确实省事,不过更新ProgressBar的时候可是给我愁死了,无论怎么着都不能正常更新ProgressBar。在这个地方钻了一周的牛角尖,昨儿个突然灵光乍现,干嘛给自己挖个坑,谁说列表就非得用ListView了,我自己写个列表不就得了。  先来看看列表页都有些什么

view sourceprint?

01.package com.test.muldownloadtest;

02.

03.import android.app.Activity;

04.import android.os.Bundle;

05.import android.view.View;

06.import android.view.View.OnClickListener;

07.import android.view.Window;

08.import android.widget.Button;

09.import android.widget.LinearLayout;

10.import android.widget.ScrollView;

11.

12.public class DownloadManagerActivity extends Activity implements OnClickListener {

13.

14.private ScrollView scDownload;

15.private LinearLayout llDownloadLayout;

16.

17.@Override

18.protected void onCreate(Bundle savedInstanceState) {

19.super.onCreate(savedInstanceState);

20.

21.this.requestWindowFeature(Window.FEATURE_NO_TITLE);

22.

23.this.setContentView(R.layout.download_manager_layout);

24.

25.initView();

26.}

27.

28.@Override

29.protected void onResume() {

30.super.onResume();

31.

32.refreshItemView();

33.}

34.

35.private void initView(){

36.

37.Button btnGoback = (Button) this.findViewById(R.id.btnGoback);

38.btnGoback.setOnClickListener(this);

39.

40.scDownload = (ScrollView) this.findViewById(R.id.svDownload);

41.scDownload.setSmoothScrollingEnabled(true);

42.

43.llDownloadLayout = (LinearLayout) this.findViewById(R.id.llDownloadLyout);

44.}

45.

46./**

47.* 列表中的每一项

48.*/

49.private void refreshItemView(){

50.for (int i = 0; i < AppConstants.listUrl.size(); i++) {

51.DownloadItemView downloadItemView = new DownloadItemView(this, AppConstants.listUrl.get(i), i);

52.downloadItemView.setId(i);

53.downloadItemView.setTag("downloadItemView_"+i);

54.llDownloadLayout.addView(downloadItemView);

55.}

56.}

57.

58.public void onClick(View v) {

59.switch (v.getId()) {

60.case R.id.btnExit:

61.this.finish();

62.break;

63.case R.id.btnGoback:

64.this.finish();

65.break;

66.default:

67.break;

68.}

69.}

70.}

很简单,一个ScrollView,在这个ScrollView中在内嵌一个LinearLayout,用这个LinearLayout来存储每一个列表项。其实列表项很简单,最基本只要三个控件就行了——ProgressBar、TextView、Button。一个是进度条,一个显示百分比,一个用来暂停/继续,偷个懒,这个布局文件就不列出来了,咱就看看这个Button都干嘛了。

view sourceprint?

01.public void onClick(View v) {

02.switch (v.getId()) {

03.case R.id.btnPauseOrResume:

04.String btnTag = (String) btnPauseOrResume.getTag();

05.if (btnTag.equals("pause")) {

06.resumeDownload();

07.}

08.else if (btnTag.equals("resume")) {

09.pauseDownload();

10.}

11.break;

12.default:

13.break;

14.}

15.}

16.

17.private void pauseDownload(){

18.btnPauseOrResume.setTag("pause");

19.btnPauseOrResume.setText(R.string.download_resume);

20.

21.Async pauseTask = null;

22.// 判断当前被停止的这个任务在任务列表中是否存在,如果存在就暂停

23.if (AppConstants.linkedMapDownloading.containsKey(urlString)) {

24.pauseTask = AppConstants.linkedMapDownloading.get(urlString);

25.if (pauseTask != null) {

26.pauseTask.pause();

27.}

28.}

29.}

30.

31.private void resumeDownload(){

32.btnPauseOrResume.setTag("resume");

33.btnPauseOrResume.setText(R.string.download_pause);

34.

35.Async continueTask = null;

36.// 判断当前被停止的这个任务在任务列表中是否存在,如果存在就继续

37.if (AppConstants.linkedMapDownloading.containsKey(urlString)) {

38.continueTask = AppConstants.linkedMapDownloading.get(urlString);

39.if (continueTask != null) {

40.continueTask.continued();

41.}

42.}

43.handler.postDelayed(runnable, 1000);

44.}

简单吧,就是判断一下当前按钮的Tag,然后根据Tag的值,来判断是继续下载,还是暂停下载。而这个暂停还是继续,其实只是修改下Async中的暂停标记的值,即paused是true还是false。  到此,核心功能展示完毕。附效果图一张

时间: 2024-10-25 17:53:40

Android使用AsyncTask实现可以断点续传的DownloadManager功能的相关文章

AsyncTask实现多线程断点续传

前面一篇博客<AsyncTask实现断点续传>讲解了如何实现单线程下的断点续传,也就是一个文件只有一个线程进行下载.   对于大文件而言,使用多线程下载就会比单线程下载要快一些.多线程下载相比单线程下载要稍微复杂一点,本博文将详细讲解如何使用AsyncTask来实现多线程的断点续传下载. 一.实现原理 多线程下载首先要通过每个文件总的下载线程数(我这里设定5个)来确定每个线程所负责下载的起止位置. long blockLength = mFileLength / DEFAULT_POOL_SI

Android开发--多线程下载加断点续传

文件下载在App应用中也用到很多,一般版本更新时多要用的文件下载来进行处理,以前也有看过很多大神有过该方面的博客,今天我也自己来实践一下,写的一般,还请大家多提意见,共同进步.主要思路: 1.多线程下载: 首先通过下载总线程数来划分文件的下载区域:利用int range = fileSize / threadCount:得到每一段下载量:每一段的位置是i * range到(i + 1) * rang  - 1,注意最后一段的位置是到filesize - 1: 通过Http协议的Range字段实现

Android项目----AsyncTask异步操作

public abstract class AsyncTask extends Object java.lang.Object    ? android.os.AsyncTask<Params, Progress, Result> Class Overview AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish

Android之——AsyncTask随笔

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46945263 在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2. 确保只在UI线程中访问Android UI工具包 当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事

Android之AsyncTask学习笔记

AsyncTask就是一个封装过的后台任务类,顾名思义就是异步任务. AsyncTask直接继承于Object类,位置为android.os.AsyncTask.要使用AsyncTask工作要提供三个泛型参数,并重载几个方法(至少重载一个). AsyncTask定义了三种泛型类型 Params,Progress和Result. Params 启动任务执行的输入参数,比如HTTP请求的URL. Progress 后台任务执行的百分比. Result 后台执行任务最终返回的结果,比如String.

Android Handler AsyncTask 消息机制

一.Android消息机制一 Android 有一种叫消息队列的说法,这里我们可以这样理解:假如一个隧道就是一个消息队列,那么里面的每一部汽车就是一个一个消息,这里我们先忽略掉超车等种种因素,只那么先进隧道的车将会先出,这个机制跟我们android 的消息机制是一样的. 角色描述 1. Looper:(相当于隧道) 一个线程可以产生一个Looper 对象,由它来管理此线程里的Message Queue( 车队,消息隧道) . 2. Handler: 你可以构造Handler 对象来与Looper

android笔记--AsyncTask例子

代码: package com.test.handler; import com.test.demo.R; import android.app.Activity; import android.app.ProgressDialog; import android.os.AsyncTask; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.widget

详解Android中AsyncTask的使用

在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制.关于Handler的相关知识,前面也有所介绍,不清楚的朋友们可以参照一下. 为了简化操作,Android1.5提供了工具类android.os.AsyncTask,它使创建异步任

android的asynctask的使用案例

http://www.cnblogs.com/devinzhang/archive/2012/02/13/2350070.html android的asynctask的使用案例,布布扣,bubuko.com