Android LiveData crash: Cannot add the same observer with different lifecycles

参考

Android lifecycle library: Cannot add the same observer with different lifecycles

项目场景:

最近项目的直播间中出现了少量的crash

出现在如下位置

liveData.observe(this, {
             ...
         })








问题描述:

当liveData多次调用observe时, 如果Observer使用lambda, 并且lamba中没有引用非静态变量或方法时, 可能就会出现crash! (为什么是可能, 可以看 后续)

比如: 当出现连点的情况, 重复启动页面时可能会出现此类情况.










原因分析:

crash的根源位置是在

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        ...

        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        ...
    }








解决方案:

可以看到crash原因是existing已经存在, 但没有attached到lifeCycleOwner时会throw Exception.

由crash error信息中可以推断出来, mObservers.putIfAbsent(observer, wrapper);

putIfAbsent中的key->observer已存在, 也就是这个lambda表达式已经存在于这个map结构中, 

lambda大坑: 
重复调用一个lambda函数时, 当该lambda中没有引用外部的非静态变量和方法, 会引用上一次的lambda实例

解决办法:

1.在lambda中添加非静态变量或方法的引用

2.不使用lambda, new出来一个Observer就可以啦~


后续:

感觉我的第一篇博客写的不够完美, 我决定重现一下这个bug, 结果整了一下午才重现(还好工作不太忙...), 最终才发现了我这边crash的根本原因 -- >

    因为项目中直播间是有后台播放的, 所以liveData没有存放在viewModel中, 而是放在了后台的service中; 当直播间在前台, 又通过某种方式(比如通过点击notification)创建了一个直播间实例后, 这时候再调用

oldLiveData.observer(newLifeCycleOwner, oldObserver),

此时crash就出来啦!!!

oldLiveData是从后台service拿到的之前liveData实例,

newLifeCycleOwner是新直播间实例的lifeCycleOwner, 

oldObserver就是lambda导致的, 所以用的是还是之前的实例.

这个时候我们再看下源码:

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        ...
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

observer由于lambda原因导致使用的之前的实例(这里有个小知识点, map.put("key", "newValue"), 如果该key已存在, 则会返回oldValue), 这个时候就会返回上一个直播间中liveData对应的existing, 而existings.isAttachedTo(owner) 实际上调用是这里

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
...
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
            mOwner = owner;
        }

@Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }
...
}

这个existings实际上是oldLifeCycleOwner和oldObserver构建的实例,

isArrachedTo 中的mOwner对应的就是oldLifeCycleOwner, owner就是newLifeCycleOwner, 因为两个直播间实例不一样, 所以这两个lifeCycleOwner当然不一样啦! -- >所以在existing.isAttachedTo返回的是false, 这个时候终于就会crash了(啊, 心好累...o(╥﹏╥)o).

记录完毕, 希望这篇文章能够帮到你~^_^

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吃饱很舒服

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值