总体的思路:
在下载文件时,将进度写入数据库,同时通知该ContentProvider的观察者更新页面,这个通知过程不要太频繁,我设置了10次,否则页面会灰常卡。
如果异常中断(网络中断或程序被kill)也没有关系,因为数据库中有记录,只要从该文件的断点位置继续下载就可以了。关键在于一切以数据库为准
就可以了。
同时要注意的是:
1、自始至终处理的都是同一个PO对象,没有直接用类的成员变量,因为下次启动线程,成员变量会变化,导致诡异的下载文件不对应。
2、启动线程后,将该线程保存进static类变量 downloaders 里,方便在Service类外进行停止线程。
3、因为有一个或多个下载进度的页面,所以在下载更新数据库的同时,进行了通知观察者的操作,当然也可以有其他的方法,比如,
1)在Activity里添加一个线程时刻扫描数据库等等,不过感觉没有这个好。
2)也有用更新进度的时候,不断发广播的方式,在Activity中注册广播接收者更新页
大家有更好的办法请回复。
红色字体部分为关键,其他都是浮云。。
/**
*
* 由于该Service用于多线程下载文件,需要确保原子性,一直处理一个DownLoadFilePO对象
*
* <p>detailed comment
* @see
* @since 1.0
*/
public class DownLoadService extends Service {
private static final String TAG = "DownLoadFileService";
private String downloadUrl;
private boolean showToast;
public static Map<String, DownLoadThread> downloaders = new HashMap<String, DownLoadThread>();
@Override
public IBinder onBind(Intent arg0) {
return null;
}
@Override
public void onCreate() {
//只执行一次
super.onCreate();
}
@Override
@Deprecated
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
if (intent == null) {
return;
}
downloadUrl = intent.getStringExtra("downloadUrl");
showToast = intent.getBooleanExtra("showToast", false);
DownLoadThread downLoadThread = new DownLoadThread(downloadUrl);
Thread thread = new Thread(downLoadThread);
thread.start();
}
/**
*
* 下载线程
*
* <p>detailed comment
* @see
* @since 1.0
*/
public class DownLoadThread implements Runnable {
String url;
boolean downloading = true;
DownLoadThread(String downloadUrl) {
this.url = downloadUrl;
}
/**
* 终止线程
*/
public void stop() {
this.downloading = false;
}
@Override
public void run() {
//查看是否已经在下载
DownLoadFilePO po = queryByDownLoadUrl(url);
if (po != null) {
if (showToast) {
showToast(getResources().getString(R.string.this_file_is_downloaded));
} else {
downloaders.put(downloadUrl, this);
showToast(getResources().getString(R.string.this_file_begin_download));
download(po);
}
} else {
po = init(url);
initProgress(po);
downloaders.put(downloadUrl, this);
download(po);
}
}
/**
* 下载
*/
private void download(DownLoadFilePO po) {
HttpURLConnection connection = null;
RandomAccessFile randomAccessFile = null;
InputStream is = null;
try {
URL url = new URL(downloadUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
// 设置远程文件的下载范围,格式为Range:bytes x-y;
connection.setRequestProperty("Range",
"bytes=" + po.getCurrent_size() + "-" + po.getTotal_size());
//设置本地文件的下载开始位置
File localFile = new File(po.getFile());
randomAccessFile = new RandomAccessFile(localFile, "rwd");
randomAccessFile.seek(po.getCurrent_size());
is = connection.getInputStream();
byte[] buffer = new byte[1024000];
int length = -1;
int count = 0;
while ((length = is.read(buffer)) != -1) {
randomAccessFile.write(buffer, 0, length);
// 更新数据库中的下载信息
po.setCurrent_size(po.getCurrent_size() + length);
updateProgress(po);
// 用消息将下载信息传给进度条,对进度条进行更新
count++;
if (count % 10 == 0) {
getContentResolver().notifyChange(ConstantUtil.uri, null);//如果改变数据,则通知所有人
Log.v(TAG, po.getName() + "=======下载进度======" + po.getCurrent_size());
}
if (!downloading) {
Log.v(TAG, po.getName() + "=======暂停====" + po.getCurrent_size());
downloaders.remove(po.getDownloadUrl());
return;
}
}
//下载完成
if (po.getCurrent_size() == po.getTotal_size()) {
finishProgress(po);
}
downloaders.remove(po.getDownloadUrl());
} catch (Exception e) {
getContentResolver().notifyChange(ConstantUtil.uri, null);//如果改变数据,则通知所有人
this.stop();
e.printStackTrace();
} finally {
try {
is.close();
randomAccessFile.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 初始化
*/
private DownLoadFilePO init(String downloadUrl) {
DownLoadFilePO po = new DownLoadFilePO();
try {
if (!FileUtil.checkFileDir()) {
return null;
}
URL url = new URL(downloadUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
int fileSize = connection.getContentLength();
//总大小
String fileName = FileUtil.getFileNameByUrl(downloadUrl);
try {
fileName = URLDecoder.decode(fileName, "UTF-8");
} catch (UnsupportedEncodingException e) {
Log.e(TAG, e.getMessage());
}
File localFile = new File(ConstantUtil.downloadFileDir + fileName);
//判断同名文件已经存在,如存在同名的文件,需修改文件名称 sample.mp4 ==>sample(1).mp4
int i = 1;
while (localFile.exists()) {
int index = fileName.lastIndexOf(".");
fileName = fileName.substring(0, index) + "(" + i + ")"
+ fileName.substring(index, fileName.length());
localFile = new File(ConstantUtil.downloadFileDir + fileName);
i++;
}
localFile.createNewFile();
i = 0;
// }
// 本地访问文件
RandomAccessFile accessFile = new RandomAccessFile(localFile, "rwd");
accessFile.setLength(fileSize);
accessFile.close();
connection.disconnect();
po.setName(fileName);
po.setDownloadUrl(downloadUrl);
po.setCurrent_size(0L);
po.setTotal_size(fileSize);
po.setFile(localFile.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
return po;
}
/**
* 向数据库中插入初始化数据
*/
private void initProgress(DownLoadFilePO po) {
ContentValues values = new ContentValues();
values.put(DataBaseUtil.DOWNLOAD_FILE_NAME, po.getName());
values.put(DataBaseUtil.CREATE_FILE_DATE, new Date().toString());
values.put(DataBaseUtil.DOWNLOAD_FILE_TOTAL_SIZE, po.getTotal_size());
values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, 0);
values.put(DataBaseUtil.DOWNLOAD_FINISHED, false);
values.put(DataBaseUtil.DOWNLOAD_URL, po.getDownloadUrl());
values.put(DataBaseUtil.DONWLOAD_FILE_PATH, po.getFile());
this.getContentResolver().insert(ConstantUtil.uri, values); //向ContentProvider插入数据
}
/**
*
* 更新进度
*
*<p>detail comment
*@see
*@since
*
*/
private void updateProgress(DownLoadFilePO po) {
ContentValues values = new ContentValues();
values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, po.getCurrent_size());
this.getContentResolver().update(ConstantUtil.uri, values,
DataBaseUtil.DOWNLOAD_URL + "=?", new String[] { po.getDownloadUrl() });
}
/**
*
* 完成下载
*
*<p>detail comment
*@param currentSize
*@see
*@since
*
*/
private void finishProgress(DownLoadFilePO po) {
ContentValues values = new ContentValues();
values.put(DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE, po.getTotal_size());
values.put(DataBaseUtil.DOWNLOAD_FINISHED, true);
this.getContentResolver().update(ConstantUtil.uri, values,
DataBaseUtil.DOWNLOAD_URL + "=?", new String[] { po.getDownloadUrl() });
getContentResolver().notifyChange(ConstantUtil.uri, null);
}
/**
*
* 查看该url是否在下载
*
*<p>detail comment
*@param url
*@return
*@see
*@since
*
*/
private DownLoadFilePO queryByDownLoadUrl(String url) {
Cursor cursor = this.getContentResolver().query(
ConstantUtil.uri,
new String[] { DataBaseUtil.DOWNLOAD_FILE_NAME,
DataBaseUtil.DOWNLOAD_FILE_CURRENT_SIZE,
DataBaseUtil.DOWNLOAD_FILE_TOTAL_SIZE, DataBaseUtil.DONWLOAD_FILE_PATH,
DataBaseUtil.DOWNLOAD_FINISHED }, DataBaseUtil.DOWNLOAD_URL + "=?",
new String[] { url }, null);
if (cursor.moveToNext()) {
DownLoadFilePO po = new DownLoadFilePO();
for (int i = 0; i < cursor.getCount(); i++) {
cursor.moveToPosition(i);
po.setName(cursor.getString(0));
po.setCurrent_size(cursor.getLong(1));
po.setTotal_size(cursor.getLong(2));
po.setFile(cursor.getString(3));
po.setDownloadUrl(url);
}
return po;
}
return null;
}
private void showToast(final String toast) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
Toast.makeText(DownLoadService.this, toast, Toast.LENGTH_SHORT).show();
}
});
}
}
用Service实现断点下载