本文整理自网络,侵删。
本文实例为大家分享了Android实现多线程断点续传的具体代码,供大家参考,具体内容如下
多线程下载涉及到的知识点:
1、Service的使用:我们在Service中去下载文件;
2、Thread的使用:Service本身不支持耗时操作,所以我们要去开启线程;
3、Sqlite的使用:使用数据库来存储每个线程下载的文件的进度,和文件的下载情况;
4、权限:涉及到文件的读写就要用到权限;
5、BroadCastReceiver的使用:通过广播来更新下载进度;
6、线程池使用:使用线程池来管理线程,减少资源的浪费
7、HttpUrlConnection的使用:下载文件使用的
8、ListView和BaseAdapter的使用:下载列表的显示
9、RandomAccessFile使用
先解释一下我们要做什么:
1、我们现在有一个文件,然后要分成好几个线程去下载,那么我们需要将这个文件平分,然后分给各个线程去下载,而每个线程在下载的时候,你不一定啥时候点了暂停,那么就要记录我的下载进度,所以要用到数据库。
2、你可能又会问,怎么去知道谁下载哪呢?我们的HttpURLConnection可以通过他的setRequestProperty()方法设置下载范围,从哪开始到哪结束。
3、同样下载解决了,那么写文件呢,怎么往文件里面写呢,那么就要用到RandomAccessFile这个文件的特性了,从文件的任意位置开始写,是不是清晰了。
4、 还有问题就是怎么更新界面,用我们的广播,告诉什么时候去更新界面。
(实现的效果,是一个文件可以由多个线程下载,可以同时下载多个文件)
**这里需要注意:**不可以在获取长度后直接去下载文件,因为,我们获取文件长度的时候需要使用的请求码是200,如果我们想要分段去下载(也就是设置了connection.setRequestProperty(“Range”,“bytes=”"之后就是分段下载了)那么使用到的请求码是206。所以我们这里要将这两个请求分开来写,我就一开始将两个写到一起了,但是是不可以的会报错,更不要想着通过请求码来区分,这个就更错了
1、下面贴出***服务类***的代码:
这里的工作主要就是开启下载任务和停止下载任务,还有就是获取下载文件的长度,并创建本地文件并设置长度。
public class DownLoadService extends Service { ? ? public static final int STATUS_START = 0; ? ? public static final int STATUS_STOP = 1; ? ? public static final String PATH = Environment.getExternalStorageDirectory().getAbsolutePath(); ? ? private FileInfo mFileInfo; ? ? //统一管理DownLoadTask,有个文件下载就有个DownLoadTask,所以使用Map去管理,主要控制暂停 ? ? private Map<Integer,Object> downtaskMap = new HashMap<>(); ? ? private DownLoadTask downLoadTask; ? ? @Override ? ? public void onCreate() { ? ? ? ? super.onCreate(); ? ? } ? ? @Override ? ? public int onStartCommand(Intent intent, int flags, int startId) { ? ? ? ? if (intent != null) { ? ? ? ? ? ? int status = intent.getIntExtra("status", 0); ? ? ? ? ? ? if (status == STATUS_START) { ? ? ? ? ? ? ? ? //开始下载 ? ? ? ? ? ? ? ? mFileInfo = (FileInfo) intent.getSerializableExtra("fileinfo"); ? ? ? ? ? ? ? ? DownLoadTask.sExecutorService.execute(new GetFileLenght(mFileInfo, this)); ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? //暂停下载 ? ? ? ? ? ? ? ? mFileInfo = (FileInfo) intent.getSerializableExtra("fileinfo"); ? ? ? ? ? ? ? ? Log.e("---------->","mFileInfo:"+mFileInfo); ? ? ? ? ? ? ? ? downLoadTask = (DownLoadTask) downtaskMap.get(mFileInfo.getId()); ? ? ? ? ? ? ? ? if(downLoadTask!=null){ ? ? ? ? ? ? ? ? ? ? downLoadTask.isPause = true; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return super.onStartCommand(intent, flags, startId); ? ? } ? ? @Nullable ? ? @Override ? ? public IBinder onBind(Intent intent) { ? ? ? ? return null; ? ? } ? ? /** ? ? ?* 获得要下载的文件的长度,并创建本地文件 ? ? ?* 不能和下载的线程写在一起 ? ? ?*/ ? ? class GetFileLenght extends Thread { ? ? ? ? private FileInfo fileInfo; ? ? ? ? private Context context; ? ? ? ? public GetFileLenght(FileInfo fileInfo, Context context) { ? ? ? ? ? ? this.fileInfo = fileInfo; ? ? ? ? ? ? this.context = context; ? ? ? ? } ? ? ? ? @Override ? ? ? ? public void run() { ? ? ? ? ? ? super.run(); ? ? ? ? ? ? HttpURLConnection conn = null; ? ? ? ? ? ? RandomAccessFile raf = null; ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? URL url = new URL(fileInfo.getUrl()); ? ? ? ? ? ? ? ? conn = (HttpURLConnection) url.openConnection(); ? ? ? ? ? ? ? ? conn.setConnectTimeout(5000); ? ? ? ? ? ? ? ? conn.setRequestMethod("GET"); ? ? ? ? ? ? ? ? int length = -1; ? ? ? ? ? ? ? ? if (conn.getResponseCode() == 200) { ? ? ? ? ? ? ? ? ? ? length = conn.getContentLength(); ? ? ? ? ? ? ? ? ? ? if (length > 0) { ? ? ? ? ? ? ? ? ? ? ? ? //创建本地文件 ? ? ? ? ? ? ? ? ? ? ? ? File file = new File(PATH, fileInfo.getFile_name()); ? ? ? ? ? ? ? ? ? ? ? ? raf = new RandomAccessFile(file, "rwd"); ? ? ? ? ? ? ? ? ? ? ? ? //设置本地文件的长度 ? ? ? ? ? ? ? ? ? ? ? ? raf.setLength(length); ? ? ? ? ? ? ? ? ? ? ? ? fileInfo.setLength(length); ? ? ? ? ? ? ? ? ? ? ? ? //开始下载 ? ? ? ? ? ? ? ? ? ? ? ? downLoadTask =new DownLoadTask(DownLoadService.this,fileInfo); ? ? ? ? ? ? ? ? ? ? ? ? downLoadTask.down(); ? ? ? ? ? ? ? ? ? ? ? ? downtaskMap.put(fileInfo.getId(),downLoadTask); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } finally { ? ? ? ? ? ? ? ? conn.disconnect(); ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? if (raf != null) { ? ? ? ? ? ? ? ? ? ? ? ? raf.close(); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? } }
2、DownLoadTask的代码,也就是真正的核心的地方
这里的关系是一个FileInfo对应一个DownLoadTask,一个DownLoadTask对应着多个线程
package com.example.a_0102.mylearn.download; import android.content.Context; import android.content.Intent; import android.util.Log; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** ?* 下载文件的内容 ?*/ public class DownLoadTask { ? ? private Context context; ? ? private FileInfo fileInfo; ? ? private int countForThread = 3;//线程的数量 ? ? private int mFinished; ? ? private DownLoadTaskImpl downLoadTask; ? ? private List<ThreadInfo> threadInfos; ? ? private List<DownLoadThread> downLoadThreads; ? ? public boolean isPause = false; ? ? public static ExecutorService sExecutorService = Executors.newCachedThreadPool();//共用一个线程池 ? ? public DownLoadTask(Context context,FileInfo fileInfo) { ? ? ? ? this.fileInfo = fileInfo; ? ? ? ? this.context = context; ? ? ? ? downLoadTask = new DownLoadTaskImpl(context); ? ? } ? ? public void down(){ ? ? ? ? threadInfos = downLoadTask.getThreadInfos(fileInfo.getUrl()); ? ? ? ? if(threadInfos.size() == 0){ ? ? ? ? ? ? mFinished = 0; ? ? ? ? ? ? //计算每个线程应下载的长度 ? ? ? ? ? ? int every_length = fileInfo.getLength()/countForThread; ? ? ? ? ? ? for(int i = 0;i<countForThread;i++){ ? ? ? ? ? ? ? ? ThreadInfo threadInfo = new ThreadInfo(); ? ? ? ? ? ? ? ? threadInfo.setStart_flag(i*every_length); ? ? ? ? ? ? ? ? threadInfo.setEnd_flag((i+1)*every_length-1); ? ? ? ? ? ? ? ? threadInfo.setFinished(0); ? ? ? ? ? ? ? ? threadInfo.setUrl(fileInfo.getUrl()); ? ? ? ? ? ? ? ? threadInfo.setThread_id(i); ? ? ? ? ? ? ? ? //可能不能平分,最后一个线程的长度为剩余的所有 ? ? ? ? ? ? ? ? if(i == countForThread-1){ ? ? ? ? ? ? ? ? ? ? threadInfo.setEnd_flag(fileInfo.getLength()); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? downLoadTask.insertThreadInfo(threadInfo); ? ? ? ? ? ? ? ? threadInfos.add(threadInfo); ? ? ? ? ? ? } ? ? ? ? }else { ? ? ? ? ? ? //该文件一共下载了多少了 ? ? ? ? ? ? mFinished = fileInfo.getFinished(); ? ? ? ? } ? ? ? ? downLoadThreads = new ArrayList<>(); ? ? ? ? DownLoadThread downLoadThread = null; ? ? ? ? for(int i = 0;i<threadInfos.size();i++){ ? ? ? ? ? ? downLoadThread = new DownLoadThread(threadInfos.get(i)); // ? ? ? ? ? ?downLoadThread.start(); ? ? ? ? ? ? DownLoadTask.sExecutorService.execute(downLoadThread);//执行线程,相当于开启个线程使用这个就不需要使用.start方法 ? ? ? ? ? ? downLoadThreads.add(downLoadThread); ? ? ? ? } ? ? } ? ? //真正开始下载文件的线程 ? ? class DownLoadThread extends Thread{ ? ? ? ? private ThreadInfo threadInfo; ? ? ? ? private boolean isFinished;//该线程是否结束 ? ? ? ? public DownLoadThread(ThreadInfo threadInfo) { ? ? ? ? ? ? this.threadInfo = threadInfo; ? ? ? ? ? ? Log.e("------------->","threadInfo:"+threadInfo); ? ? ? ? } ? ? ? ? @Override ? ? ? ? public void run() { ? ? ? ? ? ? super.run(); ? ? ? ? ? ? HttpURLConnection connection = null; ? ? ? ? ? ? RandomAccessFile accessFile = null; ? ? ? ? ? ? InputStream inputStream = null; ? ? ? ? ? ? Intent intent = new Intent(); ? ? ? ? ? ? intent.setAction("UPDATE_PROGRESSBAR"); ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? URL url = new URL(threadInfo.getUrl()); ? ? ? ? ? ? ? ? connection = (HttpURLConnection) url.openConnection(); ? ? ? ? ? ? ? ? connection.setRequestMethod("GET"); ? ? ? ? ? ? ? ? connection.setConnectTimeout(5000); ?? ??? ??? ??? ?//下载开始的范围是,这个线程的开始下载的地方+已经下载的进度 ? ? ? ? ? ? ? ? long start = threadInfo.getStart_flag()+threadInfo.getFinished(); ? ? ? ? ? ? ? ? //设置下载的范围 ? ? ? ? ? ? ? ? connection.setRequestProperty("Range","bytes="+start+"-"+threadInfo.getEnd_flag()); ? ? ? ? ? ? ? ? File file = new File(DownLoadService.PATH,fileInfo.getFile_name()); ? ? ? ? ? ? ? ? accessFile = new RandomAccessFile(file,"rwd"); ? ? ? ? ? ? ? ? //设置文件写入位置 ? ? ? ? ? ? ? ? accessFile.seek(start); ? ? ? ? ? ? ? ? int len = -1; ? ? ? ? ? ? ? ? byte[] bytes = new byte[1024]; ? ? ? ? ? ? ? ? if(connection.getResponseCode() == 206){ ? ? ? ? ? ? ? ? ? ? inputStream = connection.getInputStream(); ? ? ? ? ? ? ? ? ? ? long time = System.currentTimeMillis(); ? ? ? ? ? ? ? ? ? ? while ((len = inputStream.read(bytes))!=-1){ ? ? ? ? ? ? ? ? ? ? ? ? accessFile.write(bytes,0,len); ? ? ? ? ? ? ? ? ? ? ? ? //文件整体的下载进度 ? ? ? ? ? ? ? ? ? ? ? ? mFinished+=len; ? ? ? ? ? ? ? ? ? ? ? ? threadInfo.setFinished(threadInfo.getFinished()+len); ? ? ? ? ? ? ? ? ? ? ? ? //每1秒钟发送一个广播更新界面 ? ? ? ? ? ? ? ? ? ? ? ? if(System.currentTimeMillis()-time>1000){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? time = System.currentTimeMillis(); ? ? ? ? ? ? ? ? ? ? ? ? ? ? //以便区分下载的是那个文件 ? ? ? ? ? ? ? ? ? ? ? ? ? ? intent.putExtra("id",fileInfo.getId()); ? ? ? ? ? ? ? ? ? ? ? ? ? ? intent.putExtra("length",fileInfo.getLength()); ? ? ? ? ? ? ? ? ? ? ? ? ? ? intent.putExtra("finished",mFinished); ? ? ? ? ? ? ? ? ? ? ? ? ? ? context.sendBroadcast(intent); ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? //暂停更新数据库 ? ? ? ? ? ? ? ? ? ? ? ? if(isPause){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? downLoadTask.updateThreadInfo(threadInfo,threadInfo.getThread_id(),threadInfo.getUrl()); ? ? ? ? ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? Log.e("------------>","线程结束:"+threadInfo.toString()); ? ? ? ? ? ? ? ? ? ? isFinished = true; ? ? ? ? ? ? ? ? ? ? downLoadTask.updateThreadInfo(threadInfo,threadInfo.getThread_id(),threadInfo.getUrl()); ? ? ? ? ? ? ? ? ? ? checkAllThreadFinish(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? }finally { ? ? ? ? ? ? ? ? connection.disconnect(); ? ? ? ? ? ? ? ? if(inputStream!=null){ ? ? ? ? ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? ? ? ? ? inputStream.close(); ? ? ? ? ? ? ? ? ? ? ? ? accessFile.close(); ? ? ? ? ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? //所有的线程下载完成 ? ? ? ? private synchronized void checkAllThreadFinish(){ ? ? ? ? ? ? boolean finishAll = true; ? ? ? ? ? ? for(DownLoadThread downLoadThread:downLoadThreads){ ? ? ? ? ? ? ? ? if(!downLoadThread.isFinished){ ? ? ? ? ? ? ? ? ? ? finishAll = false; ? ? ? ? ? ? ? ? ? ? return; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? ? ? if(finishAll){ ? ? ? ? ? ? ? ? downLoadTask.deleteThreadInfo(fileInfo.getUrl()); ? ? ? ? ? ? ? ? //有些时候可能刚好下完,但是那1秒的时候没有取到所以进度可能停在97%,所以这样处理保证视觉的效果,可以直接将mFinished替换为fileInfo.getLength()。 ? ? ? ? ? ? ? ? Intent intent = new Intent(); ? ? ? ? ? ? ? ? intent.setAction("UPDATE_PROGRESSBAR"); ? ? ? ? ? ? ? ? intent.putExtra("id",fileInfo.getId()); ? ? ? ? ? ? ? ? intent.putExtra("length",fileInfo.getLength()); ? ? ? ? ? ? ? ? intent.putExtra("finished",mFinished); ? ? ? ? ? ? ? ? context.sendBroadcast(intent); ? ? ? ? ? ? } ? ? ? ? } ? ? } }
3、界面的代码
相关阅读 >>
android开发笔记之android中数据的存储方式(一)
python实现从sql型数据库读写dataframe型数据的方法【基于pandas】
navicat图形化界面之navicatpremium12安装与使用教程
更多相关阅读请进入《Sqlite》频道 >>

数据库系统概念 第6版
本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。