这是我参加「日新计划 2 月更文挑战」的第 2 天
自定义告诉系列文章包含:
- 自定义告诉的根底运用、自定义告诉样式的UI适配(展开&折叠)
- bug修正,包含TransactionTooLargeException、ANR
本文是第二篇,记录了TTL问题以及处理思路和计划,值得一看~
TransactionTooLargeException
报错如下
Fatal Exception: java.lang.RuntimeException
android.os.TransactionTooLargeException: data parcel size 518960 bytes
调用notify
办法报错,就一行代码
(ContextCompat.getSystemService(context, NotificationManager::class.java) as NotificationManager).notify(
NotifyConstant.NOTIFY_ID_RESIDENT,
getResidentNotification(context)
)
第一次测验:
看getResidentNotification
办法中有没有大图传输或许intent传输值,唯一和图片相关的便是
setLargeIcon
和setSmallIcon
,测验将图片压缩到200K以下,线上仍然报错,推测和图片无关
第2次测验:
问题非必现的,刚上线没有这个问题,时间越往后问题占比越高,结合项目的特点:告诉一直运行在后台且每隔5分钟改写一次界面,考虑和时间的积累有关,测验修改改写频率为5ms(之前是5min),进行极限测试,问题复现,初步判断和更新UI有关。改写UI调用的代码如下,考虑和RemoteViews
有关,且是跨进程通信呈现的传输数据过大,具体检查更新UI的代码
remoteView?.setTextViewText(R.id.tv_cpu_tem_tip_yellow,"")
remoteView.setViewVisibility(R.id.small_fl_cpu, View.GONE)
体系并没有经过Binder去直接支撑View的跨进程拜访,而是提供了一个Action的概念,Action代表一个View操作,Action同样实现了Parcelable接口。体系首先将View操作封装到Action目标,界面上的控件每更新一次,mActions的长度就会加1,调用NotificationManager.notify()就会遍历一切的Action,履行Action的apply办法,经过反射调用TextView的相关办法。
(图片引用自安卓开发艺术探究)
//调用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);
}
}
考虑跨进程通信时,mActions目标过大,导致抛出TTL,处理:mActions
的巨细超越必定约束就重新初始化RemoteView
,伪代码如下
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)
}
告诉的覆盖问题(探究性问题)
需求:
当用户卸载运用时,更晚的弹出问题,这样用户最终看到的便是我们的app,下拉告诉栏,我们的运用就会显现在第一个。
思路:
1.提高告诉的优先级,现在项目代码里已经是PRIORITY_MAX
2.运用window方式显现在屏幕中间,更具有干扰性
3.测验在展现告诉之前开个delay 10秒,log确实是有延迟了10秒,但是在告诉栏里边,那条告诉并没有置顶显现,此计划不可行
参考链接
www.jianshu.com/p/16120f7ea…
www.jianshu.com/p/fc237e90c…
How to update Notification with RemoteViews?