Android原生实现多线程断点下载

各位父老乡亲,我单汉三又回来了,今天为大家带来一个用原生的安卓写的下载Demo。

通过本文你可以学习到:

  • SQLite的基本使用,数据库的增删改查。
  • Handler的消息处理与更新UI(你也可以看我的这篇文章)。
  • Service(主要用于下载)的进阶与使用。
  • 原生的json文件解析(多层嵌套)。
  • RandomAccessFile的基本使用,可以将文件分段。
  • 基于HttpURLConnection的大文件下载。
  • 上面内容结合,实现多线程,断点下载。

Demo是在TV上运行的,图片显示的问题不要纠结了。

文件下载的Demo已完成,没时间上传与讲解,今天为您展示并讲解一下,纯原生的东西来下载文件,希望可以帮你理解更多安卓比较基础的问题。

我们的思路:建立一个数据库,两个表,一个用来保存网络数据,一个保存本地下载的进度等等。在点击下载按钮的时候启动DownloadService,在Service中开启线程进行下载文件,文件采用RandomAccessFile,可进行多线程分段下载,如不是第一次下载,比对之后进行断点下载。

先看一下Demo的目录结构:

所有的步骤在代码里有非常详细的讲解,一定要看代码(下面是抽取的几个重要的类讲解)!

数据库的建立与DAO

DownLoadDBHelper  创建数据库  数据库更新升级,检测到版本的变化,发现版本号不一样,就会自动调用onUpgrade函数。

/**
 * Created by Administrator on 2017/3/6 0006.
 */

public class DownLoadDBHelper extends SQLiteOpenHelper {
   
    /**
     * DownLoadDBHelper用于创建数据库,如果不会使用原生的建库的话
     *
     * 跟随小司机我的脚步来一起练一练
     * 建两个表:
     * download_info表存储下载信息
     * localdownload_info表存储本地下载信息
     * 之后对比两个表进行继续下载等等
     */

    public static String DATABASE_NAME = "downloadFILES.db";
    public static String TABLE_DOWNLOAD_INFO = "download_info";
    public static String TABLE_LOCALDOWNLOAD_INFO = "localdownload_info";
    private static int version = 1;


    public DownLoadDBHelper(Context context) {
        super(context, DATABASE_NAME, null, version);
    }


    @Override
    public void onCreate(SQLiteDatabase db) {
        /*在此进行创建数据库和表格,来一起动手写一遍,就是两个sqlite语句*/

        db.execSQL("create table " + TABLE_DOWNLOAD_INFO + "(" + "id integer PRIMARY KEY AUTOINCREMENT," +
                "thread_id integer," + "start_position integer," + "end_position integer," + " completed_size integer," + "url varchar(100))");
        db.execSQL("create table " + TABLE_LOCALDOWNLOAD_INFO + "(" + "id integer PRIMARY KEY AUTOINCREMENT," + "name varchar(50)," +
                "url varchar(100)," + "completedSize integer," + "fileSize integer," + "status integer)");

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        /*数据库更新升级,检测到版本的变化,发现版本号不一样,就会自动调用onUpgrade函数
        * 新版本号和老版本号都会作为onUpgrade函数的参数传进来,便于开发者知道数据库应该从哪个版本升级到哪个版本。
        * */
        String sql = "drop table if exists " + TABLE_DOWNLOAD_INFO + "";
        String sqlOne = "drop table if exists " + TABLE_LOCALDOWNLOAD_INFO + "";
        db.execSQL(sql);
        db.execSQL(sqlOne);
        onCreate(db);//删除数据库,重新创建。这里只是简单的,并没有添加或者减少数据库中的其他字段

    }
}

DAO对数据库进行增删改查

DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此。对数据库的操作,我们基本要用到的就是新增,更新,删除,查询等方法。

/**
 * Created by ShanCanCan on 2017/3/6 0006.
 */

public class Dao {
   
