万事须己运,他得非我贤。这篇文章主要讲述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>
【Android网络编程之——文件断点下载(暂停/继续/重新下载)】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);
}
}
九: 断点下载的知识点在文章开头已经给出, 剩下的都是加入了自己的逻辑处理和代码封装。感兴趣的伙伴们一定要自己动手尝试着去写, 不然你以为你看了一遍会了其实到后来就完全不会了……Demo下载传送门
推荐阅读
- android 集成QQ互联 (登录,分享)
- android 垃圾回收机制
- 关于Android软键盘把布局顶上去的问题
- android4.3 Bluetooth(le)分析之startLeScan分析
- android4.3 Bluetooth分析之扫描分析
- android:process=":remote"属性解说
- Android中的多线程开源框架
- Android开发(《Gradle Recipes for Android》阅读笔记(翻译)4.2——增加自定义task)
- Android Gradle插件(plugin)版本(version)与GradleSDK Build Tools版本关系