Android实现多线程断点续传

2022-07-27 10:42:59

本文实例为大家分享了Android实现多线程断点续传的具体代码,供大家参考,具体内容如下多线程下载涉及到的知识点:1、Service的使用:我们在Service中去下载文件;2、Thread的使用:S...

本文实例为大家分享了android实现多线程断点续传的具体代码,供大家参考,具体内容如下

多线程下载涉及到的知识点:

1、Service的使用:我们在Service中去下载文件;
2、Thread的使用:Service本身不支持耗时操作,所以我们要去开启线程;
3、SQLite的使用:使用数据库来存储每个线程下载的文件的进度,和文件的下载情况;
4、权限:涉及到文件的读写就要用到权限;
5、BroadCastReceiver的使用:通过广播来更新下载进度;
6、线程池使用:使用线程池来管理线程,减少资源的浪费
7、HttpUrlConnection的使用:下载文件使用的
8、ListView和BaseAdapter的使用:下载列表的显示
9、RandomAccessFile使用

先解释一下我们要做什么:

1、我们现在有一个文件,然后要分成好几个线程去下载,那么我们需要将这个文python件平分,然后分给各个线程去下载,而每个线程在下载的时候,你不一定啥时候点了暂停,那么就要记录我的下载进度,所以要用到数据库。

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 *zNTxoLfY 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一个开源的下载大文件的框架,使用就自行百度吧

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。