沃梦达 / IT编程 / 移动开发 / 正文

Android实现多线程断点续传

Android实现多线程断点续传 本文实例为大家分享了Android实现多线程断点续传的具体代码,供大家参考,具体内容如下 多线程下载涉及到的知识点: 1.Service的使用:我们在Service中去下载文件:2.Thread的使用:Service本身不支持耗时操作,所以我们要去开启线程

本文实例为大家分享了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一个开源的下载大文件的框架,使用就自行百度吧

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

本文标题为:Android实现多线程断点续传