多线程断点下载思路
1 加入断点功能
要知道每条线程的下载记录
数据库 SqliteDatabase SqliteOpenHepler
download.db
table
_id threadid path downloadlength
//使用ContentProvider
设计业务方法
DownloadService
1 插入数据 insert()
2 更新数据 update()
3 查询
3.1 查询是否有下载记录boolean isExist()
3.2 查询每条记录的下载记录int queryByThreadid(int threadid)
3.3 查询总的下载记录 intqueryAll()
4 删除
delete()
1 在哪里修改代码?
在下载的时候,需要判断是否有下载记录 DownloadManager
更新下载记录在哪里呢?DownloadThread
2 暂停
MainActivity DownloadManager boolean flag = DownloadThread
对比之前学的多线程下载,我们今天学的断点多线程下载,加入了断点功能,之所以加入了断点功能,我们就要加入SQLite数据库进行存储每天线程的下载数据量,便于第一次没下载完,第二次读取数据,继续下载。因为使用到了SQLite数据库,所以我们自己也给自己提供了contentProvider,这样更好的简化了我们对数据库的操作以及不需要处理数据库的开关,没有频繁的操作数据库,这样使得我们的操作效率更高。
代码MainAtivity
sqlite
[java] view plain copy print ?
- public class DownloadDBHelper extends SQLiteOpenHelper {
- private final static String name = "download.db";
- private static SQLiteOpenHelper mOpenHelper;
-
-
- public synchronized static SQLiteOpenHelpergetInstance(Context context){
- if(mOpenHelper == null){
- mOpenHelper = new DownloadDBHelper(context, name, null, 1);
- }
- return mOpenHelper;
- }
-
- public DownloadDBHelper(Contextcontext, String name,
- CursorFactory factory, int version) {
- super(context, name, factory, version);
-
- }
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("create tabledownload(_id integer primary key autoincrement," +
- "threadid integer," +
- "path text," +
- "downloadlength integer)");
-
- }
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- }
- }
contentprovider
[java] view plain copy print ?
- public class DownloadProvider extends ContentProvider {
- 验证url
- private static UriMatchermatcher = new UriMatcher(UriMatcher.NO_MATCH);
- private static String authority ="com.cym.multidon.provider.DownloadProvider";
- private final static int DOWNLOAD = 10;
- static {
- matcher.addURI(authority,"download", DOWNLOAD);
- }
- private SQLiteOpenHelper mOpenHelper;
- @Override
- public boolean onCreate() {
- mOpenHelper = DownloadDBHelper.getInstance(getContext());
- return false;
- }
- @Override
- public Cursor query(Uri uri,String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
-
- SQLiteDatabase db = mOpenHelper.getReadableDatabase();
- Cursor ret = null;
- int code = matcher.match(uri);
- switch (code) {
- case DOWNLOAD:
- ret = db.query("download", projection, selection, selectionArgs,
- null, null, null);
- break;
- default:
- break;
- }
- return ret;
- }
- @Override
- public String getType(Uri uri) {
-
- return null;
- }
- @Override
- public Uri insert(Uri uri,ContentValues values) {
-
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- int code = matcher.match(uri);
- switch (code) {
- case DOWNLOAD:
- db.insert("download", "_id", values);
- break;
- default:
- break;
- }
- return null;
- }
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
-
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- int code = matcher.match(uri);
- switch (code) {
- case DOWNLOAD:
- db.delete("download", selection,selectionArgs);
- break;
- default:
- break;
- }
- return 0;
- }
- @Override
- public int update(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
-
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- int code = matcher.match(uri);
- switch (code) {
- case DOWNLOAD:
- db.update("download", values, selection,selectionArgs);
- break;
- default:
- break;
- }
- return 0;
- }
- }
DowloadService
[java] view plain copy print ?
- package com.cym.multidon.service;
- import com.cym.multidon.domain.DownloadInfo;
- import android.content.ContentResolver;
- import android.content.ContentValues;
- import android.content.Context;
- import android.database.Cursor;
- import android.net.Uri;
- public class DownloadService {
-
- privateContext context;
- privateContentResolver cr;
- Private Uri uri =Uri.parse("content://com.cym.multidon.provider.DownloadProvider/download");
-
- publicDownloadService(Context context){
- this.context = context;
- this.cr = context.getContentResolver();
- }
-
-
-
-
- publicvoid insert(DownloadInfo info){
- ContentValues values = newContentValues();
- values.put("threadid",info.getThreadid());
- values.put("path",info.getPath());
- values.put("downloadlength",0);
-
- cr.insert(uri, values);
- }
-
-
-
-
- publicvoid update(DownloadInfo info){
- ContentValues values = new ContentValues();
- values.put("downloadlength",info.getDownloadlength());
- String where = "threadid = ? and path =?";
- String[] selectionArgs = new String[]{info.getThreadid()+"",info.getPath()};
- cr.update(uri, values, where, selectionArgs);
- }
-
-
-
-
-
- publicboolean isExist(String path){
- boolean isExist = false;
- String selection = "path = ?";
- String[] selectionArgs = newString[]{path};
- Cursor cursor = cr.query(uri, newString[]{"*"}, selection, selectionArgs, null);
- if(cursor!=null){
- if(cursor.moveToFirst()){
- isExist = true;
- }
- cursor.close();
- }
- return isExist;
- }
-
-
-
-
-
- publicint queryByThreadid(DownloadInfo info){
- int downlaodlength = 0;
- String selection = "threadid = ? andpath = ?";
- String[] selectionArgs = newString[]{info.getThreadid()+"",info.getPath()};
- Cursor cursor = cr.query(uri, newString[]{"*"}, selection, selectionArgs, null);
- if(cursor != null){
- if(cursor.moveToFirst()){
- downlaodlength=cursor.getInt(cursor.getColumnIndex("downloadlength")) ;
- }
- cursor.close();
- }
- return downlaodlength;
- }
-
-
-
-
-
- publicint queryAll(String path){
- int downlaodlength = 0;
- String selection = "path = ?";
- String[] selectionArgs = newString[]{path};
- Cursor cursor = cr.query(uri, newString[]{"*"}, selection, selectionArgs, null);
- if(cursor != null){
- while(cursor.moveToNext()){
- downlaodlength +=cursor.getInt(cursor.getColumnIndex("downloadlength")) ;
- }
- cursor.close();
- }
- return downlaodlength;
- }
- publicvoid delete(String path){
- String where = "path = ?";
- String[] selectionArgs = newString[]{path};
- cr.delete(uri, where, selectionArgs);
- }
- }
DownloadManager
[java] view plain copy print ?
- package com.cym.multidon.download;
- import java.io.File;
- import java.io.RandomAccessFile;
- import java.net.HttpURLConnection;
- import java.net.URL;
- import android.content.Context;
- import android.util.Log;
- importcom.cym.multidon.domain.DownloadInfo;
- importcom.cym.multidon.inter.ProgressInter;
- importcom.cym.multidon.service.DownloadService;
- public class DownloadManager {
- privatefinal static int threadSzie = 3; 线程数量
- private boolean isDwondlaod; 是否开启下载
- privateDownloadService ds;
- publicboolean isDwondlaod() {
- return isDwondlaod;
- }
- public void setDwondlaod(boolean isDwondlaod) {
- this.isDwondlaod =isDwondlaod;
- }
-
- public DownloadManager(Context context) {
- super();
- this.ds = newDownloadService(context);
- }
- publicvoid download(String path, ProgressInter pi, File dir)throws Exception{
- URLurl = new URL(path);
- HttpURLConnectionconn = (HttpURLConnection) url.openConnection();
- conn.setRequestMethod("GET");
- conn.setConnectTimeout(5000);
- if(conn.getResponseCode()== 200){
- int contentSize =conn.getContentLength();
- File file = newFile(dir,getPathName(path));
- RandomAccessFile raf = newRandomAccessFile(file, "rwd");
- raf.setLength(contentSize);
- raf.close();
- pi.setMax(contentSize);
- 如果有下载的记录那么就读取数据修改成上次进度
- if(ds.isExist(path)){
- int length = ds.queryAll(path);
- pi.setDownloadLength(length);
- }else
- {
- 如果的没有下载记录则创建记录
- for (int threadid = 0; threadid <threadSzie; threadid++) {
- ds.insert(new DownloadInfo(threadid,path, 0));
- }
- }
- 计算出每个线程下载量
- int block =contentSize%threadSzie == 0 ? contentSize/threadSzie :contentSize/threadSzie+1;
- for (int threadid = 0; threadid <threadSzie; threadid++) {
- 启动三条线程开始下载
- new DownloadThread(threadid, path, dir, block, pi, this,ds).start();
- }
- }
- }
- 截取url后面的文件名
- publicString getPathName(String path){
- returnpath.substring(path.lastIndexOf("/")+1);
- }
- }
DownloadThread
[java] view plain copy print ?
- package com.cym.multidon.download;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.InputStream;
- import java.io.RandomAccessFile;
- import java.net.HttpURLConnection;
- import java.net.URL;
- import com.cym.multidon.domain.DownloadInfo;
- importcom.cym.multidon.inter.ProgressInter;
- importcom.cym.multidon.service.DownloadService;
- public class DownloadThread extends Thread{
- privateint threadid;
- privateString path;
- privateFile dir;
- privateint block;
- privateProgressInter pi;
- privateDownloadManager dm;
- privateDownloadService ds;
- privateint startPosition;
- privateint endPosition;
- publicDownloadThread(int threadid, String path, File dir, int block,
- ProgressInterpi, DownloadManager dm, DownloadService ds) {
- super();
- this.threadid = threadid;
- this.path = path;
- this.dir = dir;
- this.block = block;
- this.pi = pi;
- this.dm = dm;
- this.ds = ds;
- 算出开始位置和结束位置
- this.startPosition= threadid * block;
- this.endPosition =(threadid + 1) * block - 1;
- }
- @Override
- publicvoid run() {
-
- super.run();
- DownloadInfo info= new DownloadInfo(threadid, path, 0);
- 一开始查询该线程的下载量
- int size =ds.queryByThreadid(info);
- File file = new File(dir,dm.getPathName(path));
- 从上次记录的下载进度开始下载(默认为0)
- startPosition +=size;
- try {
- RandomAccessFileraf = new RandomAccessFile(file, "rwd");
- 设置文件读取位置
- raf.seek(startPosition);
- byte[]buffer = new byte[1024];
- intlen = 0;
- URLurl = new URL(path);
- HttpURLConnectionconn = (HttpURLConnection) url.openConnection();
- conn.setRequestMethod("GET");
- conn.setConnectTimeout(5000);
- 设置请求内容位置
- conn.setRequestProperty("Range", "bytes="+ startPosition + "-"
- + endPosition);
- InputStreamis = conn.getInputStream();
- while((len = is.read(buffer)) != -1) {
- 如果是暂停下载,boolean值则是false,则返回进入修改,及更改进度条
- if(!dm.isDwondlaod()) {
- return;
- }
- 如果是下载则修改,及更新进度条进度
- raf.write(buffer, 0, len);
- 不断的修改,没一次下载就修改一次下载量数据及进度条进度
- size += len;
- info.setDownloadlength(size);
- ds.update(info);
- pi.setLength(len);
- }
- is.close();
- raf.close();
- } catch (Exception e) {
-
- e.printStackTrace();
- }
- }
- }
课后问题
多线程下载是怎么实现的?
开启多个线程下载一个文件
在这里我们使用哪些知识点?
Thread、Handler、Http协议、ProgressBar、ContentProvider、SQLite。
断点功能是怎么实现的。
把数据存储在SQLite里面。