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进度。
那咱先来说说启动下载任务。
01.btnDownload.setOnClickListener(new OnClickListener() {02.public void onClick(View v) {03.String url = datas.get(position).get("url");04.Async asyncTask = null; // 下载renwu05.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里都干了啥。
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,也可以是实体Bean033.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.@Override049.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.@Override185.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.@Override196.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.@Override215.protected void onPreExecute() {216.super.onPreExecute();217.}218. 219./**220.* 更新下载进度,当publishProgress方法被调用的时候就会自动来调用这个方法221.*/222.@Override223.protected void onProgressUpdate(Integer... values) {224.super.onProgressUpdate(values);225.}226. 227. 228./**229.* 获取要下载内容的长度230.* @param urlString231.* @return232.*/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 url251.* @param name www.it165.net252.* @return253.*/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 name269.* @param appid270.* @param url271.* @param downloadedLength272.*/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 cursize305.* @param name306.* @param url307.*/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 url324.* @param name325.*/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了,我自己写个列表不就得了。 先来看看列表页都有些什么
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.@Override18.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.@Override29.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都干嘛了。
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。 到此,核心功能展示完毕。附效果图一张

Android使用AsyncTask实现可以断点续传的DownloadManager功能的更多相关文章
- AsyncTask实现多线程断点续传
前面一篇博客<AsyncTask实现断点续传>讲解了如何实现单线程下的断点续传,也就是一个文件只有一个线程进行下载. 对于大文件而言,使用多线程下载就会比单线程下载要快一些.多线程下载 ...
- Android笔记——AsyncTask介绍
AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操 ...
- Android—初识AsyncTask
AsyncTask是用来处理一些后台的比较耗时的任务,给用户带来良好的体验.AsyncTask扩展Thread,增强了与主线程的交互能力. 首先介绍AsyncTask中定义的以下几个方法: onPre ...
- 详解Android中AsyncTask的使用
在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更 ...
- 55.Android之AsyncTask介绍 (转)
AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操 ...
- android高级---->AsyncTask的源码分析
在Android中实现异步任务机制有两种方式,Handler和AsyncTask,它在子线程更新UI的例子可以参见我的博客(android基础---->子线程更新UI).今天我们通过一个小的案例 ...
- Android 多线程----AsyncTask异步任务详解
[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3 ...
- Android测试AsyncTask下载图片
package com.example.myact8_async; import org.apache.http.HttpEntity; import org.apache.http.HttpResp ...
- Android开发——AsyncTask详解
android提供AsynvTask,目的是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的. Android为了降低开发难度,提供了AsyncTask.Asyn ...
随机推荐
- table动态添加删除一行和改变标题
<style type="text/css"> body{ font-size:13px; line-height:25px; } table{ border-top: ...
- FFmpeg Filters Images 参数及效果图
FFmpeg Filters Images 参数及效果图(chm) 下载 ffmpeg filters images 352 si.chm (27.98M) 下载 ffmpeg filters onl ...
- HDU4870 Rating(概率)
第一场多校,感觉自己都跳去看坑自己的题目里去了,很多自己可能会比较擅长一点的题目没看,然后写一下其中一道概率题的题解吧,感觉和自己前几天做的概率dp的思路是一样的.下面先来看题意:一个人有两个TC的账 ...
- [火狐REST] 火狐REST 模拟 HTTP get, post请求
- CSS鼠标样式整理
鼠标样式的标签: cursor:*; //该属性定义了鼠标指针放在一个元素边界范围内时所用的光标形状: 鼠标样式: 值 描述 url 需使用的自定义光标的 URL. 注释:请在此列表的末端始终定义一 ...
- Mysql 自动化任务
Mysql自动化任务,有两种:基于事件,基于时间. 基于事件,可由触发器来实现.具体触发器的编写比较简单,其语法规范可参照:http://www.jb51.net/article/59552.htm. ...
- git_2-linux
在linux下搭建git环境1.创建Github账号,https://github.com2.Linux创建SSH密钥: ssh-keygen ##一直默认就可以了 3.将公钥加入到Github账户 ...
- .net学习笔记----二级域名站点共享Session状态
前面一篇文章提到了如何在使用了ASP.NET form authentication的二级站点之间共享登陆状态, http://www.cnblogs.com/jzywh/archive/2007/0 ...
- Sonar+Hudson+Maven构建系列之三:安装Hudson
摘要:其实前面介绍过Sonar,后面Hudson安装就方便了.安装Hudson之前说说Hudson相关的事,现在世面上的有两种与Hudson相关的CI工具,一个是Hudson,一个是Jenkins,这 ...
- Loadrunner请求自定义的http(json)文件and参数化
Loadrunner请求自定义的http(json)文件and参数化 研究啦好些天这个东西啦 终于出来答案啦 嘿嘿 给大家分享一下 : 请求自定义的http文件用函数:web_custom_ ...