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. hdparm测试硬盘性能

    <1>Centos安装hdparm测试硬盘性能 一.安装hdparm yum install hdparm -y Linux学习,http:// linux.it.net.cn 二.评估读 ...

  2. VMware Snapshot 工作原理

    VMware中的快照是对VMDK在某个时间点的“拷贝”,这个“拷贝”并不是对VMDK文件的复制,而是保持磁盘文件和系统内存在该时间点的状态,以便在出现故障后虚拟机能够恢复到该时间点.如果对某个虚拟机创 ...

  3. Count Primes

    Count the number of prime numbers less than a non-negative number, n public int countPrimes(int n) { ...

  4. velocity的string转数字,numberTool

    velocity的string转数字,非常有意思: 1.通过java的包装类进行转换 #set($intString = "20") #set($Integer = 0) $Int ...

  5. 【转】velocity 显示List和Map方法

    一.遍历个map类型 1.先看后台java程序Java代码     Map<String,String> paramValues=new HashMap<String, String ...

  6. iOS UIDatePicker frame改变问题

    这种方法不行: pickerCtl = UIDatePicker(frame:pickerFrame) 但是这种却行 pickerCtl = UIDatePicker() pickerCtl!.fra ...

  7. (转)SQL server 容易让人误解的问题之 聚集表的物理顺序问题

    对于MS SQL server 数据库,有几个容易让人产生误解的问题,对于这几个问题,即使很多 SQL server DBA 都有错误认识或者认识不充分,所以我想撰文几篇,把这些容易理解错误的问题前前 ...

  8. Java for LeetCode 171 Excel Sheet Column Number

    Related to question Excel Sheet Column Title Given a column title as appear in an Excel sheet, retur ...

  9. 5.django笔记之form保存表单信息,动态select

    作者:刘耀 一.使用form保存用户输入过的信息 场景:例如 如果用户注册,那么他输入n多个表单之后,那么他提交是时候,如果错误返回的时候,那么需要重新再输入表单内容.这样会影响用户体验,所以,使用f ...

  10. July 31st, Week 32nd Sunday, 2016

    If you wept for the missing sunset, you would miss all the shining stars. 如果你为错过夕阳而哭泣,那你有可能也会错过灿烂的星空 ...