aaynctask控制多个下载进度_Android多线程+单线程+断点续传+进度条显示下载功能...

效果图白话分析:多线程:肯定是多个线程咯断点:线程停止下载的位置续传:线程从停止下载的位置上继续下载,直到完成任务为止。核心分析:断点:当前线程已经下载的数据长度续传:向服务器请求上次线程停止下载位置的数据con.setRequestProperty("Range", "bytes=" + start + "-" + end);分配线程:int currentPartSize = fileSize...
摘要由CSDN通过智能技术生成

效果图

白话分析:

多线程:肯定是多个线程咯

断点:线程停止下载的位置

续传:线程从停止下载的位置上继续下载,直到完成任务为止。

核心分析:

断点:

当前线程已经下载的数据长度

续传:

向服务器请求上次线程停止下载位置的数据

con.setRequestProperty("Range", "bytes=" + start + "-" + end);

分配线程:

int currentPartSize = fileSize / mThreadNum;

定义位置

定义线程开始下载的位置和结束的位置

for (int i = 0; i < mThreadNum; i++) {

int start = i * currentPartSize;//计算每条线程下载的开始位置

int end = start + currentPartSize-1;//线程结束的位置

if(i==mThreadNum-1){

end=fileSize;

}}

创建数据库:

由于每一个文件要分成多个部分,要被不同的线程同时进行下载。当然要创建线程表,保存当前线程下载开始的位置和结束的位置,还有完成进度等。创建file表,保存当前下载的文件信息,比如:文件名,url,下载进度等信息

线程表:

public static final String CREATE_TABLE_SQL="create table "+TABLE_NAME+"(_id integer primary "

+"key autoincrement, threadId, start , end, completed, url)";

file表:

public static final String CREATE_TABLE_SQL="create table "+TABLE_NAME+"(_id integer primary" +

" key autoincrement ,fileName, url, length, finished)";

创建线程类

无非就2个类,一个是线程管理类DownLoadManager.Java,核心方法:start(),stop(),restart(),addTask().clear()。另一个是线程任务类

DownLoadTask.java,就是一个线程类,用于下载线程分配好的任务。后面会贴出具体代码。

创建数据库方法类

无非就是单例模式,封装一些增删改查等基础数据库方法,后面会贴出具体代码。

创建实体类

也就是创建ThreadInfo和FileInfo这2个实体类,把下载文件信息和线程信息暂时存储起来。

引入的第三方开源库

NumberProgressBar是一个关于进度条的开源库,挺不错的。直达链接

代码具体分析

1.首先是创建实体类,文件的实体类FileInfo,肯定有fileName,url,length,finised,isStop,isDownloading这些属性。线程的实体类ThreadInfo肯定有threadId,start,end,completed,url这些属性。这些都很简单

//ThredInfo.java

public class FileInfo {

private String fileName; //文件名

private String url; //下载地址

private int length; //文件大小

private int finished; //下载已完成进度

private boolean isStop=false; //是否暂停下载

private boolean isDownloading=false; //是否正在下载

public FileInfo(){

}

public FileInfo(String fileName,String url){

this.fileName=fileName;

this.url=url;

}

public String getFileName() {

return fileName;

}

public void setFileName(String fileName) {

this.fileName = fileName;

}

public String getUrl() {

return url;

}

public void setUrl(String url) {

this.url = url;

}

public int getLength() {

return length;

}

public void setLength(int length) {

this.length = length;

}

public int getFinished() {

return finished;

}

public void setFinished(int finished) {

this.finished = finished;

}

public boolean isStop() {

return isStop;

}

public void setStop(boolean stop) {

isStop = stop;

}

public boolean isDownloading() {

return isDownloading;

}

public void setDownloading(boolean downloading) {

isDownloading = downloading;

}

@Override

public String toString() {

return "FileInfo{" +

"fileName='" + fileName + '\'' +

", url='" + url + '\'' +

", length=" + length +

", finished=" + finished +

", isStop=" + isStop +

", isDownloading=" + isDownloading +

'}';

}}

//FileInfo.java

public class FileInfo {

private String fileName; //文件名

private String url; //下载地址

private int length; //文件大小

private int finished; //下载已完成进度

private boolean isStop=false; //是否暂停下载

private boolean isDownloading=false; //是否正在下载

public FileInfo(){

}

public FileInfo(String fileName,String url){

this.fileName=fileName;

this.url=url;

}

public String getFileName() {

return fileName;

}

public void setFileName(String fileName) {

this.fileName = fileName;

}

public String getUrl() {

return url;

}

public void setUrl(String url) {

this.url = url;

}

public int getLength() {

return length;

}

public void setLength(int length) {

this.length = length;

}

public int getFinished() {

return finished;

}

public void setFinished(int finished) {

this.finished = finished;

}

public boolean isStop() {

return isStop;

}

public void setStop(boolean stop) {

isStop = stop;

}

public boolean isDownloading() {

return isDownloading;

}

public void setDownloading(boolean downloading) {

isDownloading = downloading;

}

@Override

public String toString() {

return "FileInfo{" +

"fileName='" + fileName + '\'' +

", url='" + url + '\'' +

", length=" + length +

", finished=" + finished +

", isStop=" + isStop +

", isDownloading=" + isDownloading +

'}';

}}

