Android之—多线程断点续传下载

本文介绍了一个基于Android平台的多线程断点续传文件下载系统。通过划分文件为多个部分并行下载,提高下载效率。利用数据库记录下载进度,支持断点续传,确保下载稳定性。

        

Android之——多线程断点续传下载示例

文件下载在App应用中也用到很多,一般版本更新时多要用的文件下载来进行处理,以前也有看过很多大神有过该方面的博客,今天我也自己来实践一下,写的一般,还请大家多提意见,共同进步。主要思路:

1.多线程下载:

首先通过下载总线程数来划分文件的下载区域:利用int range = fileSize / threadCount;得到每一段下载量;每一段的位置是i * range到(i + 1) * rang - 1,注意最后一段的位置是到filesize - 1;

通过Http协议的Range字段实现下载文件的分段;

通过Java类RandomAccessFile可以实现文件的随机访问,利用seek方法定位的文件的指定位置;

由HttpUrlConnection获取流来进行流的读写,实现文件的存储;

在下载过程中利用Handler来向外传递下载的信息。

2.断点续传:

对于每一个线程利用一个DownloadInfo类来保存下载的信息,每次在下载过程中向数据库更新信息(我也有想过只在下载暂停时进行更新,但那样的话我们的进程被杀掉时信息就无法保存下来)。在进行下载之前去访问数据库是否有记录存在,如果没有执行第一次下载的初始化,如果存在记录但下载文件不存在时,删掉数据库中的记录之后进行第一次下载的初始化,如果有记录且文件存在,则从数据库中取出信息。

实现的效果如图,自己封装的类提供了开始,暂停,删除,以及重新下载的方法。还没来得及将工程穿上优快云,给大家一个百度云盘的下载地址:https://pan.baidu.com/s/1dD1Xo8T

\


package com.example.test;
 
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
 
/**
 * 利用数据库来记录下载信息
 * @author acer
 */
public class DownLoadHelper extendsSQLiteOpenHelper{
 
    privatestaticfinal String SQL_NAME ="download.db";
    privatestaticfinal int DOWNLOAD_VERSION=1;
     
    publicDownLoadHelper(Context context) {
        super(context, SQL_NAME,null, DOWNLOAD_VERSION);
        // TODO Auto-generated constructor stub
    }
     
     /**
     * 在download.db数据库下创建一个download_info表存储下载信息
     */
    @Override
    publicvoidonCreate(SQLiteDatabase db) {
        db.execSQL("create table download_info(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, "
                +"start_pos integer, end_pos integer, compelete_size integer,url char)");
    }
    @Override
    publicvoidonUpgrade(SQLiteDatabase db,intoldVersion,int newVersion) {
 
    }
 
}

DownlaodSqlTool进行数据的插入更新删除等操作

package com.example.test;
 
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
 
/**
 *
 * 数据库操作工具类
 */
public class DownlaodSqlTool {
    privateDownLoadHelper dbHelper;
 
    publicDownlaodSqlTool(Context context) {
        dbHelper =newDownLoadHelper(context);
    }
 
    /**
     * 创建下载的具体信息
     */
    publicvoidinsertInfos(List<downloadinfo> infos) {
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        for(DownloadInfo info : infos) {
            String sql ="insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)";
            Object[] bindArgs = { info.getThreadId(), info.getStartPos(),
                    info.getEndPos(), info.getCompeleteSize(), info.getUrl() };
            database.execSQL(sql, bindArgs);
        }
    }
 
