下载器封装,便于开发,使用downloadManager。可以监听下载进度,监听方法里使用了协程,也可以使用线程进行监听,同时使用contentProvider来获取全局上下文,所以在使用中无需传递上下文。
此文仅用于本人记录开发经验所用。
```
/**
*@Company NSNTC
*@author Chord.p
*@time 2020/3/20 9:44 AM
*speak softly love
*
* 下载器,暂时无法管理多下载任务状态
*/
class DownloadHelper {
//下载器
private var downloadManager : DownloadManager = DownloadProvider.mContext.getSystemService(Context.DOWNLOAD_SERVICE)as DownloadManager
//下载时相关配置
private var options = DownloadOptions()
//下载开始监听器,回调参数为下载id,可通过该id对该下载任务进行管理
var preparListener : DownloadBaseListener? =null
//下载中监听器,回调参数为下载进度,范围在0~100
var downloadingListener : DownloadBaseListener? =null
//下载失败监听器
var downloadFailListener : DownloadNoteListener? =null
//下载延迟监听器
var downloadPenddingListener : DownloadNoteListener? =null
//下载暂停监听器
var downloadPauseListener : DownloadNoteListener? =null
//下载成功监听器,回调参数为本地存储路径
var downloadSuccessListener : DownloadBaseListener? =null
//下载周期监听器
var downloadListener : DownloadListener? =null
//下载id
var id =0L
//下载地址
var path =""
companion object {
val instance : DownloadHelperby lazy(::DownloadHelper)
fun apkDownload(url : String,name : String,deleteIfExists : Boolean =true) {
instance.downloadApk(url, name, deleteIfExists)
}
fun downloadFile(url : String,name : String,deleteIfExists : Boolean =true) {
instance.download(url, name, deleteIfExists)
}
/**
* 请使用DownloadOptions下的常量进行设定
*/
fun setNetType(type : String) {
instance.options.netType = type
}
fun setDownloadPath(path : String) {
instance.options.downloadPath = File(path)
}
fun setDownloadPath(path : File) {
instance.options.downloadPath = path
}
/**
* 请使用DownloadManager.Request 下的常量进行设定
*/
fun setShowNotify(showType : Int) {
instance.options.showNotify = showType
}
fun setDownloadListener(listener : DownloadListener) {
instance.downloadListener = listener
}
fun setPrepardListener(listener: DownloadBaseListener) {
instance.preparListener = listener
}
fun setNotifyTitle(title: String) {
instance.options.title = title
}
fun setNotifyDesc(desc : String) {
instance.options.desc = desc
}
}
fun downloadApk(url : String,name : String,deleteIfExists : Boolean =true) {
var request = DownloadManager.Request(Uri.parse(url))
request.setAllowedOverRoaming(false)
when(options.netType) {
DOWNLOAD_ONLY_PHONE -> request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE)
DOWNLOAD_ONLY_WIFI -> request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
DOWNLOAD_NO_CARE -> request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE or DownloadManager.Request.NETWORK_WIFI)
}
//设置是否显示通知栏
request.setNotificationVisibility(options.showNotify)
//设置通知栏标题
request.setTitle(options.title)
//设置通知栏简介
request.setDescription(options.desc)
request.setVisibleInDownloadsUi(true)
//指定下载的文件类型为APK
request.setMimeType("application/vnd.android.package-archive")
var file = File(options.downloadPath,name)
//若已存在该文件,则删除
if (file.exists()) {
if (deleteIfExists) {
file.delete()
}else{
Log.e("download","file is exists")
return
}
}
request.setDestinationUri(Uri.fromFile(file))
path = file.absolutePath
id =downloadManager.enqueue(request)
preparListener?.onNotice?.invoke(id)
downloadListener?.onPrepare(id)
//注册广播,接收下载任务广播
DownloadProvider.mContext.registerReceiver(receiver,IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
downloadProgress(id)
}
fun download(url : String,name : String,deleteIfExists : Boolean =true) {
var request = DownloadManager.Request(Uri.parse(url))
request.setAllowedOverRoaming(false)
when(options.netType) {
DOWNLOAD_ONLY_PHONE -> request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE)
DOWNLOAD_ONLY_WIFI -> request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
DOWNLOAD_NO_CARE -> request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE or DownloadManager.Request.NETWORK_WIFI)
}
//设置是否显示通知栏
request.setNotificationVisibility(options.showNotify)
//设置通知栏标题
request.setTitle(options.title)
//设置通知栏简介
request.setDescription(options.desc)
request.setVisibleInDownloadsUi(true)
var file = File(options.downloadPath,name)
//若已存在该文件,则删除
if (file.exists()) {
if (deleteIfExists) {
file.delete()
}else{
Log.e("download","file is exists")
return
}
}
request.setDestinationUri(Uri.fromFile(file))
path = file.absolutePath
id =downloadManager.enqueue(request)
preparListener?.onNotice?.invoke(id)
downloadListener?.onPrepare(id)
//注册广播,接收下载任务广播
DownloadProvider.mContext.registerReceiver(receiver,IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
downloadProgress(id)
}
//开协程用于监听下载进度
fun downloadProgress(id : Long) {
GlobalScope.launch {
var breakFlag =true
var query = DownloadManager.Query()
do {
query.setFilterById(id)
var cursor =downloadManager.query(query)
if (cursor.moveToFirst()) {
var bytes_downloaded = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
var bytes_total = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
var dl_progress = (bytes_downloaded *100 / bytes_total)
withContext(Dispatchers.Main){
downloadingListener?.onNotice?.invoke(dl_progress)
downloadListener?.onDownloading(dl_progress)
}
when {
dl_progress ==100 -> breakFlag =false
cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL -> breakFlag =false
cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_FAILED -> breakFlag =false
}
}
cursor.close()
delay(200)
}while (breakFlag)
}
}
//广播接受者,接收下载任务的广播
private var receiver =object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
var query = DownloadManager.Query()
query.setFilterById(id)
var cursor =downloadManager.query(query)
if (cursor.moveToFirst()) {
when(cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))) {
//正在下载
DownloadManager.STATUS_RUNNING -> {
}
//下载失败
DownloadManager.STATUS_FAILED -> {
downloadFailListener?.onNotice?.invoke()
downloadListener?.onFail()
cursor.close()
DownloadProvider.mContext.unregisterReceiver(this)
}
//下载暂停
DownloadManager.STATUS_PAUSED -> {
downloadPauseListener?.onNotice?.invoke()
downloadListener?.onPaused()
}
//下载成功
DownloadManager.STATUS_SUCCESSFUL -> {
downloadSuccessListener?.onNotice?.invoke(path)
downloadListener?.onSuccess(path)
cursor.close()
DownloadProvider.mContext.unregisterReceiver(this)
}
//下载延迟
DownloadManager.STATUS_PENDING -> {
downloadPenddingListener?.onNotice?.invoke()
downloadListener?.onPending()
}
}
}
}
}
//立即安装app
fun installApk(path : String) {
DownloadProvider.mContext.startActivity(Intent().apply {
this.action = Intent.ACTION_VIEW
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK
this.setDataAndType(Uri.parse(path),"application/vnd.android.package-archive")
})
}
fun installApk(path : Uri) {
DownloadProvider.mContext.startActivity(Intent().apply {
this.action = Intent.ACTION_VIEW
this.flags = Intent.FLAG_ACTIVITY_NEW_TASK
this.setDataAndType(path,"application/vnd.android.package-archive")
})
}
class DownloadBaseListener (onNotice : (value :T) -> Unit) {var onNotice = onNotice }
class DownloadNoteListener(onNotice : () -> Unit) {var onNotice = onNotice }
interface DownloadListener {
open fun onPrepare(id: Long) {}
open fun onDownloading(progress : Int) {}
open fun onFail() {}
open fun onPaused() {}
open fun onSuccess(path : String) {}
open fun onPending() {}
}
//用于存储下载时所需的配置
class DownloadOptions(showNotify : Int = DownloadManager.Request.VISIBILITY_HIDDEN,title : String ="",desc : String ="") {
//是否展示通知栏
var showNotify = showNotify
//通知栏标题
var title = title
//通知栏简介
var desc = desc
//下载环境
var netType =DOWNLOAD_NO_CARE
//下载存储地址
var downloadPath = DownloadProvider.mContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
companion object {
//网络环境
const val DOWNLOAD_ONLY_PHONE ="phone"
const val DOWNLOAD_ONLY_WIFI ="wifi"
const val DOWNLOAD_NO_CARE ="noCare"
}
}
//用于获取全局上下文
class DownloadProvider : ContentProvider() {
companion object {
lateinit var mContext : Context
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}
override fun query(uri: Uri, projection: Array?, selection: String?, selectionArgs: Array?, sortOrder: String?): Cursor? {
return null
}
override fun onCreate(): Boolean {
mContext =context!!
return false
}
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int {
return 0
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int {
return 0
}
override fun getType(uri: Uri): String? {
return null
}
}
}
```