本节引言:
本节给大家带来的Android中的多线程断点续传的代码解析,呵呵,为什么叫解析呢?因为我 也写不出来,( ╯□╰ )!先来说说断点的意思吧!所谓的断点就是:使用数据库记录每天线程所 下载的进度!每次启动时根据线程id查询某线程的下载进度,在继续下载!听上去蛮简单的, 要你写十有八九写不出,这很正常,所以本节看懂最好,看不懂也没什么,会用和改就好! 好的,开始本节内容~
Android多线程断点下载的代码流程解析:
运行效果图:

实现流程全解析:
Step 1:创建一个用来记录线程下载信息的表
创建数据库表,于是乎我们创建一个数据库的管理器类,继承SQLiteOpenHelper类
重写onCreate()与onUpgrade()方法,我们创建的表字段如下:

DBOpenHelper.java:
package com.jay.example.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
public class DBOpenHelper extends SQLiteOpenHelper {
public DBOpenHelper(Context context) {
super(context, "downs.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//数据库的结构为:表名:filedownlog 字段:id,downpath:当前下载的资源,
//threadid:下载的线程id,downlength:线程下载的最后位置
db.execSQL("CREATE TABLE IF NOT EXISTS filedownlog " +
"(id integer primary key autoincrement," +
" downpath varchar(100)," +
" threadid INTEGER, downlength INTEGER)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//当版本号发生改变时调用该方法,这里删除数据表,在实际业务中一般是要进行数据备份的
db.execSQL("DROP TABLE IF EXISTS filedownlog");
onCreate(db);
}
}
Step 2:创建一个数据库操作类
我们需要创建什么样的方法呢?
- ①我们需要一个根据URL获得每条线程当前下载长度的方法
- ②接着,当我们的线程新开辟后,我们需要往数据库中插入与该线程相关参数的方法
- ③还要定义一个可以实时更新下载文件长度的方法
- ④我们线程下载完,还需要根据线程id,删除对应记录的方法
FileService.java
package com.jay.example.db;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
/*
* 该类是一个业务bean类,完成数据库的相关操作
* */
public class FileService {
//声明数据库管理器
private DBOpenHelper openHelper;
//在构造方法中根据上下文对象实例化数据库管理器
public FileService(Context context) {
openHelper = new DBOpenHelper(context);
}
/**
* 获得指定URI的每条线程已经下载的文件长度
* @param path
* @return
* */
public Map<Integer, Integer> getData(String path)
{
//获得可读数据库句柄,通常内部实现返回的其实都是可写的数据库句柄
SQLiteDatabase db = openHelper.getReadableDatabase();
//根据下载的路径查询所有现场的下载数据,返回的Cursor指向第一条记录之前
Cursor cursor = db.rawQuery("select threadid, downlength from filedownlog where downpath=?",
new String[]{path});
//建立一个哈希表用于存放每条线程已下载的文件长度
Map<Integer,Integer> data = new HashMap<Integer, Integer>();
//从第一条记录开始遍历Cursor对象
cursor.moveToFirst();
while(cursor.moveToNext())
{
//把线程id与该线程已下载的长度存放到data哈希表中
data.put(cursor.getInt(0), cursor.getInt(1));
data.put(cursor.getInt(cursor.getColumnIndexOrThrow("threadid")),
cursor.getInt(cursor.getColumnIndexOrThrow("downlength")));
}
cursor.close();//关闭cursor,释放资源;
db.close();
return data;
}
/**
* 保存每条线程已经下载的文件长度
* @param path 下载的路径
* @param map 现在的di和已经下载的长度的集合
*/
public void save(String path,Map<Integer,Integer> map)
{
SQLiteDatabase db = openHelper.getWritableDatabase();
//开启事务,因为此处需要插入多条数据
db.beginTransaction();
try{
//使用增强for循环遍历数据集合
for(Map.Entry<Integer, Integer> entry : map.entrySet())
{
//插入特定下载路径特定线程ID已经下载的数据
db.execSQL("insert into filedownlog(downpath, threadid, downlength) values(?,?,?)",
new Object[]{path, entry.getKey(), entry.getValue()});
}
//设置一个事务成功的标志,如果成功就提交事务,如果没调用该方法的话那么事务回滚
//就是上面的数据库操作撤销
db.setTransactionSuccessful();
}finally{
//结束一个事务
db.endTransaction();
}
db.close();
}
/**
* 实时更新每条线程已经下载的文件长度
* @param path
* @param map
*/
public void update(String path,int threadId,int pos)
{
SQLiteDatabase db = openHelper.getWritableDatabase();
//更新特定下载路径下特定线程已下载的文件长度
db.execSQL("update filedownlog set downlength=? where downpath=? and threadid=?",
new Object[]{pos, path, threadId});
db.close();
}
/**
*当文件下载完成后,删除对应的下载记录
*@param path
*/
public void delete(String path)
{
SQLiteDatabase db = openHelper.getWritableDatabase();
db.execSQL("delete from filedownlog where downpath=?", new Object[]{path});
db.close();
}
}
Step 3:创建一个文件下载器类
好了,数据库管理器与操作类都完成了接着就该弄一个文件下载器类了,在该类中又要完成 什么操作呢?要做的事就多了:
①定义一堆变量,核心是线程池threads和同步集合ConcurrentHashMap,用于缓存线程下载长度的
②定义一个获取线程池中线程数的方法;
③定义一个退出下载的方法,
④获取当前文件大小的方法
⑤累计当前已下载长度的方法,这里需要添加一个synchronized关键字,用来解决并发访问的问题
⑥更新指定线程最后的下载位置,同样也需要用同步
⑦在构造方法中完成文件下载,线程开辟等操作
⑧获取文件名的方法:先截取提供的url最后的'/'后面的字符串,如果获取不到,再从头字段查找,还是 找不到的话,就使用网卡标识数字+cpu的唯一数字生成一个16个字节的二进制作为文件名
⑨开始下载文件的方法
⑩获取http响应头字段的方法
?打印http头字段的方法
12.打印日志信息的方法
FileDownloadered.java:
package com.jay.example.service;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.content.Context;
import android.util.Log;
import com.jay.example.db.FileService;
public class FileDownloadered {
private static final String TAG = "文件下载类"; //设置一个查log时的一个标志
private static final int RESPONSEOK = 200; //设置响应码为200,代表访问成功
private FileService fileService; //获取本地数据库的业务Bean
private boolean exited; //停止下载的标志
private Context context; //程序的上下文对象
private int downloadedSize = 0; //已下载的文件长度
private int fileSize = 0; //开始的文件长度
private DownloadThread[] threads; //根据线程数设置下载的线程池
private File saveFile; //数据保存到本地的文件中
private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>(); //缓存个条线程的下载的长度
private int block;

