新しいプロジェクトで使用されている LiveData フレームワークですが、ある日、LiveData によって登録されたリッスン コールバックが一度だけ出力され、その後は消えないことに気づきました。そこで、その理由を調べに行ってきました。最終的にトライキャッチが原因であることが分かりました。
サンプルコード:
私のコードはおそらく次のようになります: クラッシュを防ぐためにメソッドでtry catch が使用されています
val mBanner=MutableLiveData<List<Banner>>()
fun test(){
println("1")
try {
mBanner.observe(this){
println("2")
throw RuntimeException("奔溃了")
println("3")
}
}catch (e:Exception){
}
println("4")
}
//打印的结果: 1,2,4
デバッグ後、初めてクラッシュする限り、このコールバックは二度と使用されないことがわかりました。したがって、バグは tyr catch に関連しているはずです。
LiveData でイベントを配信する方法を見てみましょう。また、以下でマークされた場所でデバッグすると、 mDispatchingValue が常に true であることが判明し、その結果、次のステップを実行できなくなりました。そして、mDispatchingValue はグローバル変数です。では、この値はどこで false に設定されるのでしょうか? 次のメソッドの マークに示されているように、メソッド全体が実行された後でのみ false に設定されます。
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) { //在此处debug
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue()); //分发事件
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false; //方法执行完毕后置为falses
}
これで、問題は非常に明確になりました。メソッドの呼び出し時にクラッシュし、catch が直接実行され、後続のコードが実行されず、その結果、dispatchingValue メソッドが最後まで実行されず、mDispatchingValue が常に実行されなかったためです。本当だった。
解決:
では、クラッシュを回避し、同時に修正するにはどうすればよいでしょうか?
登録されたコールバックがクラッシュする箇所でtry catchでラップすることで、 dispatchingValueが確実に実行されるようにします。
val mBanner=MutableLiveData<List<Banner>>()
fun test(){
println("1")
try {
mBanner.observe(this){
try {
println("2")
throw RuntimeException("奔溃了")
println("3")
}catch (e:Exception){
}
}
}catch (e:Exception){
}
println("4")
}
次のメソッドに示すように、LiveData拡張関数をカプセル化して書き換えて、コールバック メソッドに try catch を与え、コールバック メソッドが完了できることを確認し、 dispatchingValueが完了しないことを回避します。
fun <T> MutableLiveData<T>.safeObserve(owner: LifecycleOwner, onChange: (T) -> Unit) {
this.observe(owner) {
try {
onChange(it)
} catch (e: Exception) {
e.printStackTrace()
}
}
}