用Service实现断点下载



总体的思路:

在下载文件时,将进度写入数据库,同时通知该ContentProvider的观察者更新页面,这个通知过程不要太频繁,我设置了10次,否则页面会灰常卡。

如果异常中断(网络中断或程序被kill)也没有关系,因为数据库中有记录,只要从该文件的断点位置继续下载就可以了。关键在于一切以数据库为准

就可以了。

同时要注意的是:

1、自始至终处理的都是同一个PO对象,没有直接用类的成员变量,因为下次启动线程,成员变量会变化,导致诡异的下载文件不对应。

2、启动线程后,将该线程保存进static类变量 downloaders 里,方便在Service类外进行停止线程。

3、因为有一个或多个下载进度的页面,所以在下载更新数据库的同时,进行了通知观察者的操作,当然也可以有其他的方法,比如,

1)在Activity里添加一个线程时刻扫描数据库等等,不过感觉没有这个好。

2)也有用更新进度的时候,不断发广播的方式,在Activity中注册广播接收者更新页

大家有更好的办法请回复。

红色字体部分为关键,其他都是浮云。。

/**

*

* 由于该Service用于多线程下载文件,需要确保原子性,一直处理一个DownLoadFilePO对象

*

* <p>detailed comment

* @see

* @since 1.0

*/