    /**
     * 得到下载具体信息
     */
    publicList<downloadinfo> getInfos(String urlstr) {
        List<downloadinfo> list =newArrayList<downloadinfo>();
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        String sql ="select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";
        Cursor cursor = database.rawQuery(sql,newString[] { urlstr });
        while(cursor.moveToNext()) {
            DownloadInfo info =newDownloadInfo(cursor.getInt(0),
                    cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),
                    cursor.getString(4));
            list.add(info);
        }
        returnlist;
    }
 
    /**
     * 更新数据库中的下载信息
     */
    publicvoidupdataInfos(intthreadId,int compeleteSize, String urlstr) {
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        String sql ="update download_info set compelete_size=? where thread_id=? and url=?";
        Object[] bindArgs = { compeleteSize, threadId, urlstr };
        database.execSQL(sql, bindArgs);
    }
    /**
     * 关闭数据库
     */
    publicvoidcloseDb() {
        dbHelper.close();
    }
 
    /**
     * 下载完成后删除数据库中的数据
     */
    publicvoiddelete(String url) {
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        database.delete("download_info","url=?",new String[] { url });
    }
}</downloadinfo></downloadinfo></downloadinfo></downloadinfo>

DownloadHttpTool进行网络下载的类

package com.example.test;
 
import java.io.File;
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 android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
 
public class DownloadHttpTool {
    /**
     *
     * 利用Http协议进行多线程下载具体实践类
     */
 
    privatestaticfinal String TAG = DownloadHttpTool.class.getSimpleName();
 
    privateintthreadCount;//线程数量
    privateString urlstr;//URL地址
    privateContext mContext;
    privateHandler mHandler;
    privateList<downloadinfo> downloadInfos;//保存下载信息的类
 
    privateString localPath;//目录
    privateString fileName;//文件名
    privateintfileSize;
    privateDownlaodSqlTool sqlTool;//文件信息保存的数据库操作类
 
    privateenumDownload_State {
        Downloading, Pause, Ready;//利用枚举表示下载的三种状态
    }
 
    privateDownload_State state = Download_State.Ready;//当前下载状态
 
    privateintglobalCompelete = 0;//所有线程下载的总数
 
    publicDownloadHttpTool(intthreadCount, String urlString,
            String localPath, String fileName, Context context, Handler handler) {
        super();
        this.threadCount = threadCount;
        this.urlstr = urlString;
        this.localPath = localPath;
        this.mContext = context;
        this.mHandler = handler;
        this.fileName = fileName;
        sqlTool =newDownlaodSqlTool(mContext);
    }
 
    //在开始下载之前需要调用ready方法进行配置
    publicvoidready() {
        Log.w(TAG,"ready");
        globalCompelete =0;
        downloadInfos = sqlTool.getInfos(urlstr);
        if(downloadInfos.size() ==0) {
            initFirst();
        }else{
            File file =newFile(localPath +"/"+ fileName);
            if(!file.exists()) {
                sqlTool.delete(urlstr);
                initFirst();
            }else{
                fileSize = downloadInfos.get(downloadInfos.size() -1)
                        .getEndPos();
                for(DownloadInfo info : downloadInfos) {
                    globalCompelete += info.getCompeleteSize();
                }
                Log.w(TAG,"globalCompelete:::"+ globalCompelete);
            }
        }
    }
 
    publicvoidstart() {
        Log.w(TAG,"start");
        if(downloadInfos !=null) {
            if(state == Download_State.Downloading) {
                return;
            }
            state = Download_State.Downloading;
            for(DownloadInfo info : downloadInfos) {
                Log.v(TAG,"startThread");
                newDownloadThread(info.getThreadId(), info.getStartPos(),
                        info.getEndPos(), info.getCompeleteSize(),
                        info.getUrl()).start();
            }
        }
    }
 
    publicvoidpause() {
        state = Download_State.Pause;
        sqlTool.closeDb();
    }
     
    publicvoiddelete(){
        compelete();
        File file =newFile(localPath +"/"+ fileName);
        file.delete();
    }
     
    publicvoidcompelete() {
        sqlTool.delete(urlstr);
        sqlTool.closeDb();
    }
 
    publicintgetFileSize() {
        returnfileSize;
    }
 
    publicintgetCompeleteSize() {
        returnglobalCompelete;
    }
 