2.实体类写完了,那么接下来写创建一个类,继承SQLiteOpenHelper类,来管理数据库连接,主要作用:管理数据库的初始化,并允许应用程序通过该类获取SQLiteDatabase对象。

public class ThreadHelper extends SQLiteOpenHelper{

public static final String TABLE_NAME="downthread";

public static final String CREATE_TABLE_SQL="create table "+TABLE_NAME+"(_id integer primary "

+"key autoincrement, threadId, start , end, completed, url)";

public ThreadHelper(Context context, String name, int version) {

super(context, name, null, version);

}

@Override

public void onCreate(SQLiteDatabase db) {

db.execSQL(CREATE_TABLE_SQL);

}

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

}}

3.接下来封装一些数据库的增删改查操作,用的单例模式,用双重检验锁实现单例。好处:既能很大程度上确保线程安全,又能实现延迟加载。 缺点:使用volatile关键字会使JVM对该代码的优化丧失,影响性能。并且在一些高并发的情况,仍然可能会创建多个实例,这称为双重检验锁定失效。单例模式

public class Thread {

private SQLiteDatabase db;

public static final String DB_NAME="downthread.db3";

public static final int VERSION=1;

private Context mContext;

private volatile static Thread t=null;

private Thread(){

mContext= BaseApplication.getContext();

db=new ThreadHelper(mContext,DB_NAME,VERSION).getReadableDatabase();

}

public static Thread getInstance(){

if(t==null){

synchronized (Thread.class){

if(t==null){

t=new Thread();

}

}

}

return t;

}

public SQLiteDatabase getDb(){

return db;

}

//保存当前线程下载进度

public synchronized void insert(ThreadInfo threadInfo){

ContentValues values=new ContentValues();

values.put("threadId",threadInfo.getThreadId());

values.put("start",threadInfo.getStart());

values.put("end",threadInfo.getEnd());

values.put("completed",threadInfo.getCompeleted());

values.put("url",threadInfo.getUrl());

long rowId=db.insert(ThreadHelper.TABLE_NAME,null,values);

if(rowId!=-1){

UtilsLog.i("插入线程记录成功");

}else{

UtilsLog.i("插入线程记录失败");

}

}

//查询当前线程 下载的进度

public synchronized ThreadInfo query(String threadId,String queryUrl){

Cursor cursor=db.query(ThreadHelper.TABLE_NAME,null,"threadId= ? and url= ?",new String[]{threadId,queryUrl},null,null,null);

ThreadInfo info=new ThreadInfo();

if(cursor!=null){

while (cursor.moveToNext()){

int start=cursor.getInt(2);

int end=cursor.getInt(3);

int completed=cursor.getInt(4);

String url=cursor.getString(5);

info.setThreadId(threadId);

info.setStart(start);

info.setEnd(end);

info.setCompeleted(completed);

info.setUrl(url);

}

cursor.close();

}

return info;

}

//更新当前线程下载进度

public synchronized void update(ThreadInfo info){

ContentValues values=new ContentValues();

values.put("start",info.getStart());

values.put("completed",info.getCompeleted());

db.update(ThreadHelper.TABLE_NAME,values,"threadId= ? and url= ?",new String[]{info.getThreadId(),info.getUrl()});

}

//关闭db

public void close(){

db.close();

}

//判断多线程任务下载 是否第一次创建线程

public boolean isExist(String url){

Cursor cursor=db.query(ThreadHelper.TABLE_NAME,null,"url= ?",new String[]{url},null,null,null);

boolean isExist=cursor.moveToNext();

cursor.close();

return isExist;

}

public synchronized void delete(ThreadInfo info){

long rowId=db.delete(ThreadHelper.TABLE_NAME,"url =? and threadId= ?",new String[]{info.getUrl(),info.getThreadId()});

if(rowId!=-1){

UtilsLog.i("删除下载线程记录成功");

}else{

UtilsLog.i("删除下载线程记录失败");

}

}

public synchronized void delete(String url){

long rowId=db.delete(ThreadHelper.TABLE_NAME,"url =? ",new String[]{url});

if(rowId!=-1){

UtilsLog.i("删除下载线程记录成功");

}else{

UtilsLog.i("删除下载线程记录失败");

}

}}

