Android SpanStringBuilder ClickableSpan内存泄漏和crash的解决方案

这个问题属于老生常谈,使用SpanStringBuilder的ClickableSpan,有如下因素导致各种问题:

  1. google对于ClickableSpan,内部TextView创建了很多内部类和变量去持有XXXSpan会导致内存泄漏, google原生代码有缺陷;
  2. 过去很多年都通过实现继承ClickableSpan的同时,实现NoCopySpan来解决,但是会在某些场景即辅助服务的时候,crash;
  3. 第二条,又有人提出TextView设置 android:importantForAccessibility=“no”, 但是某些机型仍然不生效;
  4. 回归到不继承NoCopySpan,来解决内存泄漏问题。

根本原因是匿名内部类:
比较java或者kotlin的lamda或者object:或者直接new XXXListener,参考我之前的帖子分析过内部类是啥情况(https://blog.csdn.net/jzlhll123/article/details/126593235)。
因此,这里给出建议:
第一条:
可以使用匿名内部类,必须满足不引用外部;参考我之前的帖子结论,这样的话,不论kotlin或者java都会优化编译不会持有外部对象,进而不会持有context。
第二条:这里设置的时候也定义一个static的class来实例化对象传入。也避免了对象传递;

 movementMethod = LinkMovementMethod.getInstance()
 textView.setSpan(CustomClickableSpan(click,
     underLineColor = it.underlineColor,
     textColor = it.textColor,
     isUnderlineText = it.isUnderlineText)
     , it)

class CustomClickableSpan(private val click:((View) -> Unit)?,
                          private val underLineColor:Int?,
                          private val textColor:Int?,
                          private val isUnderlineText:Boolean?) : ClickableSpan() {
    override fun onClick(widget: View) {
        click?.invoke(widget)
    }

    override fun updateDrawState(ds: TextPaint) {
        super.updateDrawState(ds)
        (underLineColor ?: textColor)?.let { color ->
            ds.color = color
        }
        isUnderlineText?.let {
            ds.isUnderlineText = it
        }
    }
}

第三条:
如果有引用则必须自定义weakRef。
比如requireContext(),则不能使用了,能用view.getContext()替换则替换。能用App的单例就用。
错误实例:

requireContext().startActivity(xxx) //Error: requireContext则会使用外部的this

改进:

private class OnListener(fragment: XXXFragment) : Function1<View, Unit> {
      private val fragmentRef:WeakReference<XXXFragment> = WeakReference(fragment)

      override fun invoke(p1: View) {
         fragmentRef.get()?.onClick(p1)       
         }
  }

private val onClick by unsafeLazy { OnListener(this) }

movementMethod = LinkMovementMethod.getInstance()
  sbText.setSpan(CustomClickableSpan(click,
      underLineColor = it.underlineColor,
      textColor = it.textColor,
      isUnderlineText = it.isUnderlineText)
      , it)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值