




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】怎么在Android中使用AsyncTask实现一个多任务多线程断点续传下载功能
怎么在Android中使用AsyncTask实现一个多任务多线程断点续传下载功能?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。1、Downloador类package
com.bbk.lling.multitaskdownload.downloador;
import
android.content.Context;
import
android.content.Intent;
import
android.os.AsyncTask;
import
android.os.Bundle;
import
android.os.Handler;
import
android.os.Message;
import
android.text.TextUtils;
import
android.util.Log;
import
android.widget.Toast;
import
com.bbk.lling.multitaskdownload.beans.AppContent;
import
com.bbk.lling.multitaskdownload.beans.DownloadInfo;
import
com.bbk.lling.multitaskdownload.db.DownloadFileDAO;
import
com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;
import
com.bbk.lling.multitaskdownload.utils.DownloadUtils;
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
java.util.ArrayList;
import
java.util.List;
import
java.util.concurrent.Executor;
import
java.util.concurrent.Executors;
/**
*
@Class:
Downloador
*
@Description:
任务下载器
*
@author:
lling(/liuling)
*
@Date:
2015/10/13
*/
public
class
Downloador
{
public
static
final
String
TAG
=
"Downloador";
private
static
final
int
THREAD_POOL_SIZE
=
9;
//线程池大小为9
private
static
final
int
THREAD_NUM
=
3;
//每个文件3个线程下载
private
static
final
int
GET_LENGTH_SUCCESS
=
1;
public
static
final
Executor
THREAD_POOL_EXECUTOR
=
Executors.newFixedThreadPool(THREAD_POOL_SIZE);
private
List<DownloadTask>
tasks;
private
InnerHandler
handler
=
new
InnerHandler();
private
AppContent
appContent;
//待下载的应用
private
long
downloadLength;
//下载过程中记录已下载大小
private
long
fileLength;
private
Context
context;
private
String
downloadPath;
public
Downloador(Context
context,
AppContent
appContent)
{
this.context
=
context;
this.appContent
=
appContent;
this.downloadPath
=
DownloadUtils.getDownloadPath();
}
/**
*
开始下载
*/
public
void
download()
{
if(TextUtils.isEmpty(downloadPath))
{
Toast.makeText(context,
"未找到SD卡",
Toast.LENGTH_SHORT).show();
return;
}
if(appContent
==
null)
{
throw
new
IllegalArgumentException("download
content
can
not
be
null");
}
new
Thread()
{
@Override
public
void
run()
{
//获取文件大小
HttpClient
client
=
new
DefaultHttpClient();
HttpGet
request
=
new
HttpGet(appContent.getUrl());
HttpResponse
response
=
null;
try
{
response
=
client.execute(request);
fileLength
=
response.getEntity().getContentLength();
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
finally
{
if
(request
!=
null)
{
request.abort();
}
}
//计算出该文件已经下载的总长度
List<DownloadInfo>
lists
=
DownloadInfoDAO.getInstance(context.getApplicationContext())
.getDownloadInfosByUrl(appContent.getUrl());
for
(DownloadInfo
info
:
lists)
{
downloadLength
+=
info.getDownloadLength();
}
//插入文件下载记录到数据库
DownloadFileDAO.getInstance(context.getApplicationContext()).insertDownloadFile(appContent);
Message.obtain(handler,
GET_LENGTH_SUCCESS).sendToTarget();
}
}.start();
}
/**
*
开始创建AsyncTask下载
*/
private
void
beginDownload()
{
Log.e(TAG,
"beginDownload"
+
appContent.getUrl());
appContent.setStatus(AppContent.Status.WAITING);
long
blockLength
=
fileLength
/
THREAD_NUM;
for
(int
i
=
0;
i
<
THREAD_NUM;
i++)
{
long
beginPosition
=
i
*
blockLength;//每条线程下载的开始位置
long
endPosition
=
(i
+
1)
*
blockLength;//每条线程下载的结束位置
if
(i
==
(THREAD_NUM
-
1))
{
endPosition
=
fileLength;//如果整个文件的大小不为线程个数的整数倍,则最后一个线程的结束位置即为文件的总长度
}
DownloadTask
task
=
new
DownloadTask(i,
beginPosition,
endPosition,
this,
context);
task.executeOnExecutor(THREAD_POOL_EXECUTOR,
appContent.getUrl());
if(tasks
==
null)
{
tasks
=
new
ArrayList<DownloadTask>();
}
tasks.add(task);
}
}
/**
*
暂停下载
*/
public
void
pause()
{
for
(DownloadTask
task
:
tasks)
{
if
(task
!=
null
&&
(task.getStatus()
==
AsyncTask.Status.RUNNING
||
!task.isCancelled()))
{
task.cancel(true);
}
}
tasks.clear();
appContent.setStatus(AppContent.Status.PAUSED);
DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);
}
/**
*
将已下载大小归零
*/
protected
synchronized
void
resetDownloadLength()
{
this.downloadLength
=
0;
}
/**
*
添加已下载大小
*
多线程访问需加锁
*
@param
size
*/
protected
synchronized
void
updateDownloadLength(long
size){
this.downloadLength
+=
size;
//通知更新界面
int
percent
=
(int)((float)downloadLength
*
100
/
(float)fileLength);
appContent.setDownloadPercent(percent);
if(percent
==
100
||
downloadLength
==
fileLength)
{
appContent.setDownloadPercent(100);
//上面计算有时候会有点误差,算到percent=99
appContent.setStatus(AppContent.Status.FINISHED);
DownloadFileDAO.getInstance(context.getApplicationContext()).updateDownloadFile(appContent);
}
Intent
intent
=
new
Intent(Constants.DOWNLOAD_MSG);
if(appContent.getStatus()
==
AppContent.Status.WAITING)
{
appContent.setStatus(AppContent.Status.DOWNLOADING);
}
Bundle
bundle
=
new
Bundle();
bundle.putParcelable("appContent",
appContent);
intent.putExtras(bundle);
context.sendBroadcast(intent);
}
protected
String
getDownloadPath()
{
return
downloadPath;
}
private
class
InnerHandler
extends
Handler
{
@Override
public
void
handleMessage(Message
msg)
{
switch
(msg.what)
{
case
GET_LENGTH_SUCCESS
:
beginDownload();
break;
}
super.handleMessage(msg);
}
}
}2、DownloadTask类package
com.bbk.lling.multitaskdownload.downloador;
import
android.content.Context;
import
android.os.AsyncTask;
import
android.util.Log;
import
com.bbk.lling.multitaskdownload.beans.DownloadInfo;
import
com.bbk.lling.multitaskdownload.db.DownloadInfoDAO;
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.IOException;
import
java.io.InputStream;
import
java.io.OutputStream;
import
java.io.RandomAccessFile;
import
.MalformedURLException;
/**
*
@Class:
DownloadTask
*
@Description:
文件下载AsyncTask
*
@author:
lling(/liuling)
*
@Date:
2015/10/13
*/
public
class
DownloadTask
extends
AsyncTask<String,
Integer
,
Long>
{
private
static
final
String
TAG
=
"DownloadTask";
private
int
taskId;
private
long
beginPosition;
private
long
endPosition;
private
long
downloadLength;
private
String
url;
private
Downloador
downloador;
private
DownloadInfoDAO
downloadInfoDAO;
public
DownloadTask(int
taskId,
long
beginPosition,
long
endPosition,
Downloador
downloador,
Context
context)
{
this.taskId
=
taskId;
this.beginPosition
=
beginPosition;
this.endPosition
=
endPosition;
this.downloador
=
downloador;
downloadInfoDAO
=
DownloadInfoDAO.getInstance(context.getApplicationContext());
}
@Override
protected
void
onPreExecute()
{
Log.e(TAG,
"onPreExecute");
}
@Override
protected
void
onPostExecute(Long
aLong)
{
Log.e(TAG,
url
+
"taskId:"
+
taskId
+
"executed");
//
downloador.updateDownloadInfo(null);
}
@Override
protected
void
onProgressUpdate(Integer...
values)
{
//通知downloador增加已下载大小
//
downloador.updateDownloadLength(values[0]);
}
@Override
protected
void
onCancelled()
{
Log.e(TAG,
"onCancelled");
//
downloador.updateDownloadInfo(null);
}
@Override
protected
Long
doInBackground(String...
params)
{
//这里加判断的作用是:如果还处于等待就暂停了,运行到这里已经cancel了,就直接退出
if(isCancelled())
{
return
null;
}
url
=
params[0];
if(url
==
null)
{
return
null;
}
HttpClient
client
=
new
DefaultHttpClient();
HttpGet
request
=
new
HttpGet(url);
HttpResponse
response;
InputStream
is;
RandomAccessFile
fos
=
null;
OutputStream
output
=
null;
DownloadInfo
downloadInfo
=
null;
try
{
//本地文件
File
file
=
new
File(downloador.getDownloadPath()
+
File.separator
+
url.substring(url.lastIndexOf("/")
+
1));
//获取之前下载保存的信息
downloadInfo
=
downloadInfoDAO.getDownloadInfoByTaskIdAndUrl(taskId,
url);
//从之前结束的位置继续下载
//这里加了判断file.exists(),判断是否被用户删除了,如果文件没有下载完,但是已经被用户删除了,则重新下载
if(file.exists()
&&
downloadInfo
!=
null)
{
if(downloadInfo.isDownloadSuccess()
==
1)
{
//下载完成直接结束
return
null;
}
beginPosition
=
beginPosition
+
downloadInfo.getDownloadLength();
downloadLength
=
downloadInfo.getDownloadLength();
}
if(!file.exists())
{
//如果此task已经下载完,但是文件被用户删除,则需要重新设置已下载长度,重新下载
downloador.resetDownloadLength();
}
//设置下载的数据位置beginPosition字节到endPosition字节
Header
header_size
=
new
BasicHeader("Range",
"bytes="
+
beginPosition
+
"-"
+
endPosition);
request.addHeader(header_size);
//执行请求获取下载输入流
response
=
client.execute(request);
is
=
response.getEntity().getContent();
//创建文件输出流
fos
=
new
RandomAccessFile(file,
"rw");
//从文件的size以后的位置开始写入
fos.seek(beginPosition);
byte
buffer
[]
=
new
byte[1024];
int
inputSize
=
-1;
while((inputSize
=
is.read(buffer))
!=
-1)
{
fos.write(buffer,
0,
inputSize);
downloadLength
+=
inputSize;
downloador.updateDownloadLength(inputSize);
//如果暂停了,需要将下载信息存入数据库
if
(isCancelled())
{
if(downloadInfo
==
null)
{
downloadInfo
=
new
DownloadInfo();
}
downloadInfo.setUrl(url);
downloadInfo.setDownloadLength(downloadLength);
downloadInfo.setTaskId(taskId);
downloadInfo.setDownloadSuccess(0);
//保存下载信息到数据库
downloadInfoDAO.insertDownloadInfo(downloadInfo);
return
null;
}
}
}
catch
(MalformedURLException
e)
{
Log.e(TAG,
e.getMessage());
}
catch
(IOException
e)
{
Log.e(TAG,
e.getMessage());
}
finally{
try{
if
(request
!=
null)
{
request.abort();
}
if(output
!=
null)
{
output.close();
}
if(fos
!=
null)
{
fos.close();
}
}
catch(Exception
e)
{
e.printStackTrace();
}
}
//执行到这里,说明该task已经下载完了
if(downloadInfo
==
null)
{
downloadInfo
=
new
DownloadInfo();
}
downloadInfo.setUrl(url);
downloadInfo.setDownloadLength(downloadLength);
downloadInfo.setTaskId(taskId);
downloadInfo.setDownloadSuccess(1);
//保存下载信息到数据库
downloadInfoDAO.insertDownloadInfo(downloadInfo);
return
null;
}
}Downloador和DownloadTask只这个例子的核心代码,下面是关于数据库的,因为要实现断点续传必须要在暂停的时候将每个线程下载的位置记录下来,方便下次继续下载时读取。这里有两个表,一个是存放每个文件的下载状态的,一个是存放每个文件对应的每个线程的下载状态的。3、DBHelperpackage
com.bbk.lling.multitaskdownload.db;
import
android.content.Context;
import
android.database.sqlite.SQLiteDatabase;
import
android.database.sqlite.SQLiteOpenHelper;
/**
*
@Class:
DBHelper
*
@Description:
数据库帮助类
*
@author:
lling(/liuling)
*
@Date:
2015/10/14
*/
public
class
DBHelper
extends
SQLiteOpenHelper
{
public
DBHelper(Context
context)
{
super(context,
"download.db",
null,
1);
}
@Override
public
void
onCreate(SQLiteDatabase
db)
{
db.execSQL("create
table
download_info(_id
INTEGER
PRIMARY
KEY
AUTOINCREMENT,
task_id
INTEGER,
"
+
"download_length
INTEGER,
url
VARCHAR(255),
is_success
INTEGER)");
db.execSQL("create
table
download_file(_id
INTEGER
PRIMARY
KEY
AUTOINCREMENT,
app_name
VARCHAR(255),
"
+
"url
VARCHAR(255),
download_percent
INTEGER,
status
INTEGER)");
}
@Override
public
void
onUpgrade(SQLiteDatabase
db,
int
oldVersion,
int
newVersion)
{
}
}4、DownloadFileDAO,文件下载状态的数据库操作类package
com.bbk.lling.multitaskdownload.db;
import
android.content.Context;
import
android.database.Cursor;
import
android.database.sqlite.SQLiteDatabase;
import
android.text.TextUtils;
import
android.util.Log;
import
com.bbk.lling.multitaskdownload.beans.AppContent;
import
java.util.ArrayList;
import
java.util.List;
/**
*
@Class:
DownloadFileDAO
*
@Description:
每个文件下载状态记录的数据库操作类
*
@author:
lling(/liuling)
*
@Date:
2015/10/13
*/
public
class
DownloadFileDAO
{
private
static
final
String
TAG
=
"DownloadFileDAO";
private
static
DownloadFileDAO
dao=null;
private
Context
context;
private
DownloadFileDAO(Context
context)
{
this.context=context;
}
synchronized
public
static
DownloadFileDAO
getInstance(Context
context){
if(dao==null){
dao=new
DownloadFileDAO(context);
}
return
dao;
}
/**
*
获取数据库连接
*
@return
*/
public
SQLiteDatabase
getConnection()
{
SQLiteDatabase
sqliteDatabase
=
null;
try
{
sqliteDatabase=
new
DBHelper(context).getReadableDatabase();
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
return
sqliteDatabase;
}
/**
*
插入数据
*
@param
appContent
*/
public
void
insertDownloadFile(AppContent
appContent)
{
if(appContent
==
null)
{
return;
}
//如果本地已经存在,直接修改
if(getAppContentByUrl(appContent.getUrl())
!=
null)
{
updateDownloadFile(appContent);
return;
}
SQLiteDatabase
database
=
getConnection();
try
{
String
sql
=
"insert
into
download_file(app_name,
url,
download_percent,
status)
values
(?,?,?,?)";
Object[]
bindArgs
=
{
appContent.getName(),
appContent.getUrl(),
appContent.getDownloadPercent()
,
appContent.getStatus().getValue()};
database.execSQL(sql,
bindArgs);
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
finally
{
if
(null
!=
database)
{
database.close();
}
}
}
/**
*
根据url获取下载文件信息
*
@param
url
*
@return
*/
public
AppContent
getAppContentByUrl(String
url)
{
if(TextUtils.isEmpty(url))
{
return
null;
}
SQLiteDatabase
database
=
getConnection();
AppContent
appContent
=
null;
Cursor
cursor
=
null;
try
{
String
sql
=
"select
*
from
download_file
where
url=?";
cursor
=
database.rawQuery(sql,
new
String[]
{
url
});
if
(cursor.moveToNext())
{
appContent
=
new
AppContent(cursor.getString(1),
cursor.getString(2));
appContent.setDownloadPercent(cursor.getInt(3));
appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4)));
}
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
finally
{
if
(null
!=
database)
{
database.close();
}
if
(null
!=
cursor)
{
cursor.close();
}
}
return
appContent;
}
/**
*
更新下载信息
*
@param
appContent
*/
public
void
updateDownloadFile(AppContent
appContent)
{
if(appContent
==
null)
{
return;
}
SQLiteDatabase
database
=
getConnection();
try
{
Log.e(TAG,
"update
download_file,app
name:"
+
appContent.getName()
+
",url:"
+
appContent.getUrl()
+
",percent"
+
appContent.getDownloadPercent()
+
",status:"
+
appContent.getStatus().getValue());
String
sql
=
"update
download_file
set
app_name=?,
url=?,
download_percent=?,
status=?
where
url=?";
Object[]
bindArgs
=
{appContent.getName(),
appContent.getUrl(),
appContent.getDownloadPercent()
,
appContent.getStatus().getValue(),
appContent.getUrl()};
database.execSQL(sql,
bindArgs);
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
finally
{
if
(null
!=
database)
{
database.close();
}
}
}
/**
*
获取所有下载文件记录
*
@return
*/
public
List<AppContent>
getAll()
{
SQLiteDatabase
database
=
getConnection();
List<AppContent>
list
=
new
ArrayList<AppContent>();
Cursor
cursor
=
null;
try
{
String
sql
=
"select
*
from
download_file";
cursor
=
database.rawQuery(sql,
null);
while
(cursor.moveToNext())
{
AppContent
appContent
=
new
AppContent(cursor.getString(1),
cursor.getString(2));
appContent.setDownloadPercent(cursor.getInt(3));
appContent.setStatus(AppContent.Status.getByValue(cursor.getInt(4)));
list.add(appContent);
}
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
finally
{
if
(null
!=
database)
{
database.close();
}
if
(null
!=
cursor)
{
cursor.close();
}
}
return
list;
}
/**
*
根据url删除记录
*
@param
url
*/
public
void
delByUrl(String
url)
{
if(TextUtils.isEmpty(url))
{
return;
}
SQLiteDatabase
database
=
getConnection();
try
{
String
sql
=
"delete
from
download_file
where
url=?";
Object[]
bindArgs
=
{
url
};
database.execSQL(sql,
bindArgs);
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
finally
{
if
(null
!=
database)
{
database.close();
}
}
}
}5、DownloadInfoDAO,每个线程对应下载状态的数据库操作类package
com.bbk.lling.multitaskdownload.db;
import
android.content.Context;
import
android.database.Cursor;
import
android.database.sqlite.SQLiteDatabase;
import
android.text.TextUtils;
import
android.util.Log;
import
com.bbk.lling.multitaskdownload.beans.DownloadInfo;
import
java.util.ArrayList;
import
java.util.List;
/**
*
@Class:
DownloadInfoDAO
*
@Description:
每个单独线程下载信息记录的数据库操作类
*
@author:
lling(/liuling)
*
@Date:
2015/10/13
*/
public
class
DownloadInfoDAO
{
private
static
final
String
TAG
=
"DownloadInfoDAO";
private
static
DownloadInfoDAO
dao=null;
private
Context
context;
private
DownloadInfoDAO(Context
context)
{
this.context=context;
}
synchronized
public
static
DownloadInfoDAO
getInstance(Context
context){
if(dao==null){
dao=new
DownloadInfoDAO(context);
}
return
dao;
}
/**
*
获取数据库连接
*
@return
*/
public
SQLiteDatabase
getConnection()
{
SQLiteDatabase
sqliteDatabase
=
null;
try
{
sqliteDatabase=
new
DBHelper(context).getReadableDatabase();
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
return
sqliteDatabase;
}
/**
*
插入数据
*
@param
downloadInfo
*/
public
void
insertDownloadInfo(DownloadInfo
downloadInfo)
{
if(downloadInfo
==
null)
{
return;
}
//如果本地已经存在,直接修改
if(getDownloadInfoByTaskIdAndUrl(downloadInfo.getTaskId(),
downloadInfo.getUrl())
!=
null)
{
updateDownloadInfo(downloadInfo);
return;
}
SQLiteDatabase
database
=
getConnection();
try
{
String
sql
=
"insert
into
download_info(task_id,
download_length,
url,
is_success)
values
(?,?,?,?)";
Object[]
bindArgs
=
{
downloadInfo.getTaskId(),
downloadInfo.getDownloadLength(),
downloadInfo.getUrl(),
downloadInfo.isDownloadSuccess()};
database.execSQL(sql,
bindArgs);
}
catch
(Exception
e)
{
Log.e(TAG,
e.getMessage());
}
finally
{
if
(null
!=
database)
{
database.close();
}
}
}
public
List<DownloadInfo>
getDownloadInfosByUrl(String
url)
{
if(TextUtils.isEmpty(url))
{
return
null;
}
SQLiteDatabase
database
=
getConnection();
List<DownloadInfo>
list
=
new
ArrayList<DownloadInfo>();
Cursor
cursor
=
null;
try
{
String
sql
=
"select
*
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 石棉矿物学与应用考核试卷
- 2024年宠物水族项目资金需求报告代可行性研究报告
- 2024年发酵合成控制系统项目投资申请报告代可行性研究报告
- 2024年高压液压阀项目投资申请报告代可行性研究报告
- 网红网红民宿特色床品租赁协议
- 拼多多农产品电商平台运营管理代运营服务合同
- 2025年中国半导体封装材料行业市场规模及投资前景预测分析报告
- 物流园区物流园区物业管理与运营管理服务协议
- 网络游戏虚拟道具版权许可使用与品牌合作开发补充合同
- 文化创意产业园区股权合作与产业园区电子商务合同
- 中班语言学习活动优化计划
- 玻璃体积血的治疗
- 2025年货物购销合同范本
- 2025年教育管理与政策研究考试试题及答案
- 2025届北京市北京一零一中学生物七下期末质量检测试题含解析
- 2025Q1 BrandOS出海品牌社媒影响力榜单-OneSight
- 2025陕西延安通和电业有限责任公司供电服务用工招聘103人笔试参考题库附带答案详解
- 《生成式人工智能职业技能评估规范》
- 颁奖礼仪队培训体系
- 2025年新媒体运营专员面试题及答案
- 心血管-肾脏-代谢综合征患者的综合管理中国专家共识2025解读-1
评论
0/150
提交评论