![](https://img.haomeiwen.com/i1459597/f9ec1aad361600ed.jpg)
参考样式
1.APP通知栏代码
package com.aimymusic.android.widget.update
import android.app.*
import android.content.Context
import android.content.Intent
import android.os.Build
import android.support.v4.app.NotificationCompat
import android.text.format.Formatter
import android.widget.RemoteViews
import com.aimymusic.android.R
import com.aimymusic.android.constants.IntConstants
import com.aimymusic.android.constants.StringConstants
import com.aimymusic.android.widget.file.AppFileDirManager
import com.aimymusic.android.widget.filedownload.AimyFileDownload
import com.aimymusic.browse.download.MD5Utils
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.Utils
import org.jetbrains.anko.runOnUiThread
import java.util.Locale
/**
* Description:
* @author: caiyoufei
* @date: 19-5-30 下午12:06
*/
class AimyUpdateService : IntentService("UpdateService") {
companion object {
const val DOWNLOAD_PATH = "download_path"
const val DOWNLOAD_VERSION_NAME = "download_version_name"
const val DOWNLOAD_SHOW_NOTIFICATION = "download_show_notification"
fun startIntent(
path: String,
version_name: String = "",
showNotification: Boolean = false
) {
val intent = Intent(Utils.getApp(), AimyUpdateService::class.java)
intent.putExtra(DOWNLOAD_PATH, path)
intent.putExtra(DOWNLOAD_VERSION_NAME, version_name)
intent.putExtra(DOWNLOAD_SHOW_NOTIFICATION, showNotification)
Utils.getApp()
.startService(intent)
}
}
//下载的百分比
private var mPercent = 0f
//通知管理
private var mNotificationManager: NotificationManager? = null
//通知创建类
private var mBuilder: NotificationCompat.Builder? = null
//上次的下载量(方便计算下载速度)
private var mLastBytes: Long = 0
//上次的时间
private var mLastTime: Long = 0
//下载速度
private var mSpeed: Long = 0
//通知栏数据设置
private var mRemoteViews: RemoteViews? = null
//显示下载更新的版本名称
private var mVerName: String? = null
//文件下载保存的文件夹
private val mFileDir = AppFileDirManager.getDownloadFileDir(Utils.getApp())
//是否显示通知栏
private var needShowNotification = false
//总大小
private var mTtotalSize = 0L
//apk下载地址
private var mApkUrl = ""
//apk下载的版本
private var mApkVersion = ""
//app名称
private val appName = AppUtils.getAppName()
//是否正在下载
private var isDownloading = false
//渠道id 安卓8.0 https://blog.csdn.net/MakerCloud/article/details/82079498
private val UPDATE_CHANNEL_ID = AppUtils.getAppPackageName() + ".update.channel.id"
private val UPDATE_CHANNEL_NAME = AppUtils.getAppPackageName() + ".update.channel.name"
override fun onHandleIntent(intent: Intent?) {
if (mNotificationManager == null) {
mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
UPDATE_CHANNEL_ID, UPDATE_CHANNEL_NAME, NotificationManager.IMPORTANCE_LOW//等级太高会一直响
)
channel.setSound(null, null)
mNotificationManager?.createNotificationChannel(channel)
}
}
if (isDownloading) return
intent?.let {
isDownloading = true
mApkUrl = it.getStringExtra(DOWNLOAD_PATH)
val downloadUrl: String = mApkUrl
mApkVersion = it.getStringExtra(DOWNLOAD_VERSION_NAME)
val versionName = mApkVersion
needShowNotification = it.getBooleanExtra(DOWNLOAD_SHOW_NOTIFICATION, false)
val downLoadName = MD5Utils.MD5(downloadUrl)
mVerName = if (versionName.isEmpty() || versionName == "最新版本") "最新版本" else versionName
AimyFileDownload.downloadFile(
url = downloadUrl,
path = mFileDir,
isReDownloadIfCompleted = false,
fileName = downLoadName,
start = { showNotification() },
progress = { _, offsetSize, totalSize -> updateProgress(offsetSize, totalSize) },
success = { _, file ->
isDownloading = false
file?.let { f ->
showSuccess(f.path)
AppUtils.installApp(f.path)
}
},
error = { url, _ ->
isDownloading = false
showFail(url)
}
)
}
}
//显示下载通知
private fun showNotification() {
//发送进度
runOnUiThread { UpdateLiveData.setProgress(0f) }
if (needShowNotification) {
initRemoteViews()
mRemoteViews?.let {
it.setTextViewText(R.id.notice_update_title, "正在下载$appName$mVerName")
it.setTextViewText(R.id.notice_update_percent, "0%")
it.setProgressBar(R.id.notice_update_progress, 100, 0, false)
it.setTextViewText(R.id.notice_update_speed, "")
it.setTextViewText(R.id.notice_update_size, "")
initBuilder(it)
mNotificationManager?.notify(IntConstants.NOTIFICATION.NOTICE_UPDATE_ID, mBuilder?.build())
}
}
}
//每次刷新的时间间隔
private val interval = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) 500 else 250
//更新下载进度
private fun updateProgress(
offsetSize: Long,
totalSize: Long
) {
mTtotalSize = totalSize
mPercent = offsetSize * 100f / totalSize
//发送进度
runOnUiThread { UpdateLiveData.setProgress(Math.min(99.9f, mPercent)) }
if (mLastTime == 0L || mLastBytes == 0L) {
mSpeed = offsetSize / 1000
mLastTime = System.currentTimeMillis()
mLastBytes = offsetSize
} else if (System.currentTimeMillis() - mLastTime >= interval || offsetSize > totalSize) {
mSpeed = (offsetSize - mLastBytes) / (System.currentTimeMillis() - mLastTime) * 1000
mLastTime = System.currentTimeMillis()
mLastBytes = offsetSize
} else {
return
}
if (needShowNotification) {
mRemoteViews?.let {
it.setProgressBar(R.id.notice_update_progress, 100, mPercent.toInt(), false)
val progress = String.format(Locale.getDefault(), "%.1f", mPercent) + "%"
it.setTextViewText(R.id.notice_update_percent, progress)
val speedStr = Formatter.formatFileSize(Utils.getApp(), mSpeed) + "/s"
it.setTextViewText(R.id.notice_update_speed, speedStr)
val curSizeStr = Formatter.formatFileSize(Utils.getApp(), offsetSize)
val totalSizeStr = Formatter.formatFileSize(Utils.getApp(), totalSize)
it.setTextViewText(R.id.notice_update_size, "$curSizeStr/$totalSizeStr")
mNotificationManager?.notify(IntConstants.NOTIFICATION.NOTICE_UPDATE_ID, mBuilder?.build())
}
}
}
//下载成功
private fun showSuccess(filePath: String) {
//发送进度
runOnUiThread { UpdateLiveData.setProgress(100f) }
if (needShowNotification) {
initRemoteViews()
mRemoteViews?.let {
if (mTtotalSize == 0L) mTtotalSize = File(filePath).length()
it.setTextViewText(R.id.notice_update_title, "$appName${mVerName}下载完成,点击安装")
it.setProgressBar(R.id.notice_update_progress, 100, 100, false)
it.setTextViewText(R.id.notice_update_percent, "100%")
val totalSizeStr = Formatter.formatFileSize(Utils.getApp(), mTtotalSize)
it.setTextViewText(R.id.notice_update_size, "$totalSizeStr/$totalSizeStr")
val intentInstall = Intent(Utils.getApp(), NotificationBroadcastReceiver::class.java)
intentInstall.action = StringConstants.Update.INTENT_KEY_INSTALL_APP
intentInstall.putExtra(StringConstants.Update.INTENT_KEY_INSTALL_PATH, filePath)
it.setOnClickPendingIntent(
R.id.notice_update_layout,
PendingIntent.getBroadcast(
Utils.getApp(), 0, intentInstall,
PendingIntent.FLAG_CANCEL_CURRENT
)
)
initBuilder(it)
mBuilder?.setOngoing(false)
mNotificationManager?.notify(IntConstants.NOTIFICATION.NOTICE_UPDATE_ID, mBuilder?.build())
}
}
}
//更新失败
private fun showFail(path: String) {
//发送进度
runOnUiThread { UpdateLiveData.setProgress(-1f) }
if (needShowNotification) {
initRemoteViews()
mRemoteViews?.let {
it.setTextViewText(R.id.notice_update_title, "$appName${mVerName}下载失败,点击重试")
val intentInstall = Intent(Utils.getApp(), NotificationBroadcastReceiver::class.java)
intentInstall.action = StringConstants.Update.INTENT_KEY_APK_DOWNLOAD_ERROR
intentInstall.putExtra(StringConstants.Update.INTENT_KEY_RETRY_PATH, mApkUrl)
intentInstall.putExtra(StringConstants.Update.INTENT_KEY_RETRY_NAME, mApkVersion)
it.setOnClickPendingIntent(
R.id.notice_update_layout,
PendingIntent.getBroadcast(
Utils.getApp(), 0, intentInstall,
PendingIntent.FLAG_CANCEL_CURRENT
)
)
initBuilder(it)
mBuilder?.setOngoing(false)
mNotificationManager?.notify(IntConstants.NOTIFICATION.NOTICE_UPDATE_ID, mBuilder?.build())
}
}
}
//初始化RemoteViews
private fun initRemoteViews() {
if (mRemoteViews == null) { //防止直接走失败
mRemoteViews = RemoteViews(packageName, R.layout.notification_update)
mRemoteViews?.setImageViewResource(R.id.notice_update_icon, R.mipmap.ic_launcher)
}
}
//初始化Builder
private fun initBuilder(remoteViews: RemoteViews) {
if (mBuilder == null) {
mBuilder = NotificationCompat.Builder(Utils.getApp(), UPDATE_CHANNEL_ID)
.setWhen(System.currentTimeMillis())
.setDefaults(Notification.FLAG_AUTO_CANCEL)
.setSound(null)
.setOngoing(true)
.setAutoCancel(true)
.setContent(remoteViews)
.setSmallIcon(R.drawable.ic_notification)
}
}
}
2.notification_update.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
>
<FrameLayout
android:id="@+id/notice_update_layout"
android:layout_width="match_parent"
android:layout_gravity="center_vertical"
android:layout_height="64dp"
>
<!--APP图标-->
<ImageView
android:id="@+id/notice_update_icon"
android:layout_width="64dp"
android:layout_height="64dp"
android:contentDescription="@null"
android:src="@mipmap/ic_launcher"
/>
<!--下载进度ProgressBar-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="6dp"
android:layout_marginStart="70dp"
android:background="@drawable/shape_notification"
>
<ProgressBar
android:id="@+id/notice_update_progress"
android:layout_width="match_parent"
android:layout_height="5dp"
android:progress="70"
android:progressDrawable="@drawable/progressbar_update"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
/>
</LinearLayout>
<!--下载提示-->
<TextView
android:id="@+id/notice_update_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="40dp"
android:layout_marginStart="70dp"
android:layout_marginTop="5dp"
android:includeFontPadding="false"
android:singleLine="true"
android:textColor="#000"
android:textSize="12sp"
tools:text="正在下载咪哒音乐:4.0.0"
/>
<!--下载百分比-->
<TextView
android:id="@+id/notice_update_percent"
android:layout_width="34dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginEnd="6dp"
android:layout_marginTop="5dp"
android:gravity="end"
android:includeFontPadding="false"
android:singleLine="true"
android:textColor="#000"
android:textSize="12sp"
tools:text="70%"
/>
<!--下载速度-->
<TextView
android:id="@+id/notice_update_speed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginBottom="5dp"
android:layout_marginEnd="120dp"
android:layout_marginStart="70dp"
android:gravity="start"
android:includeFontPadding="false"
android:minWidth="38dp"
android:textColor="#000"
android:textSize="12sp"
tools:text="下载速度:876K/S"
/>
<!--下载的大小/总大小-->
<TextView
android:id="@+id/notice_update_size"
android:layout_width="114dp"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="5dp"
android:layout_marginEnd="6dp"
android:layout_marginStart="70dp"
android:gravity="end"
android:includeFontPadding="false"
android:minWidth="38dp"
android:textColor="#000"
android:textSize="12sp"
tools:text="999.8K/46.5M"
/>
</FrameLayout>
</FrameLayout>
3.progressbar_update.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="5dip"/>
<solid android:color="@color/white_30"/>
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="5dip"/>
<solid android:color="#30D18B"/>
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="5dip"/>
<solid android:color="#30D18B"/>
</shape>
</clip>
</item>
</layer-list>
4.NotificationBroadcastReceiver
package com.aimymusic.android.widget.update
import android.app.NotificationManager
import android.content.*
import android.widget.Toast
import com.aimymusic.ambase.widget.toast.AmToast
import com.aimymusic.android.constants.IntConstants
import com.aimymusic.android.constants.StringConstants
import com.blankj.utilcode.util.AppUtils
import com.blankj.utilcode.util.NetworkUtils
import java.lang.reflect.Method
/**
* description:
*
* @author : case
* @date : 2019/5/30 16:02 创建
*/
class NotificationBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) {
intent.action?.let {
when (it) {
//安装
StringConstants.Update.INTENT_KEY_INSTALL_APP -> {
collapsingNotification(context)
(context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
.cancel(IntConstants.NOTIFICATION.NOTICE_UPDATE_ID)
val path = intent.getStringExtra(StringConstants.Update.INTENT_KEY_INSTALL_PATH)
AppUtils.installApp(path)
}
//重试
StringConstants.Update.INTENT_KEY_APK_DOWNLOAD_ERROR -> if (checkNetWork(context)) {
val apkUrl = intent.getStringExtra(StringConstants.Update.INTENT_KEY_RETRY_PATH)
val mApkVersion = intent.getStringExtra(StringConstants.Update.INTENT_KEY_RETRY_NAME)
AimyUpdateService.startIntent(apkUrl, mApkVersion, true)
}
}
}
}
//检查网络是否可用(点击的时候调用)
private fun checkNetWork(context: Context): Boolean {
val connected = NetworkUtils.isConnected()
if (!connected) {
AmToast.showBottomToast(context, "当前网络不可用", Toast.LENGTH_SHORT)
}
return connected
}
/**
* 折叠通知栏
*/
private fun collapsingNotification(context: Context) {
try {
val service = context.getSystemService("statusbar") ?: return
val clazz = Class.forName("android.app.StatusBarManager")
val sdkVersion = android.os.Build.VERSION.SDK_INT
val collapse: Method
if (sdkVersion <= 16) {
collapse = clazz.getMethod("collapse")
} else {
collapse = clazz.getMethod("collapsePanels")
}
collapse.isAccessible = true
collapse.invoke(service)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
5.UpdateLiveData
package com.aimymusic.android.widget.update
import android.arch.lifecycle.*
/**
* Description:
* @author: caiyoufei
* @date: 19-5-30 下午3:16
*/
object UpdateLiveData {
//下载中1-99 下载成功100 下载失败-1
private var updateProgress = MutableLiveData<Float>()
fun setProgress(progress: Float) {
updateProgress.value = progress
}
fun getProgress(): Float {
return updateProgress.value ?: -1f
}
fun subscribeProgress(
owner: LifecycleOwner,
observer: Observer<Float>
) {
unSubscribeProgress(owner)
updateProgress.observe(owner, observer)
}
fun unSubscribeProgress(owner: LifecycleOwner) {
updateProgress.removeObservers(owner)
}
}
网友评论