这是我参加「日新计划 2 月更文挑战」的第 2 天

自定义告诉系列文章包含:

  1. 自定义告诉的根底运用、自定义告诉样式的UI适配(展开&折叠)
  2. bug修正,包含TransactionTooLargeException、ANR

本文是第二篇,记录了TTL问题以及处理思路和计划,值得一看~

TransactionTooLargeException

报错如下

Fatal Exception: java.lang.RuntimeException
android.os.TransactionTooLargeException: data parcel size 518960 bytes

Android 自定义通知的TransactionTooLargeException问题

调用notify办法报错,就一行代码

(ContextCompat.getSystemService(context, NotificationManager::class.java) as NotificationManager).notify(
    NotifyConstant.NOTIFY_ID_RESIDENT,
    getResidentNotification(context)
)

第一次测验:

getResidentNotification办法中有没有大图传输或许intent传输值,唯一和图片相关的便是 setLargeIconsetSmallIcon,测验将图片压缩到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的相关办法。

Android 自定义通知的TransactionTooLargeException问题

(图片引用自安卓开发艺术探究)

//调用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)
}

告诉的覆盖问题(探究性问题)

Android 自定义通知的TransactionTooLargeException问题

需求:

当用户卸载运用时,更晚的弹出问题,这样用户最终看到的便是我们的app,下拉告诉栏,我们的运用就会显现在第一个。

思路:

1.提高告诉的优先级,现在项目代码里已经是PRIORITY_MAX

2.运用window方式显现在屏幕中间,更具有干扰性

Android 自定义通知的TransactionTooLargeException问题

3.测验在展现告诉之前开个delay 10秒,log确实是有延迟了10秒,但是在告诉栏里边,那条告诉并没有置顶显现,此计划不可行

Android 自定义通知的TransactionTooLargeException问题

Android 自定义通知的TransactionTooLargeException问题

参考链接

www.jianshu.com/p/16120f7ea…

www.jianshu.com/p/fc237e90c…

How to update Notification with RemoteViews?