【移动应用开发技术】怎么在Android中实现一个多线程断点续传下载功能_第1页
【移动应用开发技术】怎么在Android中实现一个多线程断点续传下载功能_第2页
【移动应用开发技术】怎么在Android中实现一个多线程断点续传下载功能_第3页
【移动应用开发技术】怎么在Android中实现一个多线程断点续传下载功能_第4页
【移动应用开发技术】怎么在Android中实现一个多线程断点续传下载功能_第5页
已阅读5页,还剩25页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】怎么在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. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论