Android实现网络下载一(单任务下载--支持断点续传)

Android实现网络下载一(单任务下载–支持断点续传)

说起下载,在Android的一些类似游戏宝的项目中会频繁使用,比如说需求要下载一个apk文件,并且要支持暂停、继续等要求。当然在GitHib上也有一些优秀的下载框架可以供我们使用,但是更多时候的一些特殊需求可能要手动实现,所以类似的下载功能还是自己实现比较好。

附上demo源码,GitHub代码后续上传,这里的链接还是csdn的。

点这里下载源码,快,戳我戳我…

q:486789970
email:[email protected]

下图是一个单任务下载的动态图:

效果图如下(多任务下载将在下篇文章中实现)

在这里插入图片描述

上面的效果图就是单任务下载,支持断点续传、实时进度更新、下载暂停、下载继续,下载完成自动安装等功能;同时包括网络下载请求和本地文件的存储。

上图下载通过Service端下载,使用广播通知给activity更新UI界面

下载流程如下:

  • 将下载URL和文件名等信息赋值给info对象
  • 通过Intent将info对象传递给Service,使用子线程联网获取文件长度
  • 拿到长度后启动下载任务类,并将下载信息传递过去
  • 下载任务类中保存下载URL到数据库(为断点续传做准备)后开始下载
  • 下载任务执行完成后使用广播通知activty界面进度条更新。

说的比较详细,其实很简单,看看代码就知道啦。往下看:

先是xml和activity

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".activity.MainActivity">

    <ProgressBar
        android:id="@+id/probar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:padding="10dp"
        android:max="100" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:weightSum="3"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/txt_filename"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentTop="true"
            android:gravity="center"
            android:textSize="15dp"
            android:textColor="#000000"
            android:text="文件名" />


        <Button
            android:id="@+id/bt_start"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="15dp"
            android:textColor="#000000"
            android:text="开始" />

        <Button
            android:id="@+id/bt_stop"
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="15dp"
            android:textColor="#000000"
            android:text="暂停" />

    </LinearLayout>

</RelativeLayout>
package com.cc.downloaddemo.activity;

import android.Manifest;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.cc.downloaddemo.R;
import com.cc.downloaddemo.info.FileInfo;
import com.cc.downloaddemo.services.DownloadService;

/**
 * 实现断点续传的Activity
 * @author
 */
public class MainActivity extends Activity implements View.OnClickListener {

    //需要动态获取的权限
    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };


//        http://tfyxb2017-1251304591.file.myqcloud.com/qsfz/qsfz_mzyw_1663647040.apk
//        http://down.shouji.kuwo.cn/star/mobile/kwplayer_ar_pcguanwangmobile.apk
    //默认下载一个apk包
    private static final String URL = "http://down.shouji.kuwo.cn/star/mobile/kwplayer_ar_pcguanwangmobile.apk";

    //上下文
    public final Activity activity = MainActivity.this;

    //显示下载进度
    private ProgressBar probar;

    //文件名
    private TextView txt_filename;

    //开始暂停按钮
    private Button bt_start, bt_stop;

    //定义两个info实体类
    FileInfo fileInfo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取权限
        MainActivity.verifyStoragePermissions(activity);

        //获取控件
        initView();
        //初始化数据
        initData();
    }

    /**
     * 给实体类添加一些数据
     */
    private void initData() {
        //添加静态数据
        fileInfo = new FileInfo(0, URL, "酷我音乐", 0, 0);
        txt_filename.setText(fileInfo.getFilename());
    }

    /**
     * 实例化控件
     */
    private void initView() {
        txt_filename = findViewById(R.id.txt_filename);
        probar = findViewById(R.id.probar);
        bt_start = findViewById(R.id.bt_start);
        bt_stop = findViewById(R.id.bt_stop);

        bt_start.setOnClickListener(this);
        bt_stop.setOnClickListener(this);

        //注册广播接收器,并设置flag(更新)
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(DownloadService.ACTION_UPDATE);
        registerReceiver(broadcastReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //销毁广播
        unregisterReceiver(broadcastReceiver);
    }

    /**
     * 点击事件
     * @param v
     */
    @Override
    public void onClick(View v) {
        int temdId = v.getId();
        if(temdId == R.id.bt_start){
            //通知Service开始下载,设置flag(开始),并将下载文件传过去
            //这里注意,服务需要在xml里面注册
            Intent intent = new Intent(activity, DownloadService.class);
            intent.setAction(DownloadService.ACTION_START);
            intent.putExtra("fileInfo", fileInfo);
            startService(intent);
        }else if(temdId == R.id.bt_stop){
            bt_start.setText("继续");
            //通知Service暂停或者继续下载,并设置Stop(暂停或者继续),并将下载文件传过去
            Intent intent = new Intent(activity, DownloadService.class);
            intent.setAction(DownloadService.ACTION_STOP);
            intent.putExtra("fileInfo", fileInfo);
            startService(intent);
        }
    }

    /**
     * 定义广播更新UI
     */
    BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            //拿到广播通知的更新tag,并获取到当前下载的长度百分比,显示在下载进度条上
            if(intent.getAction().equals(DownloadService.ACTION_UPDATE)){
                int finish = intent.getIntExtra("finished", 0);
                probar.setProgress(finish);
                //当finish为100时,说明已经全部下载结束。
                if(finish == 100){
                    Toast.makeText(getApplicationContext(), fileInfo.getFilename() + "下载完成", Toast.LENGTH_SHORT).show();
                    bt_start.setVisibility(View.INVISIBLE);
                    bt_stop.setText("下载完成");
                }
            }
        }
    };


    /**
     * 验证读写权限
     * @param activity
     */
    public static void verifyStoragePermissions(Activity activity) {
        // Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(activity,
                Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE);
        }
    }

}