    //第一次下载初始化
    privatevoidinitFirst() {
        Log.w(TAG,"initFirst");
        try{
            URL url =newURL(urlstr);
            HttpURLConnection connection = (HttpURLConnection) url
                    .openConnection();
            connection.setConnectTimeout(5000);
            connection.setRequestMethod("GET");
            fileSize = connection.getContentLength();
            Log.w(TAG,"fileSize::"+ fileSize);
            File fileParent =newFile(localPath);
            if(!fileParent.exists()) {
                fileParent.mkdir();
            }
            File file =newFile(fileParent, fileName);
            if(!file.exists()) {
                file.createNewFile();
            }
            // 本地访问文件
            RandomAccessFile accessFile =newRandomAccessFile(file,"rwd");
            accessFile.setLength(fileSize);
            accessFile.close();
            connection.disconnect();
        }catch(Exception e) {
            e.printStackTrace();
        }
        intrange = fileSize / threadCount;
        downloadInfos =newArrayList<downloadinfo>();
        for(inti = 0; i < threadCount -1; i++) {
            DownloadInfo info =newDownloadInfo(i, i * range, (i +1) * range
                    -1,0, urlstr);
            downloadInfos.add(info);
        }
        DownloadInfo info =newDownloadInfo(threadCount -1, (threadCount -1)
                * range, fileSize -1,0, urlstr);
        downloadInfos.add(info);
        sqlTool.insertInfos(downloadInfos);
    }
 
