Android网络编程之——文件断点下载
一:关于断点下载所涉及到的知识点
1.对SQLite的增删改查(主要用来保存当前任务的一些信息)
2.HttpURLConnection的请求配置
HttpURLConnection connection = null;
//设置下载请求属性
connection.setRequestProperty();
3.RandomAccessFile 对文件进行写入
RandomAccessFile rwd = null;
//从文件的某一位置写入
rwd.seek();
4.基本的I/O流操作,以及逻辑处理
二:第一步我们先来创建一张表用来保存我们的下载信息
public class DbHelper extends SQLiteOpenHelper {
public static String TABLE = "file";//表名
public DbHelper(Context context) {
super(context, "download.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//文件名,下载地址,下载文件的总长度,当前下载完成长度
db.execSQL("create table file(fileName varchar,url varchar,length integer,finished integer)");
}
}
三:第二步同时既然是对数据库的操作,那我们在DbHelper.class
中来写好几个公用方法
/**
* 插入一条下载信息
*/
public void insertData(SQLiteDatabase db, FileInfo info) {
ContentValues values = new ContentValues();
values.put("fileName", info.getFileName());
values.put("url", info.getUrl());
values.put("length", info.getLength());
values.put("finished", info.getFinished());
db.insert(TABLE, null, values);
}
/**
* 是否已经插入这条数据
*/
public boolean isExist(SQLiteDatabase db, FileInfo info) {
Cursor cursor = db.query(TABLE, null, "url = ?", new String[]{info.getUrl()}, null, null, null, null);
boolean exist = cursor.moveToNext();
cursor.close();
return exist;
}
/**
* 查询已经存在的一条信息
*/
public FileInfo queryData(SQLiteDatabase db, String url) {
Cursor cursor = db.query(TABLE, null, "url = ?", new String[]{url}, null, null, null, null);
FileInfo info = new FileInfo();
if (cursor != null) {
while (cursor.moveToNext()) {
String fileName = cursor.getString(cursor.getColumnIndex("fileName"));
int length = cursor.getInt(cursor.getColumnIndex("length"));
int finished = cursor.getInt(cursor.getColumnIndex("finished"));
info.setStop(false);
info.setFileName(fileName);
info.setUrl(url);
info.setLength(length);
info.setFinished(finished);
}
cursor.close();
}
return info;
}
/**
* 恢复一条下载信息
*/
public void resetData(SQLiteDatabase db, String url) {
ContentValues values = new ContentValues();
values.put("finished", 0);
values.put("length", 0);
db.update(TABLE, values, "url = ?", new String[]{url});
}
3.从上面方法中可以看出来还有一个FileInfo对象,没错这是自己创建的一个下载任务实体类一起来看看吧
//保存下载任务信息
public class FileInfo {
private String fileName;//文件名
private String url;//下载地址
private int length;//文件大小
private int finished;//下载以已完成进度
private boolean isStop = false;//是否暂停下载
private boolean isDownLoading = false;//是否正在下载
//......
//剩下的都是对应的get and set 方法就不贴出来了,自动生成就好了
四:第三步我们创建一个类DownLoaderManger
来管理我们的下载任务包括、添加下载任务、开始下载、暂停下载、重新下载
public class DownLoaderManger {
public static String FILE_PATH = Environment.getExternalStorageDirectory() + "/azhong";//文件下载保存路径
private DbHelper helper;//数据库帮助类
private SQLiteDatabase db;
private OnProgressListener listener;//进度回调监听
private Map<String, FileInfo> map = new HashMap<>();//保存正在下载的任务信息
private static DownLoaderManger manger;
private DownLoaderManger(DbHelper helper, OnProgressListener listener) {
this.helper = helper;
this.listener = listener;
db = helper.getReadableDatabase();
}
/**
* 单例模式
*
* @param helper 数据库帮助类
* @param listener 下载进度回调接口
* @return
*/
public static synchronized DownLoaderManger getInstance(DbHelper helper, OnProgressListener listener) {
if (manger == null) {
synchronized (DownLoaderManger.class) {
if (manger == null) {
manger = new DownLoaderManger(helper, listener);
}
}
}
return manger;
}
/**
* 开始下载任务
*/
public void start(String url) {
db = helper.getReadableDatabase();
FileInfo info = helper.queryData(db, url);
map.put(url, info);
//开始任务下载
new DownLoadTask(map.get(url), helper, listener).start();
}
/**
* 停止下载任务
*/
public void stop(String url) {
map.get(url).setStop(true);
}
/**
* 重新下载任务
*/
public void restart(String url) {
stop(url);
try {
File file = new File(FILE_PATH, map.get(url).getFileName());
if (file.exists()) {
file.delete();
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
db = helper.getWritableDatabase();
helper.resetData(db, url);
start(url);
}
/**
* 获取当前任务状态
*/
public boolean getCurrentState(String url) {
return map.get(url).isDownLoading();
}
/**
* 添加下载任务
*
* @param info 下载文件信息
*/
public void addTask(FileInfo info) {
//判断数据库是否已经存在这条下载信息
if (!helper.isExist(db, info)) {
db = helper.getWritableDatabase();
helper.insertData(db, info);
map.put(info.getUrl(), info);
} else {
//从数据库获取最新的下载信息
db = helper.getReadableDatabase();
FileInfo o = helper.queryData(db, info.getUrl());
if (!map.containsKey(info.getUrl())) {
map.put(info.getUrl(), o);
}
}
}
}
五:上面代码中OnProgressListener
接口,当然还有一个最最重要的DownLoadTask
了这里面就是实现了如何断点下载的,下面来一起看下里面的实现逻辑吧。。。
//下载进度接口
public interface OnProgressListener {
void updateProgress(int max, int progress);
}
六:重点–下载线程
/**
* 下载文件线程
* 从服务器获取需要下载的文件大小
*/
public class DownLoadTask extends Thread {
private FileInfo info;
private SQLiteDatabase db;
private DbHelper helper;//数据库帮助类
private int finished = 0;//当前已下载完成的进度
private OnProgressListener listener;//进度回调监听
public DownLoadTask(FileInfo info, DbHelper helper, OnProgressListener listener) {
this.info = info;
this.helper = helper;
this.db = helper.getReadableDatabase();
this.listener = listener;
info.setDownLoading(true);
}
@Override
public void run() {
getLength();
HttpURLConnection connection = null;
RandomAccessFile rwd = null;
try {
URL url = new URL(info.getUrl());
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(3000);
//从上次下载完成的地方下载
int start = info.getFinished();
//设置下载位置(从服务器上取要下载文件的某一段)
connection.setRequestProperty("Range", "bytes=" + start + "-" + info.getLength());//设置下载范围
//设置文件写入位置
File file = new File(DownLoaderManger.FILE_PATH, info.getFileName());
rwd = new RandomAccessFile(file, "rwd");
//从文件的某一位置开始写入
rwd.seek(start);
finished += info.getFinished();
if (connection.getResponseCode() == 206) {//文件部分下载,返回码为206
InputStream is = connection.getInputStream();
byte[] buffer = new byte[1024 * 4];
int len;
while ((len = is.read(buffer)) != -1) {
//写入文件
rwd.write(buffer, 0, len);
finished += len;
info.setFinished(finished);
//更新界面显示
Message msg = new Message();
msg.what = 0x123;
msg.arg1 = info.getLength();
msg.arg2 = info.getFinished();
handler.sendMessage(msg);
//停止下载
if (info.isStop()) {
info.setDownLoading(false);
//保存此次下载的进度
helper.updateData(db, info);
db.close();
return;
}
}
//下载完成
info.setDownLoading(false);
helper.updateData(db, info);
db.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
try {
if (rwd != null) {
rwd.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 首先开启一个线程去获取要下载文件的大小(长度)
*/
private void getLength() {
HttpURLConnection connection = null;
try {
//连接网络
URL url = new URL(info.getUrl());
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(3000);
int length = -1;
if (connection.getResponseCode() == 200) {//网络连接成功
//获得文件长度
length = connection.getContentLength();
}
if (length <= 0) {
//连接失败
return;
}
//创建文件保存路径
File dir = new File(DownLoaderManger.FILE_PATH);
if (!dir.exists()) {
dir.mkdirs();
}
info.setLength(length);
} catch (Exception e) {
e.printStackTrace();
} finally {
//释放资源
try {
if (connection != null) {
connection.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 更新进度
*/
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0x123:
if (listener != null) {
listener.updateProgress(msg.arg1, msg.arg2);
}
break;
}
}
};
}
七:下载流程—>首先获取要下载文件的总长度—>然后指定从上次结束的位置开始下载文件。客官阅读需仔细哦,精华都在注释里面哦!个人认为重点部分如下两个:
//设置下载位置(从服务器上取要下载文件的某一段)
connection.setRequestProperty("Range", "bytes=" + start + "-" + info.getLength());//设置下载范围
RandomAccessFile rwd = new RandomAccessFile(file, "rwd");
//从文件的某一位置开始写入
rwd.seek(start);
八:上面做了一系列准备工作之后,就可以正式开始下载了让我们一起来瞧瞧
1.给主布局界面放两个按钮,来开始/暂停/重新下载
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.azhong.downloader.MainActivity">
<com.azhong.downloader.view.NumberProgressBar
android:id="@+id/pb"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="开始下载" />
<Button
android:id="@+id/restart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="重新下载" />
</LinearLayout>
2.关于NumberProgressBar的使用可以移驾至这里
3.记得在清单文件中加入 网络访问和内存读写权限哦!
4.既然我们封装了那么久,那肯定用起来就会很简单了
public class MainActivity extends AppCompatActivity implements OnProgressListener {
private NumberProgressBar pb;//进度条
private DownLoaderManger downLoader = null;
private FileInfo info;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pb = (NumberProgressBar) findViewById(R.id.pb);
final Button start = (Button) findViewById(R.id.start);//开始下载
final Button restart = (Button) findViewById(R.id.restart);//重新下载
final DbHelper helper = new DbHelper(this);
downLoader = DownLoaderManger.getInstance(helper, this);
info = new FileInfo("Kuaiya482.apk", "http://downloadz.dewmobile.net/Official/Kuaiya482.apk");
downLoader.addTask(info);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (downLoader.getCurrentState(info.getUrl())) {
downLoader.stop(info.getUrl());
start.setText("开始下载");
} else {
downLoader.start(info.getUrl());
start.setText("暂停下载");
}
}
});
restart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
downLoader.restart(info.getUrl());
start.setText("暂停下载");
}
});
}
@Override
public void updateProgress(final int max, final int progress) {
pb.setMax(max);
pb.setProgress(progress);
}
}
Android网络编程之——文件断点下载的更多相关文章
- 转 Android网络编程之使用HttpClient批量上传文件 MultipartEntityBuilder
请尊重他人的劳动成果,转载请注明出处:Android网络编程之使用HttpClient批量上传文件 http://www.tuicool.com/articles/Y7reYb 我曾在<Andr ...
- Android网络编程只局域网传输文件
Android网络编程之局域网传输文件: 首先创建一个socket管理类,该类是传输文件的核心类,主要用来发送文件和接收文件 具体代码如下: package com.jiao.filesend; im ...
- ios开发网络学习四:NSURLConnection大文件断点下载
#import "ViewController.h" @interface ViewController ()<NSURLConnectionDataDelegate> ...
- Android网络编程概述
Android网络编程概述 首先,应该了解的几个问题: 1)Android平台网络相关API接口 a) java.net.*(标准Java接口) java.net.*提供与联网有关的类,包括流.数据包 ...
- Android网络编程http派/申请服务
最近的研究Android网络编程知识,这里有一些想法,今晚学习.与您分享. 在实际的应用程序的开发非常需要时间appserver请求数据,那么app怎样发送请求呢?以下的代码就是当中的一种情况.使用H ...
- Android网络编程要学的东西与Http协议学习
本节引言: 本节开始我们来学习Android网络编程相关的一些东西:Android端网络编程是要干嘛?http协议的学习,使用自带扣脚Json解析类解析Json,XML解析常用的几种方式,HttpUr ...
- Android 网络编程 记录
简单介绍 看了深入理解Android网络编程感觉不错.今天对Android网络编程进行了要点记录. 内容 Android基于网络技术和编程实践 要点 定义 描写叙述 IP协议 用于报文交换网络的一种面 ...
- Android网络编程(三)Volley使用方法全解析
相关文章 Android网络编程(一)HTTP协议原理 Android网络编程(二)HttpClient与HttpURLConnection 前言 Volley想必许多人都用过,为了建立网络编程的知识 ...
- Android网络编程(十)Retrofit2后篇[注解]
G相关文章 Android网络编程(一)HTTP协议原理 Android网络编程(二)HttpClient与HttpURLConnection Android网络编程(三)Volley用法全解析 An ...
随机推荐
- shell脚本——sed命令
sed 命令 作为行编辑器,对文件进行编辑(以行为单位进行编辑) sed编辑文件,却不改变原文件 sed工作原理: 指定一个文本文件,依次读取文本文件中的每行内容,读取到模式空间中,在模式空间中进行匹 ...
- 制作docker 离线仓库
目录 制作docker 离线仓库 1.前言 2.步骤 安装docker-distribution 编辑docker-distribution服务的配置,使用yaml进行的配置 启动仓库服务 编辑doc ...
- redis系列二: linux下安装redis
下面介绍在Linux环境下,Redis的安装与配置 一. 安装 1.首先上官网下载Redis 压缩包,地址:http://redis.io/download 下载稳定版3.0即可. 2.通过远程管理工 ...
- 一周死磕fastreport ----ASP.NET (一)
https://blog.csdn.net/wuyuander/article/details/52692435 原文链接,点击跳转 首先是安装好FastReport .net: 然后在vs2012中 ...
- 通过shell发送邮件
安装mailx CentOS 7自带有mailx软件包, 有/usr/bin/mail命令, 配置文件为/etc/mail.rc. 如果没有软件包, 可以安装 CentOS/Fedora yum in ...
- Python3学习笔记37-LeetCode刷题
LeetCode中国官网一个用来刷编程题的网站,收录了很多面试题.感觉还是学习到很多.记录一下思路.代码还是要多敲. 建议编写完后直接在LeetCode上运行和提交.提交时会有不同的测试用例来测试代码 ...
- jQuery 查找父节点 parents()与closest()
parents()由内向外,直到最高的父节点停止查找,返回的父节点是多个 closest()由内向外查找,当找到符合规则的一个,则不再查找,返回的是0或1个
- 题解 [51nod1385] 凑数字
题面 解析 首先设\(n\)有\(l\)位, 那么对于前\(l-1\)位,\(0\)~\(9\)都是要选上的, 而对于最高位上的数\(x\),\(1\)~\(x-1\)也是要选上的. 到这里就有了\( ...
- nextUntil([exp|ele][,fil]) 查找当前元素之后所有的同辈元素,直到遇到匹配的那个元素为止。
nextUntil([exp|ele][,fil]) 概述 查找当前元素之后所有的同辈元素,直到遇到匹配的那个元素为止. 如果提供的jQuery代表了一组DOM元素,.nextUntil()方法也能让 ...
- mobx是什么?有什么优点?
mobx是一个简单可扩展的状态管理库. mobx vs redux mobx是学习成本更低,性能更好的状态解决方案. mobx开发难度低: mobx代码量少: mobx渲染性能好: mobx参考