各位父老乡亲,我单汉三又回来了,今天为大家带来一个用原生的安卓写的下载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