本节引言:
本节给大家带来的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;