




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】怎么在Android中实现一个多线程断点续传下载功能
本篇文章给大家分享的是有关怎么在Android中实现一个多线程断点续传下载功能,在下觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着在下一起来看看吧。1、布局实现具体布局内容如下:<LinearLayout
xmlns:android="/apk/res/android"
xmlns:tools="/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity"
>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="下载路径"
/>
<EditText
android:id="@+id/ed_path"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="70:8080/web/youdao.exe"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载"
android:onClick="download"/>
<ProgressBar
android:id="@+id/pb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tv_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="下载:0%"/>
</LinearLayout>
2、自定义ProgressBarListener监听器接口新建自定义ProgressBarListener监听器接口,这个接口中定义两个方法,voidgetMax(intlength)用来获取下载文件的长度,voidgetDownload(intlength);用来获取每次下载的长度,这个方法中主要是在多线程中调用,子线程中获取到的数据传递到这两个接口方法中,然后在这两个接口方法中通过Handler将相应的长度信息传递到主线程,更新界面显示信息。具体代码实现如下:package
er;
/**
*
自定义进度条监听器
*
@author
liuyazhuang
*
*/
public
interface
ProgressBarListener
{
/**
*
获取文件的长度
*
@param
length
*/
void
getMax(int
length);
/**
*
获取每次下载的长度
*
@param
length
*/
void
getDownload(int
length);
}3.定义数据库的相关信息类DownloadDBHelper在这个实例中,我们将数据库的名称定义为download.db,我们需要保存主键id,文件下载后要保存的路径,每个线程的标识id,每个线程下载的文件数据块大小,所以,在创建的数据表中共有_id,path,threadid,downloadlength,详情见下图DownloadDBHelper实现的具体代码如下:package
com.example.db;
import
android.content.Context;
import
android.database.sqlite.SQLiteDatabase;
import
android.database.sqlite.SQLiteDatabase.CursorFactory;
import
android.database.sqlite.SQLiteOpenHelper;
/**
*
数据库相关类
*
@author
liuyazhuang
*
*/
public
class
DownloadDBHelper
extends
SQLiteOpenHelper
{
/**
*
数据库名称
*/
private
static
final
String
NAME
=
"download.db";
/**
*
原有的构造方法
*
@param
context
*
@param
name
*
@param
factory
*
@param
version
*/
public
DownloadDBHelper(Context
context,
String
name,
CursorFactory
factory,
int
version)
{
super(context,
name,
factory,
version);
}
/**
*
重载构造方法
*
@param
context
*/
public
DownloadDBHelper(Context
context){
super(context,
NAME,
null,
1);
}
/**
*
创建数据库时调用
*/
@Override
public
void
onCreate(SQLiteDatabase
db)
{
db.execSQL("create
table
download(_id
integer
primary
key
autoincrement,"
+
"path
text,"
+
"threadid
integer,"
+
"downloadlength
integer)");
}
/**
*
更新数据库时调用
*/
@Override
public
void
onUpgrade(SQLiteDatabase
db,
int
oldVersion,
int
newVersion)
{
}
}
4、创建DownloadProvider类DownloadProvider类继承自ContentProvider,提供操作数据库的方法,在这个类中,通过UriMatcher类匹配要操作的数据库,通过DownloadDBHelper对象来得到一个具体数据库实例,来对相应的数据库进行增、删、改、查操作。具体实现如下代码所示:package
vider;
import
com.example.db.DownloadDBHelper;
import
android.content.ContentProvider;
import
android.content.ContentValues;
import
android.content.UriMatcher;
import
android.database.Cursor;
import
android.database.sqlite.SQLiteDatabase;
import
android.database.sqlite.SQLiteOpenHelper;
import
.Uri;
/**
*
自定义ContentProvider实例
*
@author
liuyazhuang
*
*/
public
class
DownloadProvider
extends
ContentProvider
{
//实例化UriMatcher对象
private
static
UriMatcher
matcher
=
new
UriMatcher(UriMatcher.NO_MATCH);
//配置访问规则
private
static
final
String
AUTHORITY
=
"download";
//自定义常量
private
static
final
int
DOWANLOAD
=
10;
static{
//添加匹配的规则
matcher.addURI(AUTHORITY,
"download",
DOWANLOAD);
}
private
SQLiteOpenHelper
mOpenHelper;
@Override
public
boolean
onCreate()
{
mOpenHelper
=
new
DownloadDBHelper(getContext());
return
false;
}
@Override
public
Cursor
query(Uri
uri,
String[]
projection,
String
selection,
String[]
selectionArgs,
String
sortOrder)
{
//
TODO
Auto-generated
method
stub
Cursor
ret
=
null;
SQLiteDatabase
db
=
mOpenHelper.getReadableDatabase();
int
code
=
matcher.match(uri);
switch
(code)
{
case
DOWANLOAD:
ret
=
db.query("download",
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
break;
default:
break;
}
return
ret;
}
@Override
public
String
getType(Uri
uri)
{
//
TODO
Auto-generated
method
stub
return
null;
}
@Override
public
Uri
insert(Uri
uri,
ContentValues
values)
{
//
TODO
Auto-generated
method
stub
SQLiteDatabase
db
=
mOpenHelper.getWritableDatabase();
int
code
=
matcher.match(uri);
switch
(code)
{
case
DOWANLOAD:
db.insert("download",
"_id",
values);
break;
default:
break;
}
return
null;
}
@Override
public
int
delete(Uri
uri,
String
selection,
String[]
selectionArgs)
{
SQLiteDatabase
db
=
mOpenHelper.getWritableDatabase();
int
code
=
matcher.match(uri);
switch
(code)
{
case
DOWANLOAD:
db.delete("download",
selection,
selectionArgs);
break;
default:
break;
}
return
0;
}
@Override
public
int
update(Uri
uri,
ContentValues
values,
String
selection,
String[]
selectionArgs)
{
SQLiteDatabase
db
=
mOpenHelper.getWritableDatabase();
int
code
=
matcher.match(uri);
switch
(code)
{
case
DOWANLOAD:
db.update("download",
values,
selection,
selectionArgs);
break;
default:
break;
}
return
0;
}
}5、创建DownloadInfo实体类为了使程序更加面向对象化,这里我们建立DownloadInfo实体类来对数据库中的数据进行封装,DownloadInfo实体类中的数据字段与数据库中的字段相对应具体实现代码如下:package
com.example.domain;
/**
*
支持断点续传时,
*
要保存到数据库的信息
*
@author
liuyazhuang
*
*/
public
class
DownloadInfo
{
//主键id
private
int
_id;
//保存路径
private
String
path;
//线程的标识id
private
String
threadId;
//下载文件的大小
private
int
downloadSize;
public
DownloadInfo()
{
super();
}
public
DownloadInfo(int
_id,
String
path,
String
threadId,
int
downloadSize)
{
super();
this._id
=
_id;
this.path
=
path;
this.threadId
=
threadId;
this.downloadSize
=
downloadSize;
}
public
int
get_id()
{
return
_id;
}
public
void
set_id(int
_id)
{
this._id
=
_id;
}
public
String
getPath()
{
return
path;
}
public
void
setPath(String
path)
{
this.path
=
path;
}
public
String
getThreadId()
{
return
threadId;
}
public
void
setThreadId(String
threadId)
{
this.threadId
=
threadId;
}
public
int
getDownloadSize()
{
return
downloadSize;
}
public
void
setDownloadSize(int
downloadSize)
{
this.downloadSize
=
downloadSize;
}
}6、定义外界调用的操作数据库的方法类DownloadDaoDownloadDao类中封装了一系列操作数据库的方法,这个类不是直接操作数据库对象,而是通过ContentResolver这个对象来调用DownloadProvider中的方法来实现操作数据库的功能,这里用到了ContentResolver与ContentProvider这两个Android中非常重要的类。ContentProvider即内容提供者,主要是向外提供数据,简单理解就是一个应用程序可以通过ContentProvider向外提供操作本应用程序的接口,其他应用程序可以调用ContentProvider提供的接口来操作本应用程序的数据。ContentResolver内容接接收者,它可以接收ContentProvider的向外提供的数据。具体代码实现如下:package
com.example.dao;
import
android.content.ContentResolver;
import
android.content.ContentValues;
import
android.content.Context;
import
android.database.Cursor;
import
.Uri;
import
com.example.domain.DownloadInfo;
/**
*
保存下载文件信息的dao类
*
@author
liuyazhuang
*
*/
public
class
DownloadDao
{
/**
*
ContentResolver对象
*/
private
ContentResolver
cr;
public
DownloadDao(Context
context){
this.cr
=
context.getContentResolver();
}
/**
*
保存下载信息记录
*
@param
info
*/
public
void
save(DownloadInfo
info){
Uri
uri
=
Uri.parse("content://download/download");
ContentValues
values
=
new
ContentValues();
values.put("path",
info.getPath());
values.put("threadid",
info.getThreadId());
cr.insert(uri,
values);
}
/**
*
更新下载信息记录
*
@param
info
*/
public
void
update(DownloadInfo
info){
Uri
uri
=
Uri.parse("content://download/download");
ContentValues
values
=
new
ContentValues();
values.put("downloadlength",
info.getDownloadSize());
values.put("threadid",
info.getThreadId());
cr.update(uri,
values,
"
path
=
?
and
threadid
=
?
",
new
String[]{info.getPath(),
info.getThreadId()});
}
/**
*
删除下载信息记录
*
@param
info
*/
public
void
delete(DownloadInfo
info){
Uri
uri
=
Uri.parse("content://download/download");
cr.delete(uri,
"
path
=
?
and
threadid
=
?
",
new
String[]{info.getPath(),
info.getThreadId()});
}
/**
*
删除下载信息记录
*
@param
info
*/
public
void
delete(String
path){
Uri
uri
=
Uri.parse("content://download/download");
cr.delete(uri,
"
path
=
?
",
new
String[]{path});
}
/**
*
判断是否有下载记录
*
@param
path
*
@return
*/
public
boolean
isExist(String
path){
boolean
result
=
false;
Uri
uri
=
Uri.parse("content://download/download");
Cursor
cursor
=
cr.query(uri,
null,
"
path
=
?
",
new
String[]{path},
null);
if(cursor.moveToNext()){
result
=
true;
}
cursor.close();
return
result;
}
/**
*
计算所有的下载长度
*
@param
path
*
@return
*/
public
int
queryCount(String
path){
int
count
=
0;
Uri
uri
=
Uri.parse("content://download/download");
Cursor
cursor
=
cr.query(uri,
new
String[]{"downloadlength"},
"
path
=
?
",
new
String[]{path},
null);
while(cursor.moveToNext()){
int
len
=
cursor.getInt(0);
count
+=
len;
}
cursor.close();
return
count;
}
/**
*
计算每个线程的下载长度
*
@param
path
*
@return
*/
public
int
query(DownloadInfo
info){
int
count
=
0;
Uri
uri
=
Uri.parse("content://download/download");
Cursor
cursor
=
cr.query(uri,
new
String[]{"downloadlength"},
"
path
=
?
and
threadid
=
?",
new
String[]{info.getPath(),
info.getThreadId()},
null);
while(cursor.moveToNext()){
int
len
=
cursor.getInt(0);
count
+=
len;
}
cursor.close();
return
count;
}
}7、自定义线程类DownThread这里通过继承Thread的方式来实现自定义线程操作,在这个类中主要是实现文件的下载操作,在这个类中,定义了一系列与下载有关的实例变量来控制下载的数据,通过自定义监听器ProgressBarListener中的voidgetDownload(intlength)方法来跟新界面显示的进度信息,同时通过调用DownloadDao的方法来记录和更新数据的下载信息。具体实现代码如下:package
com.example.download;
import
java.io.File;
import
java.io.InputStream;
import
java.io.RandomAccessFile;
import
.HttpURLConnection;
import
.URL;
import
android.content.Context;
import
com.example.dao.DownloadDao;
import
com.example.domain.DownloadInfo;
import
er.ProgressBarListener;
/**
*
自定义线程类
*
@author
liuyazhuang
*
*/
public
class
DownloadThread
extends
Thread
{
//下载的线程id
private
int
threadId;
//下载的文件路径
private
String
path;
//保存的文件
private
File
file;
//下载的进度条更新的监听器
private
ProgressBarListener
listener;
//每条线程下载的数据量
private
int
block;
//下载的开始位置
private
int
startPosition;
//下载的结束位置
private
int
endPosition;
private
DownloadDao
downloadDao;
public
DownloadThread(int
threadId,
String
path,
File
file,
ProgressBarListener
listener,
int
block,
Context
context)
{
this.threadId
=
threadId;
this.path
=
path;
this.file
=
file;
this.listener
=
listener;
this.block
=
block;
this.downloadDao
=
new
DownloadDao(context);
this.startPosition
=
threadId
*
block;
this.endPosition
=
(threadId
+
1)
*
block
-
1;
}
@Override
public
void
run()
{
super.run();
try
{
//判断该线程是否有下载记录
DownloadInfo
info
=
new
DownloadInfo();
info.setPath(path);
info.setThreadId(String.valueOf(threadId));
int
length
=
downloadDao.query(info);
startPosition
+=
length;
//创建RandomAccessFile对象
RandomAccessFile
accessFile
=
new
RandomAccessFile(file,
"rwd");
//跳转到开始位置
accessFile.seek(startPosition);
URL
url
=
new
URL(path);
//打开http链接
HttpURLConnection
conn
=
(HttpURLConnection)
url.openConnection();
//设置超时时间
conn.setConnectTimeout(5000);
//指定请求方式为GET方式
conn.setRequestMethod("GET");
//指定下载的位置
conn.setRequestProperty("Range",
"bytes="+startPosition
+
"-"
+
endPosition);
//不用再去判断状态码是否为200
InputStream
in
=
conn.getInputStream();
byte[]
buffer
=
new
byte[1024];
int
len
=
0;
//该线程下载的总数据量
int
count
=
length;
while((len
=
in.read(buffer))
!=
-1){
accessFile.write(buffer,
0,
len);
//更新下载进度
listener.getDownload(len);
count
+=
len;
info.setDownloadSize(count);
//更新下载的信息
downloadDao.update(info);
}
accessFile.close();
in.close();
}
catch
(Exception
e)
{
//
TODO:
handle
exception
e.printStackTrace();
}
}
}8、新建下载的管理类DownloadManager这个类主要是对下载过程的管理,包括下载设置下载后文件要保存的位置,计算多线程中每个线程的数据下载量等等,同时相比《Android多线程下载示例》一文中,它多了多下载数据的记录与更新操作。具体实现代码如下:package
com.example.download;
import
java.io.File;
import
java.io.RandomAccessFile;
import
.HttpURLConnection;
import
.URL;
import
android.content.Context;
import
android.os.Environment;
import
com.example.dao.DownloadDao;
import
com.example.domain.DownloadInfo;
import
er.ProgressBarListener;
/**
*
文件下载管理器
*
@author
liuyazhuang
*
*/
public
class
DownloadManager
{
//下载线程的数量
private
static
final
int
TREAD_SIZE
=
3;
private
File
file;
private
DownloadDao
downloadDao;
private
Context
context;
public
DownloadManager(Context
context)
{
this.context
=
context;
this.downloadDao
=
new
DownloadDao(context);
}
/**
*
下载文件的方法
*
@param
path:下载文件的路径
*
@param
listener:自定义的下载文件监听接口
*
@throws
Exception
*/
public
void
download(String
path,
ProgressBarListener
listener)
throws
Exception{
URL
url
=
new
URL(path);
HttpURLConnection
conn
=
(HttpURLConnection)
url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
if(conn.getResponseCode()
==
200){
int
filesize
=
conn.getContentLength();
//设置进度条的最大长度
listener.getMax(filesize);
//判断下载记录是否存在
boolean
ret
=
downloadDao.isExist(path);
if(ret){
//得到下载的总长度,设置进度条的刻度
int
count
=
downloadDao.queryCount(path);
listener.getDownload(count);
}else{
//保存下载记录
for(int
i
=
0;
i
<
filesize;
i++){
DownloadInfo
info
=
new
DownloadInfo();
info.setPath(path);
info.setThreadId(String.valueOf(i));
//保存下载的记录信息
downloadDao.save(info);
}
}
//创建一个和服务器大小一样的文件
file
=
new
File(Environment.getExternalStorageDirectory(),
this.getFileName(path));
RandomAccessFile
accessFile
=
new
RandomAccessFile(file,
"rwd");
accessFile.setLength(filesize);
//要关闭RandomAccessFile对象
accessFile.close();
//计算出每条线程下载的数据量
int
block
=
filesize
%
TREAD_SIZE
==
0
?
(filesize
/
TREAD_SIZE)
:
(filesize
/
TREAD_SIZE
+1
);
//开启线程下载
for(int
i
=
0;
i
<
TREAD_SIZE;
i++){
new
DownloadThread(i,
path,
file,
listener,
block,
context).start();
}
}
}
/**
*
截取路径中的文件名称
*
@param
path:要截取文件名称的路径
*
@return:截取到的文件名称
*/
private
String
getFileName(String
path){
return
path.substring(path.lastIndexOf("/")
+
1);
}
}9、完善MainActivity在这个类中首先,找到页面中的各个控件,实现Button按钮的onClick事件,在onClick事件中开启一个线程进行下载操作,同时子线程中获取到的数据,通过handler与Message机制传递到主线程,更新界面显示,利用DownloadDao类中的方法来记录和更新下载数据。具体实现代码如下:package
com.example.multi;
import
android.app.Activity;
import
android.os.Bundle;
import
android.os.Handler;
import
android.os.Message;
import
android.view.Menu;
import
android.view.View;
import
android.widget.EditText;
import
android.widget.ProgressBar;
import
android.widget.TextView;
import
android.widget.Toast;
import
com.example.dao.DownloadDao;
import
com.example.download.DownloadManager;
import
er.ProgressBarListener;
/**
*
MainActivity整个应用程序的入口
*
@author
liuyazhuang
*
*/
public
class
MainActivity
extends
Activity
{
protected
static
final
int
ERROR_DOWNLOAD
=
0;
protected
static
final
int
SET_PROGRESS_MAX
=
1;
protected
static
final
int
UPDATE_PROGRESS
=
2;
private
EditText
ed_path;
private
ProgressBar
pb;
private
TextView
tv_info;
private
DownloadManager
manager;
private
DownloadDao
downloadDao;
//handler操作
private
Handler
mHandler
=
new
Handler(){
public
void
handleMessage(android.os.Message
msg)
{
switch
(msg.what)
{
case
ERROR_DOWNLOAD:
//提示用户下载失败
Toast.makeText(MainActivity.this,
"下载失败",
Toast.LENGTH_SHORT).show();
break;
case
SET_PROGRESS_MAX:
//得到最大值
int
max
=
(Integer)
msg.obj;
//设置进度条的最大值
pb.setMax(max);
break;
case
UPDATE_PROGRESS:
//获取当前下载的长度
int
currentprogress
=
pb.getProgress();
//获取新下载的长度
int
len
=
(Integer)
msg.obj;
//计算当前总下载长度
int
crrrentTotalProgress
=
currentprogress
+
len;
pb.setProgress(crrrentTotalProgress);
//获取总大小
int
maxProgress
=
pb.getMax();
//计算百分比
float
value
=
(float)currentprogress
/
(float)maxProgress;
int
percent
=
(int)
(value
*
100);
//显示下载的百分比
tv_info.setText("下载:"+percent+"%");
if(maxProgress
==
crrrentTotalProgress){
//删除下载记录
downloadDao.delete(ed_path.getText().toString());
}
break;
default:
break;
}
};
};
@Override
protected
void
onCreate(Bundle
savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.ed_path
=
(EditText)
super.findViewById(R.id.ed_path);
this.pb
=
(ProgressBar)
super.findViewById(R.id.pb);
this.tv_info
=
(TextView)
super.findViewById(R.id.tv_info);
this.manager
=
new
DownloadManager(this);
this.downloadDao
=
new
DownloadDao(this);
}
@Override
public
boolean
onCreateOptionsMenu(Menu
menu)
{
//
Inflate
the
menu;
this
adds
items
to
the
action
bar
if
it
is
present.
getMenuInflater().inflate(R.menu.main,
menu);
return
true;
}
public
void
download(View
v){
final
String
pat
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 河南省南阳完全学校2024~2025学年 高三第三次调研测试数学试卷附解析
- 2025届江苏省扬州市仪征市中考二模数学试卷
- 2024年山东省烟草专卖局(公司)笔试试题
- 2024年惠州龙门县招聘乡镇党建指导员真题
- 2024河南公务员考试行测真题(省直)
- 鞍山市立山区事业单位招聘笔试真题2024
- 石大学前儿童保育学课外必读:幼儿园游戏安全防护
- 2025年人工智能培训师试题
- 初一学生数学元认知策略水平的现状及其培养研究
- 自动化运输与货物标签识别技术-洞察阐释
- 2025年江西报业传媒集团招聘题库带答案分析
- 公司退货流程管理制度
- 矿产品销售合作合同范本
- 江苏省常州市联盟学校2022-2023学年高一下学期期末联考数学试题(学生版)
- 零售药店采购员岗位培训考核试题(带答案)
- 2025年成都市中考历史试题卷(含答案)
- 中国盐业集团有限公司所属企业招聘笔试题库2025
- DB23-T2326-2019-紫椴种质资源调查及评价技术规程-黑龙江省
- (人教2024版)英语七下期末全册分单元总复习课件(新教材)
- 2025年中考生物考前必背全册知识点梳理(全国)七年级上册
- 2025年二年级语文期末复习计划
评论
0/150
提交评论