Android实现多线程断点续传

Android实现多线程断点续传

本文实例为大家分享了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、界面的代码

上面罗列知识点的时候,说到了权限,如果手机系统是6.0 以上的要获取权限即请求用户允许的那种,否则会出现android.system.ErrnoException: open failed: EACCES (Permission denied)异常,下面代码中涉及权限的就是模拟一下,具体逻辑没有严格的去实现,大家看的时候需要注意。。

package com.example.a_0102.mylearn.download; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.ProgressBar; import com.example.a_0102.mylearn.R; import java.util.ArrayList; import java.util.List; /**  * 断点续传  * 一个文件可以分成几部分,使用不同的线程进行下载,使用数据库存储每个线程的下载进度  */ public class DownLoadActivity extends AppCompatActivity {     private ListView mListView;     private List<FileInfo> fileInfoList;     private ListViewAdapter adapter;     private UpdateUIReceiver mUpdateUIReceiver;     private DownLoadTaskImpl downLoadTask;     private Button mBtnDel;     private Intent intent;     @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_down_load);         //申请权限         if (ContextCompat.checkSelfPermission(DownLoadActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {             //没有权限             Log.e("------------->", "没有权限");             ActivityCompat.requestPermissions(DownLoadActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);         } else {             Log.e("------------->", "已经有权限");         }         mBtnDel = findViewById(R.id.btn_del);         downLoadTask = new DownLoadTaskImpl(this);         //从数据库获取要下载的文件         fileInfoList = new ArrayList<>();         fileInfoList = downLoadTask.getFileInfo();         //这里是用来模拟,具体请按照需求来写         if (fileInfoList.size() == 0) {             FileInfo fileInfo1 = new FileInfo(0, "http://oslw24znh.bkt.clouddn.com/android2017_07_05.apk", "xiaobang.apk", 0, 0, 0);             FileInfo fileInfo2 = new FileInfo(1, "http://ofmudsqae.bkt.clouddn.com/%E5%91%A8%E5%86%AC%E9%9B%A8%20-%20%E4%B8%8D%E5%AE%8C%E7%BE%8E%E5%A5%B3%E5%AD%A9.mp3", "buwanmei.mp3", 0, 0, 0);             fileInfoList.add(fileInfo1);             fileInfoList.add(fileInfo2);         }         mListView = findViewById(R.id.listview);         adapter = new ListViewAdapter();         mListView.setAdapter(adapter);         //为了测试写的,可忽略         mBtnDel.setOnClickListener(new View.OnClickListener() {             @Override             public void onClick(View v) {                 Log.e("------------>","dddsize:"+downLoadTask.getFileInfo().size());                 downLoadTask.deleteFileInfo();                 Log.e("------------>","size:"+downLoadTask.getFileInfo().size());                 downLoadTask.deleteThreadInfo();             }         });     }     //申请权限的回调     @Override     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {         super.onRequestPermissionsResult(requestCode, permissions, grantResults);         Log.e("------------->", "requestCode:" + requestCode + "," + permissions[0]);         if (requestCode == 0) {             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){                 Log.e("------------->", "授权被允许" );             }else {                 Log.e("------------->", "授权没有被允许" );             }         }     }     @Override     protected void onResume() {         super.onResume();         // 1. 实例化BroadcastReceiver子类 &  IntentFilter         mUpdateUIReceiver = new UpdateUIReceiver();         IntentFilter intentFilter = new IntentFilter();         // 2. 设置接收广播的类型         intentFilter.addAction("UPDATE_PROGRESSBAR");         // 3. 动态注册:调用Context的registerReceiver()方法         registerReceiver(mUpdateUIReceiver, intentFilter);     }     // 注册广播后,要在相应位置记得销毁广播 // 即在onPause() 中unregisterReceiver(mBroadcastReceiver) // 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中 // 当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。     @Override     protected void onPause() {         super.onPause();         //销毁在onResume()方法中的广播         unregisterReceiver(mUpdateUIReceiver);     }     @Override     protected void onDestroy() {         super.onDestroy();         if (intent == null) {             return;         }         stopService(intent);     }     private class ListViewAdapter extends BaseAdapter {         @Override         public int getCount() {             return fileInfoList.size();         }         @Override         public Object getItem(int position) {             return fileInfoList.get(position);         }         @Override         public long getItemId(int position) {             return position;         }         @Override         public View getView(final int position, View convertView, ViewGroup parent) {             ViewHolder viewHolder = null;             if (convertView == null) {                 convertView = LayoutInflater.from(DownLoadActivity.this).inflate(R.layout.layout_down_item, parent, false);                 viewHolder = new ViewHolder();                 viewHolder.mProgress = convertView.findViewById(R.id.progress);                 viewHolder.mBtnDown = convertView.findViewById(R.id.btn_down);                 viewHolder.mBtnStop = convertView.findViewById(R.id.btn_stop);                 convertView.setTag(viewHolder);                 //不用更新的尽量写在这里,防止每次都调用,进度设置为100                 viewHolder.mProgress.setMax(100);                 viewHolder.mBtnDown.setOnClickListener(new View.OnClickListener() {                     @Override                     public void onClick(View v) {                         intent = new Intent(DownLoadActivity.this, DownLoadService.class);                         intent.putExtra("status", DownLoadService.STATUS_START);                         intent.putExtra("fileinfo", fileInfoList.get(position));                         startService(intent);                         if (!downLoadTask.isExitFileInfo(fileInfoList.get(position).getId())) {                             downLoadTask.insertFileInfo(fileInfoList.get(position));                         }                     }                 });                 viewHolder.mBtnStop.setOnClickListener(new View.OnClickListener() {                     @Override                     public void onClick(View v) {                         intent = new Intent(DownLoadActivity.this, DownLoadService.class);                         intent.putExtra("status", DownLoadService.STATUS_STOP);                         intent.putExtra("fileinfo", fileInfoList.get(position));                         startService(intent);                     }                 });             } else {                 viewHolder = (ViewHolder) convertView.getTag();             }             FileInfo fileInfo = fileInfoList.get(position);             viewHolder.mProgress.setProgress(fileInfo.getProgress());             return convertView;         }         class ViewHolder {             private ProgressBar mProgress;             private Button mBtnDown;             private Button mBtnStop;         }     }     /**      * 用于更新UI的广播      * 使用静态注册的广播,广播的类如果是内部类,那么,该类必须为static修饰的类,否则has no zero argument constructor 这个异常      * https://blog.csdn.net/zhongjianblackberry/article/details/56670084      * 或者用动态注册广播      */     public class UpdateUIReceiver extends BroadcastReceiver {         @Override         public void onReceive(Context context, Intent intent) {             if (intent.getAction().equals("UPDATE_PROGRESSBAR")) {                 int id = intent.getIntExtra("id", 0);                 int finished = intent.getIntExtra("finished", 0);                 int length = intent.getIntExtra("length", 0);                 if (length == 0 || length < 0) {                     return;                 }                 int progress = finished * 100 / length;                 FileInfo fileInfo = fileInfoList.get(id);                 fileInfo.setFinished(finished);                 fileInfo.setLength(length);                 fileInfo.setProgress(progress);                 adapter.notifyDataSetChanged();                 downLoadTask.updateFileInfo(fileInfo, id);             }         }     } }

