ReactiveNetwork icon indicating copy to clipboard operation
ReactiveNetwork copied to clipboard

ConnectivityManager error messages when clearing subscriptions

Open ap-johns opened this issue 6 years ago • 5 comments

We're using reactivenetwork-rx2:2.1.0

When switching activities we're seeing some ConnectivityManager related error messages. They don't seem to be causing any actual issues, but would be nice if they were cleared up.

Error messages seem to vary depending on device

eg

D/CCA: onPause
D/ConnectivityManager: unregisterNetworkCallback; CallingUid : 10242, CallingPid : 15126

OR

D/CCA: onPause
E/ConnectivityManager.CallbackHandler: callback not found for RELEASED message

Code sample

    @Override
    public void onResume() {
        Log.d(TAG, "onResume");
        super.onResume();

        disposables.add(ReactiveNetwork.observeNetworkConnectivity(this)
                .filter(ConnectivityPredicate.hasType(ConnectivityManager.TYPE_WIFI))
                .subscribeOn(Schedulers.io())
                .observeOn(io.reactivex.android.schedulers.AndroidSchedulers.mainThread())
                .subscribe((Connectivity it) -> {
                    Log.d(TAG, "observeNetworkConnectivity - state=" + it.state());
                    if (it.state() == NetworkInfo.State.CONNECTED) {
                        startReconnectUnitActivity();
                    } else {
                        promptText.setVisibility(View.VISIBLE);
                    }
                }, throwable -> Log.crashLog(TAG, "** onError (observeNetworkConnectivity) **")));
    }

    @Override
    public void onPause() {
        Log.d(TAG, "onPause");
        super.onPause();
        disposables.clear();
    }

ap-johns avatar Sep 03 '18 15:09 ap-johns

Hi,

Thanks for reporting this issue. In the library, I explicitly unregister callback while calling dispose() method. You can take a look at this line: https://github.com/pwittchen/ReactiveNetwork/blob/RxJava2.x/library/src/main/java/com/github/pwittchen/reactivenetwork/library/rx2/network/observing/strategy/LollipopNetworkObservingStrategy.java#L61.

Is there any particular reason because of which you are using clear() instead of dispose(). I think calling dispose() should solve this problem, but I'll ensure if it's possible to handle clear() method as well and verify if it makes sense within the library code.

pwittchen avatar Sep 03 '18 15:09 pwittchen

We found issues using disposables.dispose() in onPause() - it means you can then not add more subscriptions via disposables.add in eg onResume(). So our pattern is always to use disposables.clear() in onPause()

Thanks for your quick response by the way.

All working great apart from this one issue.

ap-johns avatar Sep 03 '18 15:09 ap-johns

Ok, I'll check what I can do with use case with clear() method.

pwittchen avatar Sep 03 '18 15:09 pwittchen

Another idea could be moving your code to the place independent from the Activity Lifecycle. E.g. to instance of the Application or dedicated Android Service. You can monitor network in one place and pass events to the Activity when needed. You can use many techniques for passing events between independent components (e.g. via event bus).

pwittchen avatar Sep 03 '18 17:09 pwittchen

When you use the ReactiveNetwork.observeNetworkConnectivity(context) if you ever call dispose of that observable it sends an onCancel signal to the origin. The origin unregisters the idleReceiver : BroadcastReceiver from the context provided. If you call have more than one subscribers to that origin the second dispose tries to send a cancel signal as well. The reference tries to unregister the same idleReceiver again from the context causing the error. I don't think this is unreasonable way to implement the library. The origin has to know to clean it self up from the context passed to it. So, you should consider protecting yourself from more than one subscriber by wrapping the library and observable provided with your own class that will never send the originator a cancel until App destruction. e.g.

interface NetworkStatusInterface {
    val networkStatusObservable: Observable<NetworkState>
}

class NetworkStatusRepos(connectivityObservable: Observable<Connectivity> = ReactiveNetwork.observeNetworkConnectivity(YourApplicationContext),
                         scheduler: Scheduler = Schedulers.computation()) : NetworkStatusInterface {

    private var disposable: Disposable
    private val networkStatusSubject = BehaviorSubject.create<NetworkState>()
    override val networkStatusObservable: Observable<NetworkState> = networkStatusSubject

    init {
        disposable = connectivityObservable
                .map { connectivity: Connectivity ->
                    when (connectivity.state()) {
                        NetworkInfo.State.CONNECTED -> Connected
                        NetworkInfo.State.CONNECTING -> Connecting
                        else -> NoInternet
                    }

                }
                .subscribeOn(scheduler)
                .subscribe({ networkStatusSubject.onNext(it) }, {
                    Log.e("@@", "NetworkStatusRepos.connectivityObservable: something has gone horribly wrong and you need to resubscribe.")
                })
    }
}

sealed class NetworkState
object Connected : NetworkState()
object Connecting : NetworkState()
object NoInternet : NetworkState()

jriley avatar Sep 23 '20 15:09 jriley