    //自定义下载线程
    privateclassDownloadThread extendsThread {
 
        privateintthreadId;
        privateintstartPos;
        privateintendPos;
        privateintcompeleteSize;
        privateString urlstr;
        privateinttotalThreadSize;
 
        publicDownloadThread(intthreadId,int startPos, int endPos,
                intcompeleteSize, String urlstr) {
            this.threadId = threadId;
            this.startPos = startPos;
            this.endPos = endPos;
            totalThreadSize = endPos - startPos +1;
            this.urlstr = urlstr;
            this.compeleteSize = compeleteSize;
        }
 
        @Override
        publicvoidrun() {
            HttpURLConnection connection =null;
            RandomAccessFile randomAccessFile =null;
            InputStream is =null;
            try{
                randomAccessFile =newRandomAccessFile(localPath +"/"
                        + fileName,"rwd");
                randomAccessFile.seek(startPos + compeleteSize);
                URL url =newURL(urlstr);
                connection = (HttpURLConnection) url.openConnection();
                connection.setConnectTimeout(5000);
                connection.setRequestMethod("GET");
                connection.setRequestProperty("Range","bytes="
                        + (startPos + compeleteSize) +"-"+ endPos);
                is = connection.getInputStream();
                byte[] buffer =newbyte[1024];
                intlength = -1;
                while((length = is.read(buffer)) != -1) {
                    randomAccessFile.write(buffer,0, length);
                    compeleteSize += length;
                    Message message = Message.obtain();
                    message.what = threadId;
                    message.obj = urlstr;
                    message.arg1 = length;
                    mHandler.sendMessage(message);
                    sqlTool.updataInfos(threadId, compeleteSize, urlstr);
                    Log.w(TAG,"Threadid::"+ threadId +"    compelete::"
                            + compeleteSize +"    total::"+ totalThreadSize);
                    if(compeleteSize >= totalThreadSize) {
                        break;
                    }
                    if(state != Download_State.Downloading) {
                        break;
                    }
                }
 
            }catch(Exception e) {
                e.printStackTrace();
            }finally{
                try{
                    if(is !=null) {
                        is.close();
                    }
                    randomAccessFile.close();
                    connection.disconnect();
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}</downloadinfo></downloadinfo>

DownloadUtils提供下载向外接口方法类:

package com.example.test;
 
import android.annotation.SuppressLint;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
 
/**
 * 将下载方法封装在此类
 * 提供下载,暂停,删除,以及重置的方法
 */
public class DownloadUtil {
 
    privateDownloadHttpTool mDownloadHttpTool;
    privateOnDownloadListener onDownloadListener;
 
    privateintfileSize;
    privateintdownloadedSize = 0;
 
    @SuppressLint("HandlerLeak")
    privateHandler mHandler =newHandler() {
 
        @Override
        publicvoidhandleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            intlength = msg.arg1;
            synchronized(this) {//加锁保证已下载的正确性
                downloadedSize += length;
            }
            if(onDownloadListener !=null) {
                onDownloadListener.downloadProgress(downloadedSize);
            }
            if(downloadedSize >= fileSize) {
                mDownloadHttpTool.compelete();
                if(onDownloadListener !=null) {
                    onDownloadListener.downloadEnd();
                }
            }
        }
 
    };
 
    publicDownloadUtil(intthreadCount, String filePath, String filename,
            String urlString, Context context) {
 
        mDownloadHttpTool =newDownloadHttpTool(threadCount, urlString,
                filePath, filename, context, mHandler);
    }
 
    //下载之前首先异步线程调用ready方法获得文件大小信息,之后调用开始方法
    publicvoidstart() {
        newAsyncTask<void,void,=""void="">() {
 
            @Override
            protectedVoid doInBackground(Void... arg0) {
                // TODO Auto-generated method stub
                mDownloadHttpTool.ready();
                returnnull;
            }
 
            @Override
            protectedvoidonPostExecute(Void result) {
                // TODO Auto-generated method stub
                super.onPostExecute(result);
                fileSize = mDownloadHttpTool.getFileSize();
                downloadedSize = mDownloadHttpTool.getCompeleteSize();
                Log.w("Tag","downloadedSize::"+ downloadedSize);
                if(onDownloadListener !=null) {
                    onDownloadListener.downloadStart(fileSize);
                }
                mDownloadHttpTool.start();
            }
        }.execute();
    }
 
    publicvoidpause() {
        mDownloadHttpTool.pause();
    }
     
    publicvoiddelete(){
        mDownloadHttpTool.delete();
    }
 
    publicvoidreset(){
        mDownloadHttpTool.delete();
        start();
    }
     
    publicvoidsetOnDownloadListener(OnDownloadListener onDownloadListener) {
        this.onDownloadListener = onDownloadListener;
    }
 
    //下载回调接口
    publicinterfaceOnDownloadListener {
        publicvoiddownloadStart(intfileSize);
 
        publicvoiddownloadProgress(intdownloadedSize);//记录当前所有线程下总和
 
        publicvoiddownloadEnd();
    }
}</void,>

在MainActivity

package com.example.test;
 
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
 
import com.example.test.DownloadUtil.OnDownloadListener;
 
public class MainActivity extendsFragmentActivity {
 
    privatestaticfinal String TAG = MainActivity.class.getSimpleName();
 
    privateProgressBar mProgressBar;
    privateButton start;
    privateButton pause;
    privateButton delete;
    privateButton reset;
    privateTextView total;
 
    privateintmax;
 
    privateDownloadUtil mDownloadUtil;
 
    @Override
    protectedvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
        start = (Button) findViewById(R.id.button_start);
        pause = (Button) findViewById(R.id.button_pause);
        delete = (Button) findViewById(R.id.button_delete);
        reset = (Button) findViewById(R.id.button_reset);
        total = (TextView) findViewById(R.id.textView_total);
        String urlString ="https://bbra.cn/Uploadfiles/imgs/20110303/fengjin/013.jpg";
        String localPath = Environment.getExternalStorageDirectory()
                .getAbsolutePath() +"/local";
        mDownloadUtil =newDownloadUtil(2, localPath,"abc.jpg", urlString,
                this);
        mDownloadUtil.setOnDownloadListener(newOnDownloadListener() {
 
            @Override
            publicvoiddownloadStart(intfileSize) {
                // TODO Auto-generated method stub
                Log.w(TAG,"fileSize::"+ fileSize);
                max = fileSize;
                mProgressBar.setMax(fileSize);
            }
 
            @Override
            publicvoiddownloadProgress(intdownloadedSize) {
                // TODO Auto-generated method stub
                Log.w(TAG,"Compelete::"+ downloadedSize);
                mProgressBar.setProgress(downloadedSize);
                total.setText((int) downloadedSize *100/ max + "%");
            }
 
            @Override
            publicvoiddownloadEnd() {
                // TODO Auto-generated method stub
                Log.w(TAG,"ENd");
            }
        });
        start.setOnClickListener(newOnClickListener() {
 
            @Override
            publicvoidonClick(View arg0) {
                // TODO Auto-generated method stub
                mDownloadUtil.start();
            }
        });
        pause.setOnClickListener(newOnClickListener() {
 
            @Override
            publicvoidonClick(View arg0) {
                // TODO Auto-generated method stub
                mDownloadUtil.pause();
            }
        });
        delete.setOnClickListener(newOnClickListener() {
 
            @Override
            publicvoidonClick(View arg0) {
                // TODO Auto-generated method stub
                mDownloadUtil.delete();
            }
        });
        reset.setOnClickListener(newOnClickListener() {
 
            @Override
            publicvoidonClick(View arg0) {
                // TODO Auto-generated method stub
                mDownloadUtil.reset();
            }
        });
    }
 
}





标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
在金融行业中,对信用风险的判断是核心环节之一,其结果对机构的信贷政策和风险控制策略有直接影响。本文将围绕如何借助机器学习方法,尤其是Sklearn工具包,建立用于判断信用状况的预测系统。文中将涵盖逻辑回归、支持向量机等常见方法,并通过实际操作流程进行说明。 一、机器学习基本概念 机器学习属于人工智能的子领域,其基本理念是通过数据自动学习规律,而非依赖人工设定规则。在信贷分析中,该技术可用于挖掘历史数据中的潜在规律,进而对未来的信用表现进行预测。 二、Sklearn工具包概述 Sklearn(Scikit-learn)是Python语言中广泛使用的机器学习模块,提供多种数据处理和建模功能。它简化了数据清洗、特征提取、模型构建、验证与优化等流程,是数据科学项目中的常用工具。 三、逻辑回归模型 逻辑回归是一种常用于分类任务的线性模型,特别适用于二类问题。在信用评估中,该模型可用于判断借款人是否可能违约。其通过逻辑函数将输出映射为0到1之间的概率值,从而表示违约的可能性。 四、支持向量机模型 支持向量机是一种用于监督学习的算法,适用于数据维度高、样本量小的情况。在信用分析中,该方法能够通过寻找最佳分割面,区分违约与非违约客户。通过选用不同核函数,可应对复杂的非线性关系,提升预测精度。 五、数据预处理步骤 在建模前,需对原始数据进行清理与转换,包括处理缺失值、识别异常点、标准化数值、筛选有效特征等。对于信用评分,常见的输入变量包括收入水平、负债比例、信用历史记录、职业稳定性等。预处理有助于减少噪声干扰,增强模型的适应性。 六、模型构建与验证 借助Sklearn,可以将数据集划分为训练集和测试集,并通过交叉验证调整参数以提升模型性能。常用评估指标包括准确率、召回率、F1值以及AUC-ROC曲线。在处理不平衡数据时,更应关注模型的召回率与特异性。 七、集成学习方法 为提升模型预测能力,可采用集成策略,如结合多个模型的预测结果。这有助于降低单一模型的偏差与方差,增强整体预测的稳定性与准确性。 综上,基于机器学习的信用评估系统可通过Sklearn中的多种算法,结合合理的数据处理与模型优化,实现对借款人信用状况的精准判断。在实际应用中,需持续调整模型以适应市场变化,保障预测结果的长期有效性。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值