I have some problems, which bound with lose of network connection. How i can handle it in RxJava 2? Thank you very much.
i have this method:
Disposable disposable = api.setStatus(params)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnSubscribe(listener::onPreExecute)
.doFinally(listener::onPostExecute)
.subscribe(serviceRequest -> handleResponse(listener, serviceRequest), listener::onError);
//////////////
#POST("set_status")
Single<OrderResponse> setStatus(#FieldMap Map<String, String> params);
New answer after question was update:
You can handle it in doOnError or your listener::onError:
Disposable disposable = api.setStatus(params)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.doOnError(error->{
handleError(error)
})
.subscribe(serviceRequest -> handleResponse(listener, serviceRequest), listener::onError);
void handleError(Throwable error){
if (error instanceof IOException){
// handle network error
} else {
if(error instanceof SocketTimeoutException){
// handle timeout error
}
}
}
Old answer about subscribing to connection change:
For this purposes you need to catch the network connection change and dispatch it via BehaviorSubject.
Like this:
class NetworkManager(
private val context: Context
) {
private val state: BehaviorSubject<Boolean> = BehaviorSubject.create()
private val receiver = object : BroadcastReceiver() {
override fun onReceive(c: Context?, intent: Intent?) {
state.onNext(isConnected())
}
}
init {
val intentFilter = IntentFilter()
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
context.registerReceiver(receiver, intentFilter)
state.onNext(isConnected())
}
fun subscribe(): Observable<Boolean> {
return state
}
fun isConnected(): Boolean {
val cm = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val netInfo = cm.activeNetworkInfo
return netInfo != null && netInfo.isConnectedOrConnecting
}
}
Create this classs in your Application scope and it will be ok
if you return Single for network calls you can use the onErrorReturn method to catch error and return state to indicate network error.
Handle in onError method as below
void onError(Throwable throwable) {
if(throwable instanceof IOException) {
//Handle network error
}else if(throwable instanceof SocketTimeoutException) {
//Handle Request timeout
}else {
//Show some error like something went wrong
}
}
Related
I have a websocket connection
object StompWrapper {
private var emitter: ObservableEmitter<Event>? = null
init {
val client = OkHttpClient.Builder().build()
val stomp = StompClient(client)
stomp.connect()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
when (it.type) {
Event.Type.OPENED -> {
Timber.d("Connect OPENED")
isConnected = true
emitter?.onNext(Event(Event.Type.OPENED))
}
Event.Type.CLOSED,
Event.Type.ERROR -> {
Timber.d("Connect ERROR")
isConnected = false
emitter?.onNext(Event(Event.Type.ERROR))
}
else -> {}
}
}, { e ->
Timber.e(e)
})
}
fun status(): Observable<Event> {
return Observable.create {
Timber.d("Connect CREATE status $isConnected")
emitter = it
if (isConnected) {
emitter?.onNext(Event(Event.Type.OPENED))
}
}
}
}
calling like this
disposable.add(StompWrapper.status().subscribe {
Timber.d("Connect: %s", it.type)
})
It turns out that connection hangs in a static class. It works if there is one subscriber, if you subscribe a couple of times, the last subscription will work and that's it. Please tell me how to make it so that I can find out the current status of the connection to the server anywhere in the application and do this as many times as I like?
I am trying to listen to network changes using method registerDefaultNetworkCallback() of conenctivityManager
Using the code below from this answer
val connectivityManager = cotnext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager?.let {
it.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
//take action when network connection is gained
}
override fun onLost(network: Network) {
//take action when network connection is lost
}
})
}
but I have a few questions about this method:
what if the phone is connected to wifi but the wifi is not connected to Internet
In the method documentation I read this which I don't understand, when exactly will the limit will hit? If the callback is called 100 times then an Exception will be thrown? And how to handle this?
To avoid performance issues due to apps leaking callbacks, the system will limit the number of outstanding requests to 100 per app (identified by their UID), shared with all variants of this method, of requestNetwork as well as ConnectivityDiagnosticsManager.registerConnectivityDiagnosticsCallback. Requesting a network with this method will count toward this limit. If this limit is exceeded, an exception will be thrown. To avoid hitting this issue and to conserve resources, make sure to unregister the callbacks with unregisterNetworkCallback(ConnectivityManager.NetworkCallback).
what if the phone is connected to wifi but the wifi is not connected
to Internet
The answer, this method will return false
In the method documentation I read this which I don't
understand, when exactly will the limit will hit? If the callback is
called 100 times then an Exception will be thrown? And how to handle
this?
I think it means if you cant register more than 100 callback
At first, add the ConnectivityReceiver class:
class ConnectivityReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (connectivityReceiverListener != null) {
connectivityReceiverListener!!.onNetworkConnectionChanged(
isConnectedOrConnecting(
context
)
)
}
}
private fun isConnectedOrConnecting(context: Context): Boolean {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (cm != null) {
if (Build.VERSION.SDK_INT < 23) {
val ni = cm.activeNetworkInfo
if (ni != null) {
return ni.isConnected && (ni.type == ConnectivityManager.TYPE_WIFI || ni.type == ConnectivityManager.TYPE_MOBILE)
}
} else {
val n = cm.activeNetwork
if (n != null) {
val nc = cm.getNetworkCapabilities(n)
return nc!!.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || nc!!.hasTransport(
NetworkCapabilities.TRANSPORT_WIFI
)
}
}
}
return false
}
interface ConnectivityReceiverListener {
fun onNetworkConnectionChanged(isConnected: Boolean)
}
companion object {
var connectivityReceiverListener: ConnectivityReceiverListener? = null
}
}
Then In your BaseActivity or MainActivity add these lines:
abstract class BaseActivity:AppCompatActivity(),
ConnectivityReceiver.ConnectivityReceiverListener {
var receiver: ConnectivityReceiver? = null
override fun onResume() {
super.onResume()
try {
receiver = ConnectivityReceiver()
registerReceiver(
receiver!!,
IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
)
connectivityReceiverListener = this
} catch (ex: Exception) {
//Timber.d("Base ex ${ex.localizedMessage}")
}
}
override fun onPause() {
try {
unregisterReceiver(receiver!!)
receiver = null
} catch (ex: Exception) {
}
super.onPause()
}
override fun onNetworkConnectionChanged(isConnected: Boolean) {
showMessage(isConnected)
}
private fun showMessage(isConnected: Boolean) {
try {
if (!isConnected) {
Log.d("Connection state"," disconnected")
} else {
Log.d("Connection state"," connected")
}
} catch (ex: Exception) {
}
}
}
You should register the receiver in the OnResume method and unregister it in theOnPause method
I try handling exception using coroutine. I wrote code like this, but didn't work. I can't see any log except for using try-catch. I do not want to use try catch at all function, but want to make clean code handling exception. what should I do for this?
viewmodel
private val handler = CoroutineExceptionHandler { _, exception ->
when (exception) {
is UnknownHostException -> {
showLog("login UnknownHostException : " +exception.message)
}
else -> {
}
}
}
fun login(mobile:String){
viewModelScope.launch(handler) {
try{
var login = apiRepository.login(mobile)
_isLogin.value = login
}catch(e:Exception){
}
}
}
repository
override suspend fun login(mobile: String): LoginResultData {
var result =LoginResultData()
withContext(ioDispatcher){
val request = apiServerModel.login(mobile)
val response = request.await()
result = response
}
return result
}
fun login(mobile:String){
viewModelScope.launch(handler) {
val login = apiRepository.login(mobile)
_isLogin.value = login
}
}
I just want to ask if it is possible to get the response of another observable after encountering an error from the another observable?
for example I am calling a two api Avatar and Attachment using a combineLatest.
val avatar: Observable<ResponseBody> = api().getAvatar()
val attachment: Observable<ResponseBody> = api().getAttachment()
val obs = Observables.combineLatest(avatar, attachment)
.map { it ->
if (it.first is Exception) {
Log.e(TAG, "getAvatar failed")
} else {
updateAvatar()
}
if (it.second is Exception) {
Log.e(TAG, "getAttachment failed")
} else {
updateAttachment()
}
if (it.first !is Exception && it.second !is Exception) {
Log.i(TAG, "success first=${it.first}, second=${it.second}")
updateAll()
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorReturn { it }
.subscribe()
disposable.add(obs)
I just want to get the avatar response if the attachment error and I want to get the attachment response if the avatar error.
Thanks.
Yes, my friend. You can handle error for each observable that you combine by calling onErrorReturn() method. You can use empty ResponseBody for detecting error. Final code
val avatar: Observable<Optional<ResponseBody>> = api().getAvatar().onErrorReturn{ Optional.empty }
val attachment: Observable<Optional<ResponseBody>> = api().getAttachment().onErrorReturn{ Optional.empty }
val obs = Observables.combineLatest(avatar, attachment) {avatar, attachment ->
if (!avatar.isPresent()) {
//logic
}
if (!attachment.isPresent()) {
//logic
}
}.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorReturn { it }
.subscribe()
If you use java 7 or lower in you project, you can write your own Optional
class Optional<T>(val value: T?) {
companion object {
fun <T> empty(): Optional<T> = Optional(null)
}
fun isPresent() = value != null
}
Hello guys I have in my BaseActivity the following function.
override fun <T> subscribeToInternet(observable: Observable<Response<BaseResponse<T>>>, observer: Observer<BaseResponse<T>>) {
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { observer.onSubscribe(it) }
.doOnError {
Log.d(TAG, it.message)
observer.onError(it)
}
.doOnComplete { observer.onComplete() }
.doOnNext {
Log.d(TAG, "${it.body() ?: "no body"}")
Log.d(TAG, "${it.errorBody()?.string() ?: "no error body"}")
Log.d(TAG, it.code().toString())
when {
it.code() == 401 -> {
view.userUnauthenticated()
observer.onNext(BaseResponse(false, "unauthenticated", null))
Log.d(TAG, "UNAUTHENTICATED")
}
it.code() == 423 -> {
view.userBlocked()
observer.onNext(BaseResponse(false, "blocked", null))
Log.d(TAG, "BLOCKED")
}
it.isSuccessful -> observer.onNext(it.body()!!)
it.code() == 429 -> observer.onNext(BaseResponse(false, "Too many attempts", null))
it.code() == 400 -> observer.onNext(BaseResponse(false, "Invalid Email or password", null))
else -> observer.onNext(BaseResponse(false, "", null))
}
}
.subscribe()
}
And I handle the error in the observer's onNext() if the server returns a response, but the problem when there's no Internet connection on the device at all!! It throws the following exception
at io.reactivex.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onError(ObservableDoOnEach.java:119)
at io.reactivex.internal.observers.DisposableLambdaObserver.onError(DisposableLambdaObserver.java:64)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.checkTerminated(ObservableObserveOn.java:276)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:172)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:252)
And this is the usage of the previously mentioned function
override fun sendLoginRequest(email: String, password: String, fcm_token: String) {
subscribeToInternet(dataManager.sendLoginRequest(email, password, fcm_token), this)
}
override fun onComplete() {
}
override fun onSubscribe(d: Disposable) {
DisposableManager.add(d)
}
override fun onNext(t: BaseResponse<LoginData>) {
if(t.status) {
Log.d(TAG, "${t.data}")
dataManager.createLoginSession(t.data!!)
view.loginSuccess()
} else {
Log.d(TAG, t.message)
view.showError(t.message)
}
}
override fun onError(e: Throwable) {
view.showToastError()
Log.d(TAG, e.message)
}
That problem is connected with the way you subscribing to observable. According to
documentation when using subscribe() without passing action for handling errors, you should receive OnErrorNotImplementedException when source throws exceptions - that's because default exception handler from RxJavaPlugins is used.
To resolve that problem use one of overloaded subscribe methods with onError parameter. For example, public final Disposable subscribe(Consumer onNext,
Consumer onError)