日期:
来源:郭霖收集编辑:码上开炼
https://juejin.cn/user/2551282372718599
NotificationChannel NotificationManager(notify方法) RemoteViews NotificationManagerCompat.cancel(notifyId) UI适配(展开&折叠)
/**
*id:渠道ID,必须唯一,且不超过40个字符
*name:渠道名,用户在设置中看到的,如上图
*importance:重要程度
*/
public NotificationChannel(String id, CharSequence name, @Importance int importance) {
}
---importance参数的可选值---
//在通知栏可见,不会有提示音,不会横幅弹出
public static final int IMPORTANCE_LOW = 2;
//在通知栏可见,有提示音,不会横幅弹出
public static final int IMPORTANCE_DEFAULT = 3;
//在通知栏可见,有提示音,会横幅弹出
public static final int IMPORTANCE_HIGH = 4;
//建议单独写在一个常量类中,防止别人在不了解的情况下随意修改
private const val CHANNEL_ID_DAILY = "channel_id_daily"
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
val dailyChannel: NotificationChannel = NotificationChannel(
CHANNEL_ID_DAILY,
getString(R.string.notify_channel_name_daily),
NotificationManager.IMPORTANCE_DEFAULT
).apply {
//设置一些参数,可以参考API文档自行设置
setShowBadge(false)
enableLights(true)
enableVibration(true)
lightColor = Color.RED
}
notificationManager.createNotificationChannel(dailyChannel)
}
private const val REQUEST_CODE_DAILY = 0x1000
private const val NOTIFY_ID_DAILY = 0x2000
//通过传入channelId将这条通知和上面定义的通知渠道绑定
val notification = NotificationCompat.Builder(this, CHANNEL_ID_DAILY)
.setSmallIcon(R.mipmap.ic_launcher)//必须设置,否则会奔溃
.setLargeIcon(BitmapFactory.decodeResource(this.resources, R.mipmap.ic_launcher))
.setCustomContentView(remoteViews) //折叠后通知显示的布局
.setCustomHeadsUpContentView(remoteViews)//横幅样式显示的布局
.setCustomBigContentView(remoteViews) //展开后通知显示的布局
.setContent(remoteViews) //兼容低版本
.setColor(ContextCompat.getColor(this, R.color.color_367AF6))//小图标的颜色
.setAutoCancel(true) // 允许点击后清除通知
.setPriority(NotificationCompat.PRIORITY_MAX)
.setDefaults(NotificationCompat.DEFAULT_ALL) // 默认配置,包括通知的提示音,震动效果等
.setContentIntent(pendingIntent) //一定要设置,点击整个remoteView就可跳转
notificationManager.notify(NOTIFY_ID_DAILY, notification.build())
val remoteViews = RemoteViews(this.packageName, R.layout.layout_cus_notify)
val funIntent = Intent(this, ArticleActivity::class.java)//演示代码:点击后跳转到ArticleActivity
funIntent.putExtra("key_article_nun", "第一篇")
val pendingIntent: PendingIntent =
PendingIntent.getActivity(
this, REQUEST_CODE_DAILY, funIntent,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
)
remoteViews.setTextViewText(R.id.text_info, "欢迎您查看的我的文章")
remoteViews.setOnClickPendingIntent(R.id.btn_fun, pendingIntent) //为按钮设置点击事件
public class RemoteViews implements Parcelable, Filter
AdapterViewFlipper FrameLayout GridLayout GridView LinearLayout ListView RelativeLayout StackView ViewFlipper
AnalogClock Button Chronometer ImageButton ImageView ProgressBar TextClock TextView
CheckBox RadioButton RadioGroup Switch
Caused by: android.view.InflateException:
Binary XML file line #26 in com.test.notification:layout/layout_cus_notify:
Class not allowed to be inflated android.widget.EditText
setContentIntent(PendingIntent)
setAutoCancel(true)
NotificationUtils.cancel(notifyId)
//点击关闭按钮的相关代码
val closeIntent = Intent(context, CpuUseService::class.java)
closeIntent.putExtra(UpdateConstant.KEY_CLOSE_NOTIFY_ID, notifyId)
val closePendingIntent: PendingIntent =
PendingIntent.getService(
context,
notifyId,
closeIntent,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else PendingIntent.FLAG_UPDATE_CURRENT
)
remoteViews.setOnClickPendingIntent(R.id.btn_close, closePendingIntent)
//Service接收的相关代码
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
val bundle = intent?.extras
val notifyId = bundle?.getInt(UpdateConstant.KEY_CLOSE_NOTIFY_ID)
lifecycleScope.launch(Dispatchers.IO) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
NotificationUtils.cancel(notifyId)
}
}
return START_STICKY
}
RemoteView?.setInt(R.id.ll_root, "setBackgroundColor", ContextCompat.getColor(context, R.color.color_367AF6))
setCustomHeadsUpContentView(mRemoteViews) //设置悬浮通知的布局
setCustomContentView(cusSmallRemoteViews) //设置收起后通知的布局
setCustomBigContentView(cusBigRemoteViews) //设置展开后通知的布局
setContent(cusBigRemoteViews) //兼容低版本,可根据需求设置
//创建通知需要传入NotificationChannel对应的channelId
val notification = NotificationCompat.Builder(context, channelId)
.setCustomContentView(cusSmallRemoteViews) //折叠后通知显示的布局
.setCustomBigContentView(cusBigRemoteViews)//展开后通知显示的布局
.setContent(cusSmallRemoteViews) //兼容低版本,可根据需求设置
...忽略其他需要设置的api
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
notification.setCustomHeadsUpContentView(cusBigRemoteViews)//设置悬浮通知的样式
}else{
//安卓12以上不用设置setCustomHeadsUpContentView
}
notificationManager.notify(notifyId, notification.build())
Fatal Exception: java.lang.RuntimeException
android.os.TransactionTooLargeException: data parcel size 518960 bytes
(ContextCompat.getSystemService(context, NotificationManager::class.java) as NotificationManager).notify(
NotifyConstant.NOTIFY_ID_RESIDENT,
getResidentNotification(context)
)
remoteView?.setTextViewText(R.id.tv_cpu_tem_tip_yellow,"")
remoteView.setViewVisibility(R.id.small_fl_cpu, View.GONE)
//调用setTextViewText后,mActions会+1
private ArrayList<Action> mActions;
private void addAction(Action a) {
if (mActions == null) {
mActions = new ArrayList<>();
}
mActions.add(a);
}
//BaseReflectionAction.java
@Override
public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
ColorResources colorResources) {
final View view = root.findViewById(viewId);
if (view == null) return;
Class<?> param = getParameterType(this.type);
if (param == null) {
throw new ActionException("bad type: " + this.type);
}
Object value = getParameterValue(view);
//调用setText方法
try {
getMethod(view, this.methodName, param, false /* async */).invoke(view, value);
} catch (Throwable ex) {
throw new ActionException(ex);
}
}
private var mActionsSize = 0
private var mRefreshTime = 0
mRefreshTime++
runCatching {
val remoteViewsClass = Class.forName("android.widget.RemoteViews")
val mActionsField: Field = remoteViewsClass.getDeclaredField("mActions")
mActionsField.isAccessible = true
//反射拿到mActions的大小
val d = mActionsField.get(residentRemoteView) as MutableList<*>
mActionsSize = d.size
}
//这里有一个兜底逻辑,如果反射获取mActionsSize失败,就走mRefreshTime的逻辑
//mRefreshTime是指调用RemoteViews API的次数
// 100 和 15是一个粗略值,大佬有更好的建议请在文末留言,谢谢啦~
if (mActionsSize >= 100 || mRefreshTime >= 15) {
mActionsSize = 0
mRefreshTime = 0
residentRemoteView = null //手动将RemoteView置null
residentSmallRemoteView = null
}
if (residentRemoteView == null || residentSmallRemoteView == null) {
initResidentRemoteView(context)
}