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// 下载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里都干了啥。

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了,我自己写个列表不就得了。  先来看看列表页都有些什么

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都干嘛了。

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功能的更多相关文章

  1. AsyncTask实现多线程断点续传

    前面一篇博客<AsyncTask实现断点续传>讲解了如何实现单线程下的断点续传,也就是一个文件只有一个线程进行下载.   对于大文件而言,使用多线程下载就会比单线程下载要快一些.多线程下载 ...

  2. Android笔记——AsyncTask介绍

    AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操 ...

  3. Android—初识AsyncTask

    AsyncTask是用来处理一些后台的比较耗时的任务,给用户带来良好的体验.AsyncTask扩展Thread,增强了与主线程的交互能力. 首先介绍AsyncTask中定义的以下几个方法: onPre ...

  4. 详解Android中AsyncTask的使用

    在Android中实现异步任务机制有两种方式,Handler和AsyncTask. Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更 ...

  5. 55.Android之AsyncTask介绍 (转)

    AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操 ...

  6. android高级---->AsyncTask的源码分析

    在Android中实现异步任务机制有两种方式,Handler和AsyncTask,它在子线程更新UI的例子可以参见我的博客(android基础---->子线程更新UI).今天我们通过一个小的案例 ...

  7. Android 多线程----AsyncTask异步任务详解

    [声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3 ...

  8. Android测试AsyncTask下载图片

    package com.example.myact8_async; import org.apache.http.HttpEntity; import org.apache.http.HttpResp ...

  9. Android开发——AsyncTask详解

    android提供AsynvTask,目的是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的. Android为了降低开发难度,提供了AsyncTask.Asyn ...

随机推荐

  1. string s = null 和 string s = “”的区别

    string s = null; 表示一个空串,没有占用了空间,不在内存中开辟空间 string s = "";在内存中开辟空间,但空间中没有值(""也是一个字 ...

  2. makefile文件编写

    文件转载自:http://www.cppblog.com/lapcca/archive/2010/11/26/134714.html 下面这篇文章讲的很清楚,基本的用法也很简单.   一.Makefi ...

  3. mysql 表空间

    开启了Innodb的innodb_file_per_table这个参数之后[innodb_file_per_table = 1],也就是启用InnoDB的独立表空间模式,便于管理.此时,在新建的inn ...

  4. ecshop绕过验证码暴力破解

    若验证码不匹配,并没有销毁当前验证码   所以就可以一次请求验证码图片后,只要不再刷新验证码就可以一直使用  1.获取正确的验证码   2. 1 2 3 4 5 6 7 8 9 10 11 12 13 ...

  5. 爱改名的小融 2(codevs 3149)

    3149 爱改名的小融 2  时间限制: 2 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description Wikioi上有个人 ...

  6. mysql php nginx 源码包下载地址

    http://mirror.cogentco.com/pub/mysql/MySQL-5.5/ http://mirrors.sohu.com/php/ http://nginx.org/downlo ...

  7. 第六步:Lucene查询索引

    package cn.harmel.lucene; import java.io.IOException; import java.nio.file.Paths; import org.apache. ...

  8. mac平台下面ruby环境搭建

    一.安装xcode 先安装 [Xcode](http://developer.apple.com/xcode/) 开发工具,它将帮你安装好 Unix 环境需要的开发包 二.安装 RVM curl -L ...

  9. google svn 服务器使用(免费SVN服务器)

    转自:http://hi.baidu.com/%C0%AF%B1%CA%B9%A4%D7%F7%CA%D2/blog/item/d6f6c6d7707d81d0a044df5f.html 1. 进入h ...

  10. Android学习 之 startActivityForResult 和 onActivityResult

    startActivityForResult 和 onActivityResult() 作用:主要用于 主Activity向调用的 子Activity 获得数据. 使用方法:在 主Activity写 ...