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

通过本文你可以学习到:
Demo是在TV上运行的,图片显示的问题不要纠结了。
文件下载的Demo已完成,没时间上传与讲解,今天为您展示并讲解一下,纯原生的东西来下载文件,希望可以帮你理解更多安卓比较基础的问题。
我们的思路:建立一个数据库,两个表,一个用来保存网络数据,一个保存本地下载的进度等等。在点击下载按钮的时候启动DownloadService,进行比对之后下载
先看一下Demo的目录结构:
所有的步骤在代码里有非常详细的讲解,一定要看代码(下面是抽取的几个重要的类讲解)!
数据库的建立与DAO
/**
* 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对数据库进行增删改查
/**
* 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, completed_size, url) values (?,?,?,?,?)";
Object[] bindArgs = {info.getThreadId(), info.getStartPosition(), info.getEndPosition(), info.getCompletedSize(), info.getUrl()};
database.execSQL(sql, bindArgs);
}
database.setTransactionSuccessful();//结束事务
} catch (SQLException e) {
e.printStackTrace();
} finally {
database.endTransaction();//关闭事务
}
}
}
/**
* 得到下载具体信息
*
* @param urlstr
* @return List<DownloadInfo> 一个下载器信息集合器,里面存放了每条线程的下载信息
*/
public List<DownLoadInfo> getInfos(String urlstr) {
List<DownLoadInfo> list = new ArrayList<DownLoadInfo>();
SQLiteDatabase database = dbHelper.getReadableDatabase();
String sql = "select thread_id, start_position, end_position, completed_size, url from " + TABLE_DOWNLOAD_INFO + " where url=?";
Cursor cursor = database.rawQuery(sql, new String[]{urlstr});
while (cursor.moveToNext()) {//通过cursor取到下载器信息,循环遍历,得到下载器集合
DownLoadInfo info = new DownLoadInfo(cursor.getInt(0), cursor.getInt(1), cursor.getInt(2), cursor.getInt(3), cursor.getString(4));
list.add(info);
}
cursor.close();
return list;
}
/**
* 本地下载列表添加记录,添加本地数据库信息,完成度等等
*
* @param fileStatus
**/
public void insertFileStatus(FileStatus fileStatus) {
synchronized (file_Lock) {//异步加开启事务,保证数据的完整性
SQLiteDatabase database = dbHelper.getWritableDatabase();
database.beginTransaction();
try {
String sql = "insert into " + TABLE_LOCALDOWNLOAD_INFO + " (name,url,completedSize,fileSize,status) values(?,?,?,?,?)";
Object[] bindArgs = {fileStatus.getName(), fileStatus.getUrl(), fileStatus.getCompletedSize(), fileStatus.getFileSize(), fileStatus.getStatus()};
database.execSQL(sql, bindArgs);
database.setTransactionSuccessful();
} catch (SQLException e) {
e.printStackTrace();
} finally {
database.endTransaction();
}
}
}
/**
* @param context
* @param compeletedSize
* @param threadId
* @param urlstr 这里是更新数据库,建议在保存一个表格的时候就对另一个表格数据库进行更新
*/
public void updataInfos(int threadId, int compeletedSize, String urlstr, Context context) {
synchronized (Lock) {
String sql = "update " + TABLE_DOWNLOAD_INFO + "set completed_size = ? where thread_id =? and url=?";
String localSql = "update " + TABLE_LOCALDOWNLOAD_INFO + "set completedSize = (select sum(completed_size) from " +
TABLE_DOWNLOAD_INFO + "where url=? group by url ) where url=?";
Object[] bindArgs = {compeletedSize, threadId, urlstr};
Object[] localArgs = {urlstr, urlstr};
SQLiteDatabase database = dbHelper.getWritableDatabase();
database.beginTransaction();
try {
database.execSQL(sql, bindArgs);
database.execSQL(localSql, localArgs);
database.setTransactionSuccessful();
} catch (SQLException e) {
e.printStackTrace();
} finally {
database.endTransaction();
}
}
}
/**
* @param url 更新文件的状态,0为正在下载,1为已经下载完成,2为下载出错
**/
public void updateFileStatus(String url) {
synchronized (file_Lock) {
String sql = "update " + TABLE_LOCALDOWNLOAD_INFO + " set status = ? where url = ?";
Object[] bindArgs = {1, url};
SQLiteDatabase database = dbHelper.getWritableDatabase();
database.beginTransaction();
try {
database.execSQL(sql, bindArgs);
database.setTransactionSuccessful();
} catch (SQLException e) {
e.printStackTrace();
} finally {
database.endTransaction();
}
}
}
/**
* @return List<FileStatus>
* 取出本地下载列表数据,如在重新进入应用时,要重新把进度之类的设置好
**/
public List<FileStatus> getFileStatus() {
List<FileStatus> list = new ArrayList<FileStatus>();
SQLiteDatabase database = dbHelper.getReadableDatabase();
//String sql = "slect * from " + TABLE_LOCALDOWNLOAD_INFO + ""; //不能用,需要哪些条件就在语句中写出哪些条件
String sql = "select name, url, status, completedSize, fileSize from " + TABLE_LOCALDOWNLOAD_INFO + "";
Cursor cursor = database.rawQuery(sql, null);
while (cursor.moveToNext()) {
FileStatus fileState = new FileStatus(cursor.getString(0), cursor.getString(1), cursor.getInt(2), cursor.getInt(3), cursor.getInt(4));
list.add(fileState);
}
cursor.close();
return list;
}
/**
* @param url
* @param completeSize
* @param status 更新文件的下载状态
**/
public void updateFileDownStatus(int completeSize, int status, String url) {
synchronized (file_Lock) {
String sql = "update " + TABLE_LOCALDOWNLOAD_INFO + " set completedSize = ?,status = ? where url = ?";
SQLiteDatabase database = dbHelper.getWritableDatabase();
database.beginTransaction();
try {
Object[] bindArgs = {completeSize, status, url};
database.execSQL(sql, bindArgs);
database.delete(TABLE_DOWNLOAD_INFO, "url = ?", new String[]{url});
database.setTransactionSuccessful();
} catch (SQLException e) {
e.printStackTrace();
} finally {
database.endTransaction();
}
}
}
/**
* @param url 获取文件名称
**/
public String getFileName(String url) {
String result = "";
String sql = "select name from " + TABLE_LOCALDOWNLOAD_INFO + " where url = ?";
SQLiteDatabase database = dbHelper.getReadableDatabase();
Cursor cursor = database.rawQuery(sql, new String[]{url});
if (cursor.moveToNext()) {
result = cursor.getString(0);
}
cursor.close();
return result;
}
/**
* 删除文件之后,要删除下载的数据,一个是用户可以重新下载
* 另一个是表再次添加一条数据的时候不出现错误
*
* @param url
*/
public void deleteFile(String url) {
SQLiteDatabase database = dbHelper.getWritableDatabase();
database.beginTransaction();
try {
database.delete(TABLE_DOWNLOAD_INFO, " url = ?", new String[]{url});
database.delete(TABLE_LOCALDOWNLOAD_INFO, " url = ?", new String[]{url});
database.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
database.endTransaction();
}
}
/**
* 关闭数据库
*
* @close
*/
public void closeDB() {
dbHelper.close();
}
}
DownloadService 主要代码
@SuppressLint("HandlerLeak")
public class DownloadService extends Service
{
public IBinder binder = new MyBinder();
public class MyBinder extends Binder
{
public DownloadService getService()
{
return DownloadService.this;
}
}
@Override
public IBinder onBind(Intent intent)
{
return binder;
}
public static int number = 0;
// 文件保存地址
public final String savePath = "/mnt/sdcard/MultiFileDownload/";
// 存放下载列表的引用
public static List<FileStatus> list = new ArrayList<FileStatus>();
public static Map<String, String> localDownList = new HashMap<String, String>();
// 保存每个文件下载的下载器
public static Map<String, Downloader> downloaders = new HashMap<String, Downloader>();
// 每个下载文件完成的长度
private Map<String, Integer> completeSizes = new HashMap<String, Integer>();
// 每个下载文件的总长度
private Map<String, Integer> fileSizes = new HashMap<String, Integer>();
private Downloader downloader;
private int threadCount = 5;
private Dao dao;
private DownLoadCallback loadCallback;
private FileStatus mFileStatus = null;
private Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
if (msg.what == 1)
{
String url = (String) msg.obj;
int length = msg.arg1;
int completeSize = completeSizes.get(url);
int fileSize = fileSizes.get(url);
completeSize += length;
completeSizes.put(url, completeSize);
synchronized (list)
{
for (int i = 0; i < list.size(); i++)
{
FileStatus fileStatus = list.get(i);
if (fileStatus.getUrl().equals(url))
{
if (completeSize == fileStatus.getFileSize())
{
System.out.println("-----------下载完成:"+fileStatus.getName()+":"+completeSize+"-----"+fileStatus.getFileSize());
list.set(i, new FileStatus(fileStatus.getName(), fileStatus.getUrl(), 1, completeSize, fileStatus.getFileSize()));
dao.updateFileDownStatus(completeSize, 1, url);
}
else
{
list.set(i, new FileStatus(fileStatus.getName(), fileStatus.getUrl(), 0, completeSize, fileStatus.getFileSize()));
}
mFileStatus = list.get(i);
}
}
this.postDelayed(new Runnable()
{
@Override
public void run()
{
if (loadCallback != null && mFileStatus != null)
{
loadCallback.refreshUI(mFileStatus);
}
}
}, 1000);
}
}
}
};
@Override
public void onCreate()
{
super.onCreate();
dao = Dao.getInstance(this);
list = dao.getFileStatus();
for (FileStatus fileStatus : list)
{
localDownList.put(fileStatus.getUrl(), fileStatus.getUrl());
}
Timer timer = new Timer();
timer.schedule(new TimerTask()
{
@Override
public void run()
{
number++;
}
}, 0, 1000);
}
public void download(final Button button, final String url, final String name, final Handler mHandler)
{
if (dao.isExist(url))
{
Toast.makeText(this, "别点了,已经在下载了", Toast.LENGTH_SHORT).show();
return;
}
final String fileName = name + url.substring(url.lastIndexOf("."));
new Thread(new Runnable()
{
@Override
public void run()
{
downloader = downloaders.get(url);
if (downloader == null)
{
downloader = new Downloader(url, savePath, fileName, threadCount, DownloadService.this, handler);
downloaders.put(url, downloader);
}
if (downloader.isDownloading())
return;
LoadInfo loadInfo = downloader.getDownloaderInfors();
if(loadInfo != null)
{
FileStatus fileStatus = new FileStatus(fileName, url, 0, loadInfo.getComplete(), loadInfo.getFileSize());
dao.insertFileStatus(fileStatus);
completeSizes.put(url, loadInfo.getComplete());
fileSizes.put(url, fileStatus.getFileSize());
list.add(fileStatus);
localDownList.put(url, url);
downloader.download();
Message msg = new Message();
msg.what = 1;
msg.obj = button;
mHandler.sendMessage(msg);
}
else
{
Message msg = new Message();
msg.what = 2;
msg.obj = button;
mHandler.sendMessage(msg);
}
}
}).start();
}
//暂停下载
public void Pause(Downloader downloader)
{
downloader.pause();
}
//继续下载
public void reDownload(final Button button, final String url, final String name, final Handler mHandler)
{
new Thread(new Runnable()
{
@Override
public void run()
{
String fileName = dao.getFileName(url);
downloader = downloaders.get(url);
if (downloader == null)
{
downloader = new Downloader(url, savePath, fileName, threadCount, DownloadService.this, handler);
downloaders.put(url, downloader);
}
if (downloader.isDownloading())
return;
LoadInfo loadInfo = downloader.getDownloaderInfors();
if(loadInfo != null && !fileName.equals(""))
{
if(!completeSizes.containsKey(url))
{
completeSizes.put(url, loadInfo.getComplete());
}
if(!fileSizes.containsKey(url))
{
fileSizes.put(url, loadInfo.getFileSize());
}
downloader.download();
Message msg = new Message();
msg.what = 1;
msg.obj = button;
mHandler.sendMessage(msg);
}
else
{
Message msg = new Message();
msg.what = 2;
msg.obj = button;
mHandler.sendMessage(msg);
}
}
}).start();
}
public void delete(final String url)
{
Downloader down = downloaders.get(url);
if(down != null)
{
down.pause();
}
handler.postDelayed(new Runnable()
{
@Override
public void run()
{
dao.deleteFile(url);
for (int i = 0; i < list.size(); i++)
{
FileStatus fileStatus = list.get(i);
if (fileStatus.getUrl().equals(url))
{
list.remove(i);
}
}
localDownList.remove(url);
downloaders.remove(url);
completeSizes.remove(url);
fileSizes.remove(url);
if(loadCallback != null)
{
loadCallback.deleteFile(url);
}
}
}, 1000);
}
public interface DownLoadCallback
{
public void refreshUI(FileStatus fileStatus);
public void deleteFile(String url);
}
public void setLoadCallback(DownLoadCallback loadCallback)
{
this.loadCallback = loadCallback;
}
}
下载工具类DownLoadUtil
/**
* Created by ShanCanCan on 2017/3/7 0007.
*/
public class DownLoadUtil {
/**
* 此类的主要功能
* 1、检查是否下载
* 2、下载文件,文件的下载采用httpurlconnection
*/
private String downPath;// 下载路径
private String savePath;// 保存路径
private String fileName;// 文件名称
private int threadCount;// 线程数
private Handler mHandler;
private Dao dao;
private Context context;
private int fileSize;// 文件大小
private int range;
private List<DownLoadInfo> infos;// 存放下载信息类的集合
private int state = INIT;
private static final int INIT = 1;// 定义三种下载的状态:初始化状态,正在下载状态,暂停状态
private static final int DOWNLOADING = 2;
private static final int PAUSE = 3;
/**
* 构造方法,获取dao的对象
*
* @param downPath
* @param savePath
* @param fileName
* @param threadCount
* @param context
* @param mHandler
*/
public DownLoadUtil(String downPath, String savePath, String fileName, int threadCount, Handler mHandler, Context context) {
this.downPath = downPath;
this.savePath = savePath;
this.fileName = fileName;
this.threadCount = threadCount;
this.mHandler = mHandler;
this.context = context;
dao = Dao.getInstance(context);
}
/**
* 判断是否PAUSE
**/
public boolean isPause() {
return state == PAUSE;
}
/**
* 判断是否DOWNLOADING
*/
public boolean isDownloading() {
return state == DOWNLOADING;
}
/**
* @param url 判断是否是第一次下载,利用dao查询数据库中是否有下载这个地址的记录
*/
private boolean isFirst(String url) {
return dao.isFirstDownload(url);
}
/**
* 获取要下载的东西
*/
public LoadItemInfo getDownloadInfos() {
if (isFirst(downPath)) {
if (initFirst()) {//如果是第一次下载的话,要进行初始化,1.获得下载文件的长度 2.创建文件,设置文件的大小
range = this.fileSize / this.threadCount;
infos = new ArrayList<DownLoadInfo>();
//这里就是启动多线程下载,看出来了吗?配合RandomAccessFile。每一个DownLoadInfo就是RandomAccessFile文件的一部分
for (int i = 0; i < this.threadCount - 1; i++) {
DownLoadInfo info = new DownLoadInfo(i, i * range, (i + 1) * range - 1, 0, downPath);
infos.add(info);
}
DownLoadInfo info = new DownLoadInfo(this.threadCount - 1, (this.threadCount - 1) * range, this.fileSize, 0, downPath);
infos.add(info);
dao.saveInfos(infos, this.context);
//(String urlDownload, int completePercent, int fileSize)
LoadItemInfo loadInfo = new LoadItemInfo(this.downPath, 0, this.fileSize);
return loadInfo;
} else {
return null;
}
} else {
//不是第一次下载,我们应该怎么做呢?从数据库里面取回来
infos = dao.getInfos(this.downPath);
if (infos != null && infos.size() > 0) {
int size = 0;
int completeSize = 0;
for (DownLoadInfo info : infos) {
completeSize += info.getCompletedSize();
size += info.getEndPosition() - info.getStartPosition() + this.threadCount - 1;
}
LoadItemInfo loadInfo = new LoadItemInfo(this.downPath, completeSize, size);
return loadInfo;
} else {
return null;
}
}
}
// 设置暂停
public void pause() {
state = PAUSE;
}
// 重置下载状态,将下载状态设置为init初始化状态
public void reset() {
state = INIT;
}
/**
* 基本上,RandomAccessFile的工作方式是,把DataInputStream和DataOutputStream结合起来,再加上它自己的一些方法,
* 比如定位用的getFilePointer( ),在文件里移动用的seek( ),以及判断文件大小的length( )、skipBytes()跳过多少字节数。
* 此外,它的构造函数还要一个表示以只读方式("r"),还是以读写方式("rw")打开文件的参数 (和C的fopen( )一模一样)。它不支持只写文件。
*/
private boolean initFirst() {
boolean result = true;
HttpURLConnection conn = null;
RandomAccessFile randomFile = null;
URL url = null;
try {
url = new URL(downPath);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
// 如果http返回的代码是200或者206则为连接成功
if (conn.getResponseCode() == 200 || conn.getResponseCode() == 206) //状态码(206),表示服务器已经执行完部分对资源的GET请求
{
fileSize = conn.getContentLength();// 得到文件的大小
if (fileSize <= 0) {
//("网络故障,无法获取文件大小");
return false;
}
File dir = new File(savePath);
// 如果文件目录不存在,则创建
if (!dir.exists()) {
if (dir.mkdirs()) {
//("mkdirs success.");
}
}
File file = new File(this.savePath, this.fileName);
randomFile = new RandomAccessFile(file, "rwd");
randomFile.setLength(fileSize);// 设置保存文件的大小
randomFile.close();
conn.disconnect();
}
} catch (Exception e) {
e.printStackTrace();
result = false;
} finally {
if (randomFile != null) {
try {
randomFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (conn != null) {
conn.disconnect();
}
}
return result;
}
/**
* 下面的这个方法就是开启多线程进行下载了数据了
*/
public void downLoad() {
if (infos != null) {
if (state == DOWNLOADING) {
return;
}
state = DOWNLOADING;// 把状态设置为正在下载
for (DownLoadInfo info : infos) {//为什么说我们是多线程呢?因为我们分别用新线程去下载刚才分割好的一个RandomAccessFile文件
new DownLoadThread(info.getThreadId(), info.getStartPosition(), info.getEndPosition(), info.getCompletedSize(), info.getUrl(), this.context).start();
}
}
}
/**
* 现在要创建线程用来下载了,这里采用内部类
*/
public class DownLoadThread extends Thread {
private int threadId;
private int startPostion;
private int endPostion;
private int compeletedSize;
private String url;
private Context context;
public static final int PROGRESS = 1;
public DownLoadThread(int threadId, int startPostion, int endPostion, int compeletedSize, String url, Context context) {//构造方法,传入特定的参数
this.threadId = threadId;
this.startPostion = startPostion;
this.endPostion = endPostion;
this.compeletedSize = compeletedSize;
this.url = url;
this.context = context;
}
//开始下载
@Override
public void run() {
HttpURLConnection conn = null;
RandomAccessFile randomAccessFile = null;
InputStream inStream = null;
File file = new File(savePath, fileName);
URL url = null;
try {
url = new URL(this.url);
conn = (HttpURLConnection) url.openConnection();
constructConnection(conn);
if (conn.getResponseCode() == 200 || conn.getResponseCode() == 206) {
randomAccessFile = new RandomAccessFile(file, "rwd");
randomAccessFile.seek(this.startPostion + this.compeletedSize);//RandomAccessFile移动指针,到需要下载的块
inStream = conn.getInputStream();
byte buffer[] = new byte[4096];//这个4096为么子呢?我也不知道,就是看阿里的人下载apk的时候都用4096,我也用
int length = 0;
while ((length = inStream.read(buffer, 0, buffer.length)) != -1) {
randomAccessFile.write(buffer, 0, length);
compeletedSize += length;
// 更新数据库中的下载信息
dao.updataInfos(threadId, compeletedSize, this.url, this.context);
// 用消息将下载信息传给进度条,对进度条进行更新
Message message = Message.obtain();
message.what = PROGRESS;
message.obj = this.url;
message.arg1 = length;
mHandler.sendMessage(message);// 给DownloadService发送消息
if (state == PAUSE) {
//("-----pause-----");
return;
}
}
// ("------------线程:" + this.threadId + "下载完成");
}
} catch (IOException e) {
e.printStackTrace();
//("-----下载异常-----"); 这里下载异常我就不处理了,你可以发一条重新下载的消息
} finally {//用完只后流要关闭,不然容易造成资源抢占,内存泄漏
try {
if (inStream != null) {
inStream.close();
}
if (randomAccessFile != null) {
randomAccessFile.close();
}
if (conn != null) {
conn.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 构建请求连接时的参数 返回开始下载的位置
*
* @param conn
*/
private void constructConnection(HttpURLConnection conn) throws IOException {
conn.setConnectTimeout(5 * 1000);// 设置连接超时5秒
conn.setRequestMethod("GET");// GET方式提交,如果你是用post请求必须添加 conn.setDoOutput(true); conn.setDoInput(true);
conn.setRequestProperty(
"Accept",
"image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
conn.setRequestProperty("Accept-Language", "zh-CN");
conn.setRequestProperty("Referer", this.url);
conn.setRequestProperty("Charset", "UTF-8");
int startPositionNew = this.startPostion + this.compeletedSize;
// 设置获取实体数据的范围
conn.setRequestProperty("Range", "bytes=" + startPositionNew + "-" + this.endPostion);
conn.setRequestProperty(
"User-Agent",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.connect();
}
}
}
Github地址:https://github.com/Shanlovana/DownLoadFiles/
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# android实现断点下载
# android
# 断点下载
# android多线程下载文件
# Android 断点下载和自动安装的示例代码
# android多线程断点下载-带进度条和百分比进度显示效果
# Android HttpURLConnection断点下载(单线程)
# 详解Android中的多线程断点下载
# Android入门:多线程断点下载详细介绍
# Android使用多线程实现断点下载
# Android实现断点下载的方法
# Android实现多线程断点下载的方法
# Android实现断点多线程下载
# 本地下载
# 多线程
# 你可以
# 用完
# 是一个
# 下载器
# 数据库中
# 在此
# 判断是否
# 下载完成
# 你必须
# 设置为
# 自己的
# 重新下载
# 的人
# 都是
# 进阶
# 这是
# 进度条
# 几个
相关文章:
济南网站建设制作公司,室内设计网站一般都有哪些功能?
建站VPS选购需注意哪些关键参数?
如何登录建站主机?访问步骤全解析
商务网站制作工程师,从哪几个方面把握电子商务网站主页和页面的特色设计?
建站之星如何快速解决建站难题?
如何自定义建站之星网站的导航菜单样式?
建站之星如何实现网站加密操作?
网站制作公司排行榜,四大门户网站排名?
如何通过虚拟机搭建网站?详细步骤解析
如何选择服务器才能高效搭建专属网站?
如何通过智能用户系统一键生成高效建站方案?
洛阳网站制作公司有哪些,洛阳的招聘网站都有哪些?
建站中国官网:模板定制+SEO优化+建站流程一站式指南
如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?
微信推文制作网站有哪些,怎么做微信推文,急?
宝塔建站后网页无法访问如何解决?
临沂网站制作企业,临沂第三中学官方网站?
武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?
建站主机CVM配置优化、SEO策略与性能提升指南
番禺网站制作公司哪家值得合作,番禺图书馆新馆开放了吗?
建站之星伪静态规则如何正确配置?
在线教育网站制作平台,山西立德教育官网?
黑客入侵网站服务器的常见手法有哪些?
公司网站制作需要多少钱,找人做公司网站需要多少钱?
建站三合一如何选?哪家性价比更高?
建站之星ASP如何实现CMS高效搭建与安全管理?
详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)
微网站制作教程,我微信里的网站怎么才能复制到浏览器里?
企业宣传片制作网站有哪些,传媒公司怎么找企业宣传片项目?
建站上市公司网站建设方案与SEO优化服务定制指南
如何挑选优质建站一级代理提升网站排名?
如何选择高效响应式自助建站源码系统?
PHP 500报错的快速解决方法
免费ppt制作网站,有没有值得推荐的免费PPT网站?
SQL查询语句优化的实用方法总结
如何在IIS中新建站点并配置端口与IP地址?
如何选择高性价比服务器搭建个人网站?
大连网站设计制作招聘信息,大连投诉网站有哪些?
香港服务器如何优化才能显著提升网站加载速度?
建站ABC备案流程中有哪些关键注意事项?
广州网站设计制作一条龙,广州巨网网络科技有限公司是干什么的?
北京企业网站设计制作公司,北京铁路集团官方网站?
一键网站制作软件,义乌购一件代发流程?
如何获取上海专业网站定制建站电话?
建站之星如何快速更换网站模板?
c++怎么编写动态链接库dll_c++ __declspec(dllexport)导出与调用【方法】
香港代理服务器配置指南:高匿IP选择、跨境加速与SEO优化技巧
建站之星如何修改网站生成路径?
如何用免费手机建站系统零基础打造专业网站?
如何用已有域名快速搭建网站?
*请认真填写需求信息,我们会在24小时内与您取得联系。