4、接下来是文件类和线程类的代码

public class FileInfo implements Serializable {     private int id;     private String url;//文件的URL     private String file_name;//文件名称     private int progress;//当前进度(显示在进度条上的)     private int finished;//已下载完的(实际下载的大小)     private int length;//文件的大小     public FileInfo() {     }     public FileInfo(int id, String url, String file_name, int progress, int finished, int length) {         this.id = id;         this.url = url;         this.file_name = file_name;         this.progress = progress;         this.finished = finished;         this.length = length;     }     public int getId() {         return id;     }     public void setId(int id) {         this.id = id;     }     public String getUrl() {         return url;     }     public void setUrl(String url) {         this.url = url;     }     public String getFile_name() {         return file_name;     }     public void setFile_name(String file_name) {         this.file_name = file_name;     }     public int getProgress() {         return progress;     }     public void setProgress(int progress) {         this.progress = progress;     }     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;     }     @Override     public String toString() {         return "FileInfo{" +                 "id=" + id +                 ", url='" + url + '\'' +                 ", file_name='" + file_name + '\'' +                 ", progress=" + progress +                 ", finished=" + finished +                 ", length=" + length +                 '}';     } } public class ThreadInfo implements Serializable {     private int id;//主键自增     private int thread_id;//如果没有id,唯一的标识,多线程的时候就不知道更新哪个了     private String url;     private long start_flag;     private long end_flag;     private long finished;//该线程的下载进度     public ThreadInfo() {     }     public ThreadInfo(int thread_id, String url, long start_flag, long end_flag, long finished) {         this.thread_id = thread_id;         this.url = url;         this.start_flag = start_flag;         this.end_flag = end_flag;         this.finished = finished;     }     public int getId() {         return id;     }     public void setId(int id) {         this.id = id;     }     public int getThread_id() {         return thread_id;     }     public void setThread_id(int thread_id) {         this.thread_id = thread_id;     }     public String getUrl() {         return url;     }     public void setUrl(String url) {         this.url = url;     }     public long getStart_flag() {         return start_flag;     }     public void setStart_flag(long start_flag) {         this.start_flag = start_flag;     }     public long getEnd_flag() {         return end_flag;     }     public void setEnd_flag(long end_flag) {         this.end_flag = end_flag;     }     public long getFinished() {         return finished;     }     public void setFinished(long finished) {         this.finished = finished;     }     @Override     public String toString() {         return "ThreadInfo{" +                 "id=" + id +                 ", thread_id=" + thread_id +                 ", url='" + url + '\'' +                 ", start_flag=" + start_flag +                 ", end_flag=" + end_flag +                 ", finished=" + finished +                 '}';     } }

5、数据库的代码

这里要用单例模式,否则会报错

import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /**  * 要用单例的,否则会出现Cannot perform this operation because the connection pool has been closed  */ public class DbHalper extends SQLiteOpenHelper {     private static final String DB_NAME = "downloadfile";     private static final int DB_VERSION = 1;     private static final String CREATE_THREAD_INFO = "create table thread_info (id integer primary key autoincrement,thread_id int,url text ,start_flag int,end_flag int,finished int);";     private static final String CREATE_FILE_INFO = "create table file_info (id integer primary key,url text ,file_name text,length int,progress int,finished int);";     private static DbHalper dbHalper;     public static DbHalper getDbHalper(Context context){         if(dbHalper == null){             dbHalper = new DbHalper(context);         }         return dbHalper;     }     private DbHalper(Context context) {         super(context, DB_NAME, null, DB_VERSION);     }     @Override     public void onCreate(SQLiteDatabase db) {         db.execSQL(CREATE_THREAD_INFO);         db.execSQL(CREATE_FILE_INFO);     }     @Override     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {     } } public interface IDownLoadTask {     /**      * 插入线程信息      *      * @param threadInfo      */     void insertThreadInfo(ThreadInfo threadInfo);     /**      * 更新线程信息      *      * @param threadInfo      * @param id      */     void updateThreadInfo(ThreadInfo threadInfo, int id, String url);     /**      * 删除下载完成的线程记录      *      * @param url      */     void deleteThreadInfo(String url);     /**      * 获取所有线程信息      *      * @param url      * @return      */     List<ThreadInfo> getThreadInfos(String url);     /**      * 获取所有线程信息      *      * @return      */     List<ThreadInfo> getThreadInfos();     /**      * 插入文件信息      *      * @param fileInfo      */     void insertFileInfo(FileInfo fileInfo);     /**      * 修改文件的信息      *      * @param fileInfo      * @param id      */     void updateFileInfo(FileInfo fileInfo, int id);     /**      * 该文件信息是否存在      *      * @param id      * @return      */     boolean isExitFileInfo(int id);     /**      * 查询文件信息      *      * @return      */     List<FileInfo> getFileInfo();     /**      * 删除文件信息      */     void deleteFileInfo();     /**      * 删除文件下载的线程信息      */     void deleteThreadInfo(); }

接口类的实现,注意同步,否则多个线程一起操作一个方法会出现“惊喜“

import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import java.util.ArrayList; import java.util.List; /**  * 增、删、改方法要保证线程安全,同一时刻只能有一个线程访问  */ public class DownLoadTaskImpl implements IDownLoadTask {     private DbHalper dbHalper;     private SQLiteDatabase db;     public DownLoadTaskImpl(Context context) {         dbHalper = DbHalper.getDbHalper(context);     }     @Override     public synchronized void insertThreadInfo(ThreadInfo threadInfo) {         SQLiteDatabase db = dbHalper.getWritableDatabase();         db.execSQL("insert into thread_info (thread_id,url,start_flag,end_flag,finished) values (?,?,?,?,?);",                 new Object[]{threadInfo.getThread_id(),threadInfo.getUrl(),threadInfo.getStart_flag(),                         threadInfo.getEnd_flag(),threadInfo.getFinished()});         db.close();     }     @Override     public synchronized void updateThreadInfo(ThreadInfo threadInfo, int thread_id,String url) {         SQLiteDatabase db = dbHalper.getWritableDatabase();         db.execSQL("update thread_info set thread_id=?, url=?,start_flag=?,end_flag=?,finished=?  where thread_id = ? and url = ?;",                 new Object[]{threadInfo.getThread_id(),threadInfo.getUrl(), threadInfo.getStart_flag(),                         threadInfo.getEnd_flag(),threadInfo.getFinished(),thread_id,url});         db.close();     }     @Override     public synchronized void deleteThreadInfo(String url) {         SQLiteDatabase db = dbHalper.getWritableDatabase();         db.execSQL("delete from thread_info where url=?;",new String[]{url});         db.close();     }     @Override     public List<ThreadInfo> getThreadInfos(String url) {             List<ThreadInfo> threadInfos = new ArrayList<>();             SQLiteDatabase db = dbHalper.getReadableDatabase();             Cursor cursor = db.rawQuery("select * from thread_info where url=?;",new String[]{url});             while (cursor.moveToNext()){                 ThreadInfo threadInfo = new ThreadInfo();                 threadInfo.setThread_id(cursor.getInt(cursor.getColumnIndex("thread_id")));                 threadInfo.setUrl(cursor.getString(cursor.getColumnIndex("url")));                 threadInfo.setStart_flag(cursor.getInt(cursor.getColumnIndex("start_flag")));                 threadInfo.setEnd_flag(cursor.getInt(cursor.getColumnIndex("end_flag")));                 threadInfo.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));                 threadInfos.add(threadInfo);             }             cursor.close();             db.close();             return threadInfos;         }     @Override     public List<ThreadInfo> getThreadInfos() {         List<ThreadInfo> threadInfos = new ArrayList<>();         SQLiteDatabase db = dbHalper.getReadableDatabase();         Cursor cursor = db.rawQuery("select * from thread_info;",new String[]{});         while (cursor.moveToNext()){             ThreadInfo threadInfo = new ThreadInfo();             threadInfo.setThread_id(cursor.getInt(cursor.getColumnIndex("thread_id")));             threadInfo.setUrl(cursor.getString(cursor.getColumnIndex("url")));             threadInfo.setStart_flag(cursor.getInt(cursor.getColumnIndex("start_flag")));             threadInfo.setEnd_flag(cursor.getInt(cursor.getColumnIndex("end_flag")));             threadInfo.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));             threadInfos.add(threadInfo);         }         cursor.close();         db.close();         return threadInfos;     }     @Override     public synchronized void insertFileInfo(FileInfo fileInfo) {         SQLiteDatabase db = dbHalper.getWritableDatabase();         db.execSQL("replace into file_info (id,url,file_name,length,progress,finished) values (?,?,?,?,?,?);",                 new Object[]{fileInfo.getId(),fileInfo.getUrl(),fileInfo.getFile_name(),fileInfo.getLength(),                         fileInfo.getProgress(),fileInfo.getFinished()});         db.close();     }     @Override     public synchronized void updateFileInfo(FileInfo fileInfo, int id) {         SQLiteDatabase db = dbHalper.getWritableDatabase();         db.execSQL("update file_info set id=?, url=?,file_name=?,length=?,progress=?,finished=?  where id = ?;",                 new Object[]{fileInfo.getId(),fileInfo.getUrl(), fileInfo.getFile_name(),fileInfo.getLength(),                         fileInfo.getProgress(),fileInfo.getFinished(),id});         db.close();     }     @Override     public boolean isExitFileInfo(int id) {         SQLiteDatabase db = dbHalper.getReadableDatabase();         boolean isExit = false;         Cursor cursor = db.rawQuery("select * from file_info where id=?;",new String[]{id+""});         while (cursor.moveToNext()){             isExit = true;         }         cursor.close();         db.close();         return isExit;     }     @Override     public List<FileInfo> getFileInfo() {         List<FileInfo> fileInfos = new ArrayList<>();         SQLiteDatabase db = dbHalper.getReadableDatabase();         Cursor cursor = db.rawQuery("select * from file_info;",new String[]{});         while (cursor.moveToNext()){             FileInfo fileInfo = new FileInfo();             fileInfo.setId(cursor.getInt(cursor.getColumnIndex("id")));             fileInfo.setUrl(cursor.getString(cursor.getColumnIndex("url")));             fileInfo.setFile_name(cursor.getString(cursor.getColumnIndex("file_name")));             fileInfo.setLength(cursor.getInt(cursor.getColumnIndex("length")));             fileInfo.setProgress(cursor.getInt(cursor.getColumnIndex("progress")));             fileInfo.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));             fileInfos.add(fileInfo);         }         cursor.close();         db.close();         return fileInfos;     }     @Override     public synchronized void deleteFileInfo() {         SQLiteDatabase db = dbHalper.getWritableDatabase();         db.execSQL("delete from file_info;",new String[]{});         db.close();     }     @Override     public void deleteThreadInfo() {         SQLiteDatabase db = dbHalper.getWritableDatabase();         db.execSQL("delete from thread_info;",new String[]{});         db.close();     } }

提示:可以直接使用FileDownloader一个开源的下载大文件的框架,使用就自行百度吧

推荐阅读