4.基本的准备操作我们已经完成了,那么开始写关于下载的类吧。首先写的肯定是DownLoadManager类,就是管理任务下载的类。不多说,直接看代码。

public class DownLoadManager {

private Map map = new HashMap<>();

private static int mThreadNum;

private int fileSize;

private boolean flag = false; //true第一次下载 false不是第一次下载

private List threads;

private static FileInfo mInfo;

private static ResultListener mlistener;

public static ExecutorService executorService = Executors.newCachedThreadPool();

public static File file;

private int totalComleted;

private DownLoadManager() {

threads = new ArrayList<>();

}

public static DownLoadManager getInstance(FileInfo info, int threadNum,ResultListener listener) {

mlistener = listener;

mThreadNum = threadNum;

mInfo = info;

return DownLoadManagerHolder.dlm;

}

private static class DownLoadManagerHolder {

private static final DownLoadManager dlm = new DownLoadManager();

}

public void start() {

totalComleted=0;

clear();

final FileInfo newInfo = DownLoad.getInstance().queryData(mInfo.getUrl());

newInfo.setDownloading(true);

map.put(mInfo.getUrl(),newInfo);

prepare(newInfo);

}

//停止下载任务

public void stop() {

map.get(mInfo.getUrl()).setDownloading(false);

map.get(mInfo.getUrl()).setStop(true);

}

public void clear(){

if(threads.size()>0){

threads.clear();

}

}

//重新下载任务

public void restart() {

stop();

try {

File file = new File(com.cmazxiaoma.downloader.download.DownLoadManager.FILE_PATH, map.get(mInfo.getUrl()).getFileName());

if (file.exists()) {

file.delete();

}

java.lang.Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

DownLoad.getInstance().resetData(mInfo.getUrl());

start();

}

//获取当前任务状态, 是否在下载

public boolean getCurrentState() {

return map.get(mInfo.getUrl()).isDownloading();

}

//添加下载任务

public void addTask(FileInfo info) {

//判断数据库是否已经存在此下载信息

if (!DownLoad.getInstance().isExist(info)) {

DownLoad.getInstance().insertData(info);

map.put(info.getUrl(), info);

} else {

DownLoad.getInstance().delete(info);

DownLoad.getInstance().insertData(info);

UtilsLog.i("map已经更新");

map.remove(info.getUrl());

map.put(info.getUrl(), info);

}

}

private void prepare(final FileInfo newInfo) {

new java.lang.Thread(){

@Override

public void run() {

HttpURLConnection con = null;

RandomAccessFile raf=null;

try {

//连接资源

URL url = new URL(newInfo.getUrl());

UtilsLog.i("url=" + url);

con = (HttpURLConnection) url.openConnection();

con.setConnectTimeout(2 * 1000);

con.setRequestMethod("GET");

int length = -1;

UtilsLog.i("responseCode=" + con.getResponseCode());

if (con.getResponseCode() == 200) {

length = con.getContentLength();

UtilsLog.i("文件大小=" + length);

}

if (length <= 0) {

return;

}

//创建文件保存路径

File dir = new File(com.cmazxiaoma.downloader.download.DownLoadManager.FILE_PATH);

if (!dir.exists()) {

dir.mkdirs();//建立多级文件夹

}

newInfo.setLength(length);

fileSize = length;

UtilsLog.i("当前线程Id=" + java.lang.Thread.currentThread().getId() + ",name=" + java.lang.Thread.currentThread().getName());

int currentPartSize = fileSize / mThreadNum;

file = new File(com.cmazxiaoma.downloader.download.DownLoadManager.FILE_PATH, newInfo.getFileName());

raf = new RandomAccessFile(file, "rwd");

raf.setLength(fileSize);

if (Thread.getInstance().isExist(newInfo.getUrl())) {

flag = false;

} else {

flag = true;

}

for (int i = 0; i < mThreadNum; i++) {

if (flag) {

UtilsLog.i("第一次多线程下载");

int start = i * currentPartSize;//计算每条线程下载的开始位置

int end = start + currentPartSize-1;//线程结束的位置

if(i==mThreadNum-1){

end=fileSize;

}

String threadId = "xiaoma" + i;

ThreadInfo threadInfo = new ThreadInfo(threadId, start, end, 0,newInfo.getUrl());

Thread.getInstance().insert(threadInfo);

DownLoadTask thread = new DownLoadTask(threadInfo,newInfo, threadId, start, end, 0);

DownLoadManager.executorService.execute(thread);

threads.add(thread);

} else {

UtilsLog.i("不是第一次多线程下载");

ThreadInfo threadInfo = Thread.getInstance().query("xiaoma" + i, newInfo.getUrl());

DownLoadTask thread = new DownLoadTask(threadInfo,newInfo,threadInfo.getThreadId(),threadInfo.getStart(),threadInfo.getEnd(),threadInfo.getCompeleted());//这里出现过问题

DownLoadManager.executorService.execute(thread);

threads.add(thread);

}

}

boolean isCompleted=false;

while(!isCompleted){

isCompleted=true;

for(DownLoadTask thread:threads){

totalComleted+=thread.completed;

if(!thread.isCompleted){

isCompleted=false;

}

}

if(newInfo.isStop()){

totalComleted=0;

return;

}

Message message=new Message();

message.what=0x555;

message.arg1=fileSize;

message.arg2=totalComleted;

handler.sendMessage(message);

if(isCompleted){

totalComleted=0;

//任务线程全部完成,清空集合

clear();

handler.sendEmptyMessage(0x666);

return;

}

totalComleted=0;

java.lang.Thread.sleep(1000);

}

}catch (Exception e) {

e.printStackTrace();

}finally {

try {

if (con != null) {

con.disconnect();

}

if(raf!=null){

raf.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

}.start();

}

private Handler handler=new Handler(){

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

switch (msg.what){

case 0x555:

if(mlistener!=null){

mlistener.progress(msg.arg1,msg.arg2);

}

break;

case 0x666:

if(mlistener!=null){

mlistener.comleted();

}

break;

}

}

};}

5.接下来呢,就是DownLoadTask类了,就是一个线程下载类。

public class DownLoadTask extends java.lang.Thread{

private int start;//当前线程的开始下载位置

private int end;//当前线程结束下载的位置

private RandomAccessFile raf;//当前线程负责下载的文件大小

public int completed=0;//当前线程已下载的字节数

private String threadId;//自己定义的线程Id

private FileInfo info;

private ThreadInfo threadInfo;

public boolean isCompleted=false; //true为当前线程完成任务,false为当前线程未完成任务

//保存新的start

public int finshed=0;

public int newStart=0;

public DownLoadTask(ThreadInfo threadInfo,FileInfo info,String threadId, int start, int end,int completed){

this.threadInfo=threadInfo;

this.info=info;

this.threadId=threadId;

this.start=start;

this.end=end;

this.completed=completed;

}

@Override

public void run() {

HttpURLConnection con = null;

try {

UtilsLog.i("start="+start+",end="+end+",completed="+completed+",threadId="+getThreadId());

URL url = new URL(info.getUrl());

con = (HttpURLConnection) url.openConnection();

con.setConnectTimeout(2 * 1000);

con.setRequestMethod("GET");

con.setRequestProperty("Range", "bytes=" + start + "-"+end);//重点

raf=new RandomAccessFile(DownLoadManager.file,"rwd");

//从文件的某一位置写入

raf.seek(start);

if (con.getResponseCode() == 206) { //文件部分下载 返回码是206

InputStream is = con.getInputStream();

byte[] buffer = new byte[4096];

int hasRead = 0;

while ((hasRead = is.read(buffer)) != -1) {

//写入文件

raf.write(buffer, 0, hasRead);

//单个文件的完成程度

completed += hasRead;

threadInfo.setCompeleted(completed);

//保存新的start

finshed=finshed+hasRead;//这里出现过问题,嘻嘻

newStart=start+finshed;

threadInfo.setStart(newStart);

//UtilsLog.i("Thread:"+getThreadId()+",completed=" + completed);

//停止下载

if (info.isStop()) {

UtilsLog.i("isStop="+info.isStop());

//保存下载进度

UtilsLog.i("现在Thread:"+getThreadId()+",completed=" + completed);

Thread.getInstance().update(threadInfo);

return;

}

}

//删除该线程下载记录

Thread.getInstance().delete(threadInfo);

isCompleted=true;

Thread.getInstance().update(threadInfo);

UtilsLog.i("thread:"+getThreadId()+"已经完成任务!--"+"completed="+completed);

}

} catch (Exception e) {

if (con != null) {

con.disconnect();

}

try {

if (raf != null) {

raf.close();

}

} catch (IOException e1) {

e1.printStackTrace();

}

}

}

public String getThreadId() {

return threadId;

}}

6.接口,就是一个监听下载进度的接口,也是很简单。

public interface ResultListener{

void progress(int max, int progress);

void comleted();}

结束

大致操作就是这样,其实多线程也挺简单的。

以上所述是小编给大家介绍的Android多线程+单线程+断点续传+进度条显示下载功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值