    /*
    * DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此。
    * DAO层所定义的接口里的方法都大同小异,这是由我们在DAO层对数据库访问的操作来决定的,
    * 对数据库的操作,我们基本要用到的就是新增,更新,删除,查询等方法。
    * 因而DAO层里面基本上都应该要涵盖这些方法对应的操作。
    * */
    private static Dao dao;
    private static DownLoadDBHelper dbHelper;
    public static final byte[] Lock = new byte[0]; //新建两个字节作为对象锁
    public static final byte[] file_Lock = new byte[0];

    public Dao() {
  //空构造方法,
    }

    public static synchronized Dao getInstance(Context context) {
  //本demo用单例模式中的懒汉模式+线程不安全  线程安全的代价是效率变低
        if (dao == null) {
            dao = new Dao();
            dbHelper = new DownLoadDBHelper(context);
        }
        return dao;
    }

    /* public static synchronized Dao getInstance(Context context) {//本demo用单例模式中的懒汉模式+线程安全  线程安全的代价是效率变低,99%情况下不需要同步
        if (dao == null) {  //你可以在这两个方法中随便选择一个
            dao = new Dao();
            dbHelper = new DownLoadDBHelper(context);
        }
        return dao;
    }*/

    /***************************************   下方Dao层中对数据库的增、删、改、查   *********************************************************/


    /**
     * 检查本地下载记录,是否下载过
     *
     * @param url
     * @return
     */
    public boolean isExist(String url) {
        SQLiteDatabase database = dbHelper.getReadableDatabase(); //获取本app所创建的数据库
        String sql = "select count(*) from " + TABLE_LOCALDOWNLOAD_INFO + " where url=?"; //查询语句,查询总共有多少条的语句
        Cursor cursor = database.rawQuery(sql, new String[]{url});
        /**
         *
         * @Cursor
         *  Cursor 是每行的集合。
         *  使用 moveToFirst() 定位第一行。
         *  你必须知道每一列的名称。
         *  你必须知道每一列的数据类型。
         *  Cursor 是一个随机的数据源。
         *  所有的数据都是通过下标取得。
         *  Cursor按照我的理解就是一个箭头,指到哪一行就是那一行的集合
         *  比较重要的方法有:close(),moveToFirst(),moveToNext(),moveToLast(),moveToPrevious(),getColumnCount()等。
         *
         * @rawQuery
         * rawQuery是直接使用SQL语句进行查询的,也就是第一个参数字符串,
         * 在字符串内的“?”会被后面的String[]数组逐一对换掉
         * cursor用完之后要关闭,cursor用完之后要关闭,cursor用完之后要关闭。重要的事情说三遍!!!
         *
         * */
        cursor.moveToFirst();
        int count = cursor.getInt(0);
        cursor.close();
        return count > 0;
    }

    /**
     * 是否为首次下载
     *
     * @param url
     * @return
     */
    public boolean isFirstDownload(String url) {
        SQLiteDatabase database = dbHelper.getReadableDatabase();
        String sql = "select count(*) from " + TABLE_DOWNLOAD_INFO + " where url=?";
        Cursor cursor = database.rawQuery(sql, new String[]{url});
        cursor.moveToFirst();
        int count = cursor.getInt(0);
        cursor.close();
        return count == 0;
    }

    /**
     * 保存下载的具体信息 保存所下载的list集合中的数据
     *
     * @param infos
     * @param context
     */
    public void saveInfos(List<DownLoadInfo> infos, Context context) {
        /**
         * 事务(Transaction)是并发控制的单位,是用户定义的一个操作序列。
         * 这些操作要么都做,要么都不做,是一个不可分割的工作单位。
         * 通过事务,SQL Server能将逻辑相关的一组操作绑定在一起,
         * 以便保持数据的完整性。
         *
         * 事务具有四个特征:原子性( Atomicity )、一致性( Consistency )、
         * 隔离性( Isolation )和持续性( Durability )。这四个特性简称为 ACID 特性。
         *
         * */
        synchronized (Lock) {
            SQLiteDatabase database = dbHelper.getWritableDatabase();
            database.beginTransaction();//开启事务
            try {
  //如果有异常,在这里捕获
                for (DownLoadInfo info : infos) {
  //for循环将数据存入数据库
                    String sql = "insert into " + TABLE_DOWNLOAD_INFO + "(thread_id,start_position, end_position, compl
  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值