注:在activity中记得申请读写权限,避免报错;

下来就是Service的:

package com.cc.downloaddemo.services;

import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import com.cc.downloaddemo.info.FileInfo;
import java.io.File;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 下载service
 */
public class DownloadService extends Service {

    //通过URL地址下载apk文件并保存在本地存储路径
    public static final String DOWNLOAD_PATH =
            Environment.getExternalStorageDirectory().getAbsolutePath() +
                    "/downloads/";

    //开始下载
    public static final String ACTION_START = "ACTION_START";

    //暂停下载
    public static final String ACTION_STOP = "ACTION_STOP";

    //更新下载进度
    public static final String ACTION_UPDATE = "ACTION_UPDATE";

    //定义handler的flag
    public static final int MSG_INIT = 0;

    //异步下载任务类
    private DownloadTask downloadTask = null;

    /**
     * 执行下载、暂停、继续下载
     * @param intent
     * @param flags
     * @param startId
     * @return
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //获取从Activity传过去的参数,判断intent的值。
        if(ACTION_START.equals(intent.getAction())){
            FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
            //启动初始化子线程,联网下载文件内容。
            new InitThread(fileInfo).start();
        } else if (ACTION_STOP.equals(intent.getAction())) {
            //暂停下载时,更改下载任务类的flag便会自动暂停。
            if(downloadTask != null){
                downloadTask.ispause = true;
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * 网络内容下载完成后
     * 通过handler启动异步任务类开始正式下载文件。
     */
    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //是否为初始化消息
            if(msg.what == MSG_INIT){
                //获取发回来的信息
                FileInfo fileInfo = (FileInfo) msg.obj;
                //启动一个下载任务,将文件内容传递过去。
                downloadTask = new DownloadTask(DownloadService.this, fileInfo);
                //启动下载方法
                downloadTask.download();
            }
        }
    };

    /**
     * 定义子线程用来下载
     */
    class InitThread extends Thread{

        //定义一个文件用来接收下载信息
        private FileInfo mFileInfo = null;

        //启动线程时传入的对象
        public InitThread(FileInfo mFileInfo) {
            this.mFileInfo = mFileInfo;
        }

        @Override
        public void run() {
            //定义conn
            HttpURLConnection conn = null;
            //定义文件内容访问类
            RandomAccessFile raf = null;
            try {
                //连接网络文件
                URL url = new URL(mFileInfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                conn.setReadTimeout(3000);
                conn.setRequestMethod("GET");

                //自定义一个长度,注意尽量用long(处理下载消息进度百分比时好计算)
                long length = -1;
                //下载完成
                if(conn.getResponseCode() == HttpURLConnection.HTTP_OK ){
                    //获取文件总长度赋值给length
                    length = conn.getContentLength();
                }
                //长度不能小于0
                if(length <= 0){
                    return;
                }
                //判断该路径存在与否
                File dir = new File(DOWNLOAD_PATH);
                //文件不存在时创建
                if(!dir.exists()){
                    dir.mkdir();
                }
                //本地创建一个文件
                File file = new File(dir, mFileInfo.getFilename());
                //输出流
                raf = new RandomAccessFile(file, "rwd");
                //设置本地的文件长度(等于下载文件的总长度。杜绝浪费资源)
                raf.setLength(length);
                //赋值给定义的对象长度
                mFileInfo.setLength(length);
                //启动flag,将文件发送给handler处理
                handler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    //关闭资源
                    raf.close();
                    conn.disconnect();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

}

注:记得下载完之后关闭conn连接和流;

异步下载任务类(重中之重):

package com.cc.downloaddemo.services;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import com.cc.downloaddemo.db.dao.ThreadDao;
import com.cc.downloaddemo.db.impl.ThreadDaoImpl;
import com.cc.downloaddemo.info.FileInfo;
import com.cc.downloaddemo.info.ThreadInfo;
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.List;

/**
 * 任务下载类
 * @author
 */
public class DownloadTask {

    //上下文
    private Context context;

    //定义数据库线程对象文件
    private FileInfo fileInfo;

    //实例化接口
    public static ThreadDao threadDao = null;

    //定义finished(当前已下载的长度)
    private int finished = 0;

    //默认直接下载
    public boolean ispause = false;

    /**
     * 构造方法
     * @param context
     * @param fileInfo
     */
    public DownloadTask(Context context, FileInfo fileInfo) {
        this.context = context;
        //拿到handler传过来的文件
        this.fileInfo = fileInfo;

        //实例化接口实现类
        threadDao = new ThreadDaoImpl(context);
    }

    /**
     * 下载方法
     */
    public void download(){
        //下载任务一开始,先查询数据库,看是否有文件在等待下载。
        //读取数据库的所有线程信息
        List<ThreadInfo> threads = threadDao.getThreads(fileInfo.getUrl());

        //定义一个线程内容类
        ThreadInfo threadInfo;
        //当list的size为0时,说明没有等待下载的线程,为1时则有。
        if(threads.size() == 0){
            //开始初始化线程文件信息
            threadInfo = new ThreadInfo(0, fileInfo.getUrl(), 0, (int)fileInfo.getLength(), 0);
        }else{
            Log.i("download", "threads = " + threads.size());
            //因为一开始的静态内容只有一条,所以这里就直接拿下标为0的啦。
            //正式开发中,如果有多条的话,需要循环取出
            threadInfo = threads.get(0);
        }

        //创建子线程进行下载
        new DownloadThread(threadInfo).start();
    }

    /**
     * 下载线程
     */
    class DownloadThread extends Thread{

        //继续定义一个临时线程对象
        private ThreadInfo threadInfo;

        //构造
        public DownloadThread(ThreadInfo threadInfo) {
            this.threadInfo = threadInfo;
        }

        @Override
        public void run() {
            //当前线程信息在数据库没有时,说明是新线程下载,将新线程同步给数据库。
            if(!threadDao.isExits(threadInfo.getUrl(), threadInfo.getId())){
                threadDao.insertThread(threadInfo);
            }

            //定义conn、输入流和文件内容访问类
            HttpURLConnection conn = null;
            InputStream inputStream = null;
            RandomAccessFile raf = null;

            try{
                //开始联网下载
                URL url = new URL(threadInfo.getUrl());
                conn = (HttpURLConnection) url.openConnection();
                conn.setReadTimeout(3000);
                conn.setRequestMethod("GET");

                //设置下载位置(通过当前开始值和现在值判断下载位置)
                int start = threadInfo.getStart() + threadInfo.getFinished();
                conn.setRequestProperty("Range", "bytes=" + start + "-" + threadInfo.getEnd());
                //设置文件写入位置
                File file = new File(DownloadService.DOWNLOAD_PATH, fileInfo.getFilename());
                raf = new RandomAccessFile(file, "rwd");
                raf.seek(start);
                //定义一个广播,用来更新下载进度
                Intent intent = new Intent(DownloadService.ACTION_UPDATE);

                //
                finished += threadInfo.getFinished();
                //开始下载
                if(conn.getResponseCode() == HttpURLConnection.HTTP_PARTIAL){
                    //读取数据
                    inputStream = conn.getInputStream();
                    byte[] buffer = new byte[1024 * 4];
                    int len = -1;
                    long time = System.currentTimeMillis();
                    while ((len = inputStream.read(buffer)) != -1){
                        //写入文件
                        raf.write(buffer, 0, len);
                        //把下载进度发送给广播
                        finished += len;
                        if(System.currentTimeMillis() - time > 500){
                            time = System.currentTimeMillis();
                            intent.putExtra("finished", (int)(finished / (float)fileInfo.getLength() * 100));
                            Log.i("cghangdu===", (int)(finished / (float)fileInfo.getLength() * 100) + "");
                            context.sendBroadcast(intent);
                        }
                        //在下载暂停时,保存下载进度
                        if(ispause){
                            threadDao.updateThread(threadInfo.getUrl(), threadInfo.getId(), finished);
                            Log.i("下载暂停", "finished---->" + finished);
                            return;
                        }
                    }
                    intent.putExtra("finished", 100);
                    context.sendBroadcast(intent);
                    //下载完成,删除数据库所保存的线程信息
                    threadDao.deleteThread(threadInfo.getUrl(), threadInfo.getId());
                    openFile(file);
                }
            }catch (Exception e){
                e.printStackTrace();;
            }finally {
                conn.disconnect();
                try {
                    inputStream.close();
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 自动安装
     * @param file
     */
    private void openFile(File file) {
        // TODO Auto-generated method stub
        Log.e("OpenFile", file.getName());
        Intent intent = new Intent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setAction(android.content.Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.fromFile(file),
                "application/vnd.android.package-archive");
        context.startActivity(intent);
    }

}

注:数据库代码在下面

先创建数据库:

package com.cc.downloaddemo.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * 数据库帮助类
 * @author
 */
public class DBHelper extends SQLiteOpenHelper {

    public static final int DB_VERSION = 1;
    public static final String DB_NAME = "download.db";
    public static final String DB_TABLE_NAME = "thread_info";

    public static final String DB_CREATE_SQL = "create table " + DB_TABLE_NAME + "("
            + "_id integer primary key autoincrement,"
            + "thread_id Integer,"
            + "url text,"
            + "start Integer,"
            + "end Integer,"
            + "finished Integer)";

    public static final String DB_DROP_SQL = "drop table if exists thread_info";

    public DBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(DB_CREATE_SQL);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {
        db.execSQL(DB_DROP_SQL);
        db.execSQL(DB_CREATE_SQL);
    }
}

再定义保存下载进度等接口:

package com.cc.downloaddemo.db.dao;

import com.cc.downloaddemo.info.ThreadInfo;

import java.util.List;

/**
 * 线程数据访问接口
 * @author
 */
public interface ThreadDao {

    /**
     * 插入一条线程信息
     * @param threadInfo
     */
    public void insertThread(ThreadInfo threadInfo);

    /**
     * 更新线程信息
     * @param url
     * @param thread_id
     * @param finished
     */
    public void updateThread(String url, int thread_id, int finished);

    /**
     * 删除线程
     * @param url
     * @param thread_id
     */
    public void deleteThread(String url, int thread_id);

    /**
     * 查询线程
     * @param url
     * @return
     */
    public List<ThreadInfo> getThreads(String url);

    /**
     * 判断线程是否已存在
     * @param url
     * @param thread_id
     * @return
     */
    public boolean isExits(String url, int thread_id);

}

接口实现类:

package com.cc.downloaddemo.db.impl;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.cc.downloaddemo.db.DBHelper;
import com.cc.downloaddemo.db.dao.ThreadDao;
import com.cc.downloaddemo.info.ThreadInfo;

import java.util.ArrayList;
import java.util.List;

/**
 * 线程数据接口实现类
 * @author
 */
public class ThreadDaoImpl implements ThreadDao {

    private DBHelper dbHelper = null;

    public ThreadDaoImpl(Context context) {
        dbHelper = new DBHelper(context);
    }

    /**
     * 实现插入方法
     * @param threadInfo
     */
    @Override
    public void insertThread(ThreadInfo threadInfo) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        db.execSQL("insert into thread_info(thread_id, url, start, end, finished) values(?, ?, ?, ?, ?)",
                new Object[]{threadInfo.getId(), threadInfo.getUrl(), threadInfo.getStart(), threadInfo.getEnd(), threadInfo.getFinished()});
        db.close();
    }

    /**
     * 更新
     * @param url
     * @param thread_id
     * @param finished
     */
    @Override
    public void updateThread(String url, int thread_id, int finished) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("finished", finished);
        long ret = -1;
        do {
            ret = db.update("thread_info", values, "thread_id = ? and url = ?",
                    new String[] { thread_id + "", url });
        } while (ret < 0);
    }

    @Override
    public void deleteThread(String url, int thread_id) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        db.execSQL("delete from thread_info");
        db.close();
    }

    @Override
    public List<ThreadInfo> getThreads(String url) {
        List<ThreadInfo> infos = new ArrayList<>();
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Cursor cursor = db.rawQuery("select * from thread_info where url = ?", new String[]{url});
        while (cursor.moveToNext()){
            ThreadInfo info = new ThreadInfo();
            info.setId(cursor.getInt(cursor.getColumnIndex("thread_id")));
            info.setUrl(cursor.getString(cursor.getColumnIndex("url")));
            info.setStart(cursor.getInt(cursor.getColumnIndex("start")));
            info.setEnd(cursor.getInt(cursor.getColumnIndex("end")));
            info.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));
            infos.add(info);
        }
        db.close();
        return infos;
    }

    @Override
    public boolean isExits(String url, int thread_id) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        Cursor cursor =
                db.rawQuery("select * from thread_info where url = ? and thread_id = ?", new String[]{url, thread_id + ""});
        boolean exits = cursor.moveToNext();
        cursor.close();
        db.close();
        return exits;
    }
}

注:实体类在最后面:

两个实体类:

package com.cc.downloaddemo.info;

import java.io.Serializable;

/**
 * 本地存储文件信息
 * @author
 */
public class FileInfo implements Serializable {

    //id
    private int id;

    //url
    private String url;

    //文件名
    private String filename;

    //长度
    private long length;

    //下载了多少
    private int finished;

    public FileInfo() {
        super();
    }

    public FileInfo(int id, String url, String filename, long length, int finished) {
        this.id = id;
        this.url = url;
        this.filename = filename;
        this.length = length;
        this.finished = finished;
    }

    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 getFilename() {
        return filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public long getLength() {
        return length;
    }

    public void setLength(long 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 + '\'' +
                ", filename='" + filename + '\'' +
                ", length=" + length +
                ", finished=" + finished +
                '}';
    }
}

第二个:

package com.cc.downloaddemo.info;

/**
 * 数据库线程信息
 * @author
 */
public class ThreadInfo {

    //id
    private int id;

    //url
    private String url;

    //从哪里下载
    private int start;

    //结束
    private int end;

    //下载了多少
    private int finished;

    public ThreadInfo() {
        super();
    }

    public ThreadInfo(int id, String url, int start, int end, int finished) {
        this.id = id;
        this.url = url;
        this.start = start;
        this.end = end;
        this.finished = finished;
    }

    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 int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public int getEnd() {
        return end;
    }

    public void setEnd(int end) {
        this.end = end;
    }

    public int getFinished() {
        return finished;
    }

    public void setFinished(int finished) {
        this.finished = finished;
    }

    @Override
    public String toString() {
        return "ThreadInfo{" +
                "id=" + id +
                ", url='" + url + '\'' +
                ", start=" + start +
                ", end=" + end +
                ", finished=" + finished +
                '}';
    }

}

以上就是所有的代码

附上demo源码,GitHub代码后续上传,这里的链接还是csdn的。

点这里下载源码,快,戳我戳我…

q:486789970
email:[email protected]

如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。

												---财财亲笔

Guess you like

Origin blog.csdn.net/qq_35840038/article/details/90239354