public class DownLoadService extends Service {

private static final String TAG = "DownLoadFileService";

private String downloadUrl;

private boolean showToast;

public static Map<String, DownLoadThread> downloaders = new HashMap<String, DownLoadThread>();

@Override

public IBinder onBind(Intent arg0) {

return null;

}

@Override

public void onCreate() {

//只执行一次

super.onCreate();

}

@Override

@Deprecated

public void onStart(Intent intent, int startId) {

super.onStart(intent, startId);

if (intent == null) {

return;

}

downloadUrl = intent.getStringExtra("downloadUrl");

showToast = intent.getBooleanExtra("showToast", false);

DownLoadThread downLoadThread = new DownLoadThread(downloadUrl);

Thread thread = new Thread(downLoadThread);

thread.start();

}

/**

*

* 下载线程

*

* <p>detailed comment

* @see

* @since 1.0

*/

public class DownLoadThread implements Runnable {

String url;

boolean downloading = true;

DownLoadThread(String downloadUrl) {

this.url = downloadUrl;

}

/**

* 终止线程

*/

public void stop() {

this.downloading = false;

}

@Override

public void run() {

//查看是否已经在下载

DownLoadFilePO po = queryByDownLoadUrl(url);

if (po != null) {

if (showToast) {

showToast(getResources().getString(R.string.this_file_is_downloaded));

} else {

downloaders.put(downloadUrl, this);

showToast(getResources().getString(R.string.this_file_begin_download));

download(po);

}

} else {

po = init(url);

initProgress(po);

downloaders.put(downloadUrl, this);

download(po);

}

}

/**

* 下载

*/

private void download(DownLoadFilePO po) {

HttpURLConnection connection = null;

RandomAccessFile randomAccessFile = null;

InputStream is = null;

try {

URL url = new URL(downloadUrl);

connection = (HttpURLConnection) url.openConnection();

connection.setConnectTimeout(5000);

connection.setRequestMethod("GET");

// 设置远程文件的下载范围,格式为Range:bytes x-y;

connection.setRequestProperty("Range",

"bytes=" + po.getCurrent_size() + "-" + po.getTotal_size());

//设置本地文件的下载开始位置

File localFile = new File(po.getFile());

randomAccessFile = new RandomAccessFile(localFile, "rwd");

randomAccessFile.seek(po.getCurrent_size());

is = connection.getInputStream();

byte[] buffer = new byte[1024000];

int length = -1;

int count = 0;

while ((length = is.read(buffer)) != -1) {

randomAccessFile.write(buffer, 0, length);

// 更新数据库中的下载信息

po.setCurrent_size(po.getCurrent_size() + length);

updateProgress(po);

// 用消息将下载信息传给进度条,对进度条进行更新

count++;

if (count % 10 == 0) {

getContentResolver().notifyChange(ConstantUtil.uri, null);//如果改变数据,则通知所有人

Log.v(TAG, po.getName() + "=======下载进度======" + po.getCurrent_size());

}

if (!downloading) {

Log.v(TAG, po.getName() + "=======暂停====" + po.getCurrent_size());

downloaders.remove(po.getDownloadUrl());

return;

}

}

//下载完成

if (po.getCurrent_size() == po.getTotal_size()) {

finishProgress(po);

}

downloaders.remove(po.getDownloadUrl());

} catch (Exception e) {

getContentResolver().notifyChange(ConstantUtil.uri, null);//如果改变数据,则通知所有人

this.stop();

e.printStackTrace();

} finally {

try {

is.close();

randomAccessFile.close();

connection.disconnect();

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

/**

* 初始化

*/

private DownLoadFilePO init(String downloadUrl) {

DownLoadFilePO po = new DownLoadFilePO();

try {

if (!FileUtil.checkFileDir()) {

return null;

}

URL url = new URL(downloadUrl);

HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.setConnectTimeout(5000);

connection.setRequestMethod("GET");

int fileSize = connection.getContentLength();

//总大小

String fileName = FileUtil.getFileNameByUrl(downloadUrl);

try {

fileName = URLDecoder.decode(fileName, "UTF-8");

} catch (UnsupportedEncodingException e) {

Log.e(TAG, e.getMessage());

}

File localFile = new File(ConstantUtil.downloadFileDir + fileName);

//判断同名文件已经存在,如存在同名的文件,需修改文件名称 sample.mp4 ==>sample(1).mp4

int i = 1;

while (localFile.exists()) {

int index = fileName.lastIndexOf(".");

fileName = fileName.substring(0, index) + "(" + i + ")"

+ fileName.substring(index, fileName.length());

localFile = new File(ConstantUtil.downloadFileDir + fileName);

i++;

}

localFile.createNewFile();

i = 0;

//            }

// 本地访问文件

RandomAccessFile accessFile = new RandomAccessFile(localFile, "rwd");

accessFile.setLength(fileSize);

accessFile.close();

connection.disconnect();

po.setName(fileName);

po.setDownloadUrl(downloadUrl);

po.setCurrent_size(0L);

po.setTotal_size(fileSize);

po.setFile(localFile.getAbsolutePath());

} catch (Exception e) {

e.printStackTrace();

}

return po;

}

/**

* 向数据库中插入初始化数据

*/

private void initProgress(DownLoadFilePO po) {

ContentValues values = new ContentValues();

values.put(DataBaseUtil.DOWNLOAD_FILE_NAME, po.getName());

values.put(DataBaseUtil.CREATE_FILE_DATE, new Date().toString());

values.put(DataBaseUtil.DOWNLOAD_FILE_TOTAL_SIZE, po.getTotal_size());

values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, 0);

values.put(DataBaseUtil.DOWNLOAD_FINISHED, false);

values.put(DataBaseUtil.DOWNLOAD_URL, po.getDownloadUrl());

values.put(DataBaseUtil.DONWLOAD_FILE_PATH, po.getFile());

this.getContentResolver().insert(ConstantUtil.uri, values); //向ContentProvider插入数据

}

/**

*

* 更新进度

*

*<p>detail comment

*@see

*@since

*

*/

private void updateProgress(DownLoadFilePO po) {

ContentValues values = new ContentValues();

values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, po.getCurrent_size());

this.getContentResolver().update(ConstantUtil.uri, values,

DataBaseUtil.DOWNLOAD_URL + "=?", new String[] { po.getDownloadUrl() });

}

/**

*

* 完成下载

*

*<p>detail comment

*@param currentSize

*@see

*@since

*

*/

private void finishProgress(DownLoadFilePO po) {

ContentValues values = new ContentValues();

values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, po.getTotal_size());

values.put(DataBaseUtil.DOWNLOAD_FINISHED, true);

this.getContentResolver().update(ConstantUtil.uri, values,

DataBaseUtil.DOWNLOAD_URL + "=?", new String[] { po.getDownloadUrl() });

getContentResolver().notifyChange(ConstantUtil.uri, null);

}

/**

*

* 查看该url是否在下载

*

*<p>detail comment

*@param url

*@return

*@see

*@since

*

*/

private DownLoadFilePO queryByDownLoadUrl(String url) {

Cursor cursor = this.getContentResolver().query(

ConstantUtil.uri,

new String[] { DataBaseUtil.DOWNLOAD_FILE_NAME,

DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE,

DataBaseUtil.DOWNLOAD_FILE_TOTAL_SIZE, DataBaseUtil.DONWLOAD_FILE_PATH,

DataBaseUtil.DOWNLOAD_FINISHED }, DataBaseUtil.DOWNLOAD_URL + "=?",

new String[] { url }, null);

if (cursor.moveToNext()) {

DownLoadFilePO po = new DownLoadFilePO();

for (int i = 0; i < cursor.getCount(); i++) {

cursor.moveToPosition(i);

po.setName(cursor.getString(0));

po.setCurrent_size(cursor.getLong(1));

po.setTotal_size(cursor.getLong(2));

po.setFile(cursor.getString(3));

po.setDownloadUrl(url);

}

return po;

}

return null;

}

private void showToast(final String toast) {

Handler handler = new Handler(Looper.getMainLooper());

handler.post(new Runnable() {

public void run() {

Toast.makeText(DownLoadService.this, toast, Toast.LENGTH_SHORT).show();

}

});

}

}



用Service实现断点下载

时间: 2024-10-10 09:07:32

用Service实现断点下载的相关文章

后台多任务多线程断点下载

忘了上图: 多线程断点下载其实不是很难,主要就是三个方面: 1.根据文件的大小和下载线程的数量,确定每个下载线程要下载的分割文件的大小: 2.记录每个下载线程已经下载完成的进度: 3.将每个线程下载的分割的文件合并到一个文件中. 那么怎么将远程的一个文件分割成三部分来下载呢?其实在HTTP协议中,有一个Range字段,用于客户端到服务器端的请求,可通过该字段指定下载文件的某一段大小,及其单位,格式为:Range: bytes x - y,eg:Range: bytes=0-100 下载从第0 -

多线程断点下载详解

首先先理清一下逻辑,后面会贴上源代码,里面有详细的解释,根据前面的逻辑去看后面的源码能够很快掌握断点下载.续传也是同样的道理. 看源码之前需要了解的知识:使用Http的Range头字段可以指定每条线程从文件的什么位置开始下载,下载到什么位置为止,如:指定从文件的2M位置开始下载,下载到位置(4M-1byte)为止,代码如下:HttpURLConnection.setRequestProperty("Range", "bytes=2097152-4194303");

安卓(android)之实现断点下载功能

一.建立实体类 1.文件实体类 package com.example.zjw.myapplication.dao; import java.io.Serializable; /** * 预下载文件实体类 * Created by Administrator on 2016/6/20. */public class FileInfo implements Serializable { //文件id private int id; //文件下载地址 private String url; //文件

Android-多线程断点下载详解及源码下载(三)

本项目完成的功能类似与迅雷等下载工具所实现的功能--实现多线程断点下载. 主要设计的技术有: 1.android中主线程与非主线程通信机制. 2.多线程的编程和管理. 3.android网络编程 4.自己设计实现设计模式-监听器模式 5.Activity.Service.数据库编程 6.android文件系统 7.缓存 博文链接: Android-多线程断点下载详解及源码下载(一) Android-多线程断点下载详解及源码下载(二) Android-多线程断点下载详解及源码下载(四) 本篇接着上

Android-多线程断点下载详解及源码下载(四)

本项目完成的功能类似与迅雷等下载工具所实现的功能--实现多线程断点下载. 主要设计的技术有: 1.android中主线程与非主线程通信机制. 2.多线程的编程和管理. 3.android网络编程 4.自己设计实现设计模式-监听器模式 5.Activity.Service.数据库编程 6.android文件系统 7.缓存 博文链接: Android-多线程断点下载详解及源码下载(一) Android-多线程断点下载详解及源码下载(二) Android-多线程断点下载详解及源码下载(三) 本篇完结多

多线程断点下载原理(java代码实例演示)

其实多线程断点下载原理,很简单的,那么我们就来先了解下,如何实现多线程的断点下载,首先:你必须明白第一点,那么就是,什么是多线程下载,该知识点可以查看本博客上一篇文章,Android之多线程下载原理,断点下载呢,其实就是在这个的基础之上添加了一些东西,那么添加了什么东西了,现在来做一个详细的了解. 1.在下载的过程中,边下载,变用一个文件来记录下载的位置,也就是下载了多少的数据 1.创建文件 2.记录下载多少数据 3.存储数据 2.第二次下载的时候,就去读取文件中是否存有数据,读取上次下载的位置

断点下载的请求头设置

断点下载的时候,需要设置请求头的“Range” 表示头100个字节:Range:bytes=0-99 表示第二个100个字节:Range:bytes=100-199 表示最后100个字节:Range:bytes=-100 表示200个字节以后的所有字节:Range:bytes=200-

Java之多线程断点下载的实现

RandomAccessFile类: 此类的实例支持对随机訪问文件的读取和写入.随机訪问文件的行为相似存储在文件系统中的一个大型 byte 数组. 存在指向该隐含数组.光标或索引,称为文件指针.输入操作从文件指针開始读取字节.并随着对字节的读取而前移此文件指针. 假设随机訪问文件以读取/写入模式创建,则输出操作也可用.输出操作从文件指针開始写入字节.并随着对字节的写入而前移此文件指针.写入隐含数组的当前末尾之后的输出操作导致该数组扩展.该文件指针能够通过 getFilePointer 方法读取.

Android的断点下载详细分析二

由于一篇blog写不完,这里是接着上一篇blog的. 写完了MVC中的View,写着我们需要考虑Control层了,他的任务是在后台利用多线程实现断点下载. 先看源码: public class FileDownloader { /* TAG,便于调试 */ private static final String TAG = "FileDownloader"; /* 上下文 */ private Context context; /* 用于对数据库的操作 */ private File