gRPC Android DEADLINE_EXCEEDED but no request made to server - android

I believe I may not understand something about how gRPC Channels, Stubs, And Transports work. I have an Android app that creates a channel and a single blocking stub and injects it with dagger when the application is initialized. When I need to make a grpc call, I have a method in my client, that calls a method with that stub. After the app is idle a while, all of my calls return DEADLINE_EXCEEDED errors, though there are no calls showing up in the server logs.
#Singleton
#Provides
fun providesMyClient(app: Application): MyClient {
val channel = AndroidChannelBuilder
.forAddress("example.com", 443)
.overrideAuthority("example.com")
.context(app.applicationContext)
.build()
return MyClient(channel)
}
Where my client class has a function to return a request with a deadline:
class MyClient(channel: ManagedChannel) {
private val blockingStub: MyServiceGrpc.MyServiceBlockingStub = MyServiceGrpc.newBlockingStub(channel)
fun getStuff(): StuffResponse =
blockingStub
.withDeadlineAfter(7, TimeUnit.SECONDS)
.getStuff(stuffRequest())
}
fun getOtherStuff(): StuffResponse =
blockingStub
.withDeadlineAfter(7, TimeUnit.SECONDS)
.getOtherStuff(stuffRequest())
}
I make the calls to the server inside a LiveData class in My Repository, where the call looks like this: myClient.getStuff()
I am guessing that the channel looses its connection at some point, and then all of the subsequent stubs simply can't connect, but I don't see anywhere in the AndroidChannelBuilder documentation that talks about how to handle this (I believed it reconnected automatically). Is it possible that the channel I use to create my blocking stub gets stale, and I should be creating a new blocking stub each time I call getStuff()? Any help in understanding this would be greatly appreciated.

After researching a bit, I believe the issue was that the proxy on the server was closing the connection after a few minutes of idle time, and the client ManagedChannel didn't automatically detect that and connect again when that happened. When constructing the ManagedChannel, I added an idleTimeout to it, which will proactively kill the connection when it's idle, and reestablish it when it's needed again, and this seems to solve the problem. So the new channel construction looks like this:
#Singleton
#Provides
fun providesMyClient(app: Application): MyClient {
val channel = AndroidChannelBuilder
.forAddress("example.com", 443)
.overrideAuthority("example.com")
.context(app.applicationContext)
.idleTimeout(60, TimeUnit.SECONDS)
.build()
return MyClient(channel)
}

Related

How to use workmanager as an upload service

I am building an offline first chat app and so I'm using Workmanager to handle POST requests to send chat messages.
I'm learning android development and so I wanted some help on architecturing the upload of chat messages
Current implementation
When ever a new chat message needs to be posted the client does the following
Saves the chat to SQLite using Room with a new UUID
Starts a workmanager unique work to POST this message
This way I can be sure the message is posted eventually when the client has internet
Is this ideal? There are a few issues I see.
I'm starting too many workers. Each message has a work request.
Chronology of messages posted to the server is lost.
A better implementation
A single unique worker to POST messages. Which will fetch all offline messages and post them in the right order
Still not ideal
The issues with these implementation are:
You have very little control on a work (not so easy) once it's started.
If a work fails we've set a backoff time. So when a new message is to be sent we need to replace the old worker with the new work request. This just seems nonoptimal.
We are mutating the worker instead of appending a new task to the queue.
We can't use the one worker per message implementation because we loose chronology and there are too many workers
This is sort of a distributed systems question.
We are starting workers who should work independ of the lifetime of the app
Workers should come back alive in a case they die (already managed by android-workmanager)
Workers should read from a queue of task to be executed (which is what I'm looking for)
There should be a persistance store that acts as a queue for the workers
There should be a service or a factory that invokes workers when ever needed (We don't have this in the current impl)
Questions
Is there a better way to post offline messages to the server when the client has internet? Like a service?
Is there a community build library that does this?
Can the current implementation be scaled to files?
I would need long running workers
Or could use this lib - android-upload-service
Yes you can use Service to effectively sync the messages, First create an Object which will extend from Live Data Class to get live updates for network updates like this,
object NetworkState : LiveData<Boolean>() {
private lateinit var application: Application
private lateinit var networkRequest: NetworkRequest
private lateinit var connectivityManager: ConnectivityManager
fun init(application: Application) {
this.application = application
connectivityManager = application.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
networkRequest = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build()
}
override fun onActive() {
super.onActive()
getDetails()
}
private fun getDetails() {
connectivityManager.registerNetworkCallback(networkRequest, object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
postValue(true)
}
override fun onLost(network: Network) {
super.onLost(network)
postValue(false)
}
override fun onUnavailable() {
super.onUnavailable()
postValue(false)
}
})
}
Then initialize this in your Application class like this,
#HiltAndroidApp
class BaseApplication : Application(), Configuration.Provider {
#Inject
lateinit var workerFactory: HiltWorkerFactory
override fun getWorkManagerConfiguration() =
Configuration.Builder()
.setWorkerFactory(workerFactory)
.build()
override fun onCreate() {
super.onCreate()
NetworkState.init(this)
if (BuildConfig.DEBUG){
Timber.plant(Timber.DebugTree())
}
}
}
Now you can create a Service class which will extend from Lifecycle Service and observe the Network updated there. And when the Live Data will emit value true you can check if there are messages which needs to be synced online. But remember it will only work when the app is in foreground.

Websocket connection wasn't closed after the app killed

My Websocket client (OkHttp) doesn't close the connection after the app closed. It opens a new connection every time I open the app which makes the app suffering from multiple messages received on the old and the new Websocket connections from the broadcasting server.
Is that a normal behavior for the android client, as for what I have experienced with web-client, the session was closed properly after the tab killed?
I have been looking up the problem across the internet but no luck so far. I want to make sure whether it happened because of my bad code logic or just the buggy Websocket's implementation from the library?
Here is how I start a new websocket session in the main Activity
var request: Request = Request.Builder()
.url("ws://$serverIP:8080/example/sim/speed")
.build()
var webSocketListener: WebSocketListener = object : WebSocketListener() {
override fun callback(msg: Message) {
updateSpeed(msg.content)
}
override fun onClosing(webSocket: WebSocket?, code: Int, reason: String?) {
super.onClosing(webSocket, code, reason)
}
}
var webSocket = client!!.newWebSocket(request, webSocketListener)
After that updateSpeed() will update a text view on UIThread
The onClosed event was not triggered when the app closed but only when the close function called manually.
I'm sure that it opened a new socket every time because I can see new sessions created on the server with different ports.
What I want is to have the app closing its connection before it was closed.
Thank you
I also encountered this issue, that WebSocket was still opened after my app was killed.
To solve this issue I manually close socket and remove all idle connections:
fun clear() {
webSocket?.close(1001, "Android: User exited the game.")
webSocket = null
subs.clear()
subs.add(Completable.fromAction {
client.connectionPool.evictAll()
}.subscribeOn(Schedulers.io()).subscribe({}, {}))
}
And I basically call clear() inside activity/ies, that might be opened during app kill.
override fun onDestroy() {
super.onDestroy()
App.webSocketClient().clear()
}
Not an ideal solution, but works every time.

How to verify Mockito interaction with the view that happen on new thread other than the one which is running the test case?

I have a below scenario when setView is called then Presenter fetches some data over the network on a new thread. Test fails by giving this reason - Actually, there were zero interactions with this mock. But it should pass if interaction gets verified.
Testcase
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
verify(rateView,times(1)).showRate(new Rate());
}
Inside "ratePresenter.setView"
Call<UnoRate> call1 = ratesAPI.getUnoRate();
call1.enqueue(new Callback<UnoRate>() {
#Override
public void onResponse(Call<UnoRate> call,Response<UnoRate> response) {
UnoRate unoRate = response.body();
Rate rate = new Rate();
rate.setBuyRate(unoRate.getBuy());
rate.setSellRate(unoRate.getSell());
rate.setFee(0);
rateView.showRate(rate);
}
});
One very simple solution is to use Mockito's verification with timeout feature. This will retry the verification repeatedly up until the timeout, looking for the condition to pass at some point or another.
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
verify(rateView, timeout(100).times(1)).showRate(new Rate());
}
The docs, however, warn against it: "This feature should be used rarely - figure out a better way of testing your multi-threaded system." This is probably because you're introducing a new aspect--time--as a proxy for the thing you really want to check, which is that all of the queues have been processed. You could even imagine a busy enough VM where a conservative timeout could cause the test to flake in automated testing systems but that works fine on development machines.
If feasible, you could switch your ratesAPI to use a synchronous executor, or instead you could add methods needed to your API accessor to block the test thread until all calls have returned asynchronously:
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
ratesAPI.flush(); // Implement this to perform a Thread.join on the callback thread,
// or otherwise wait until all callbacks have been called.
verify(rateView,times(1)).showRate(new Rate());
}
Or, to remove multithreading and external API interactions from your test, simulate the callback synchronously:
#Mock RatesAPI ratesApiMock;
#Mock Call<UnoRate> unoRateCallMock;
#Captor Callback<UnoRate> unoRateCallbackCaptor;
#Test
public void checkUnoRate() {
// Set up mock.
when(ratesApiMock.getUnoRate()).thenReturn(unoRateCallMock);
// Perform the action.
ratePresenter.setView(rateView,Constants.UNO);
// Verify nothing happens yet.
verify(rateView, never()).showRate(any());
// Capture and trigger the callback.
verify(unoRateCallMock).enqueue(unoRateCallbackCaptor.capture());
unoRateCallbackCaptor.getValue().onResponse(yourCall, yourResponse);
// Verify the asynchronous action.
verify(rateView,times(1)).showRate(new Rate());
}
As a side note, eventually you'll probably want to verify against a different parameter than new Rate(). Mockito compares via equals methods when not using Mockito matchers.

Android - OkHttp - Tagging all network requests with TrafficStats

I'm trying to figure out specifically how much of my app's data use is being used by the requests I send with OkHttpClient, and I saw that I can use TrafficStats to tag a thread and then see it's network activity with the tag.
if I do something like
TrafficStats.setThreadStatsTag(1234);
okHttpClient.execute(request);
then it actually tags it okay(ish), but then when I use the async method (okHttpClient.enqueue(request)) it doesn't (which is kinda obvious though I hoped they'd have support for that).
So I tried a couple of things:
Setting a dispatcher for the client where it's a normal dispatcher which basically on every execute replaces the Runnable it receives with a new runnable that first tags the thread an then runs the original runnable - some traffic was tagged but a lot wasn't.
Setting a socket factory which basically tags every socket it produces - still some some traffic tagged but most of it wasn't.
Any ideas?
I think TrafficStats.setThreadStatsTag() is for thread, so maybe we can add an interceptor for okhttp client.
private static class TrafficStatInterceptor implements Interceptor {
int mTrafficTag;
TrafficStatInterceptor(int trafficTag) {
mTrafficTag = trafficTag;
}
#Override
public Response intercept(Chain chain) throws IOException {
if (mTrafficTag > 0) {
TrafficStatUtils.setThreadStatsTag(mTrafficTag);
} else {
Log.w(TAG, "invalid traffic tag " + mTrafficTag);
}
return chain.proceed(chain.request());
}
}
then just add this interceptor
OkHttpClient.Builder client = new OkHttpClient.Builder();
client.addNetworkInterceptor(new TrafficStatInterceptor(trafficTag));
It’s difficult to do generally because with HTTP/2 sockets are shared across requests. With HTTP/1.1 they’re reused. Your best bet will be to write a network interceptor to tag the current thread. That’ll handle all HTTP/1.1 traffic and outgoing HTTP/2 traffic. There’s currently no API to access the thread that reads incoming HTTP/2 traffic.

Is it possible to re-subscribe to a Retrofit 2 observable?

I'm using Retrofit 2 with RxAndroid, and I want to keep a request going during a config change. I thought I could do it with Observable.cache() as described in this blog post and others I've seen, but the following flow causes an InterruptedException.
Observable<Result<List<Post>>> request =
postService.index(page).cache();
Subscription subscribeOne = request.subscribe();
subscribeOne.unsubscribe();
Subscription subscribeTwo = request.subscribe();
I'm pretty sure the following code in the Retrofit source is responsible for cancelling the request when unsubscribe is called.
// Attempt to cancel the call if it is still in-flight on unsubscription.
subscriber.add(Subscriptions.create(new Action0() {
#Override public void call() {
call.cancel();
}
}));
Not unsubscribing makes everything work, but this could cause leaks. Has anyone managed to handle config changes with Retrofit 2? Is there a different approach I can use?
Thanks to a hint from /u/insane-cabbage, I managed to implement this with a BehaviourSubject (safely encapsulated in a presenter). Here's an example of the flow.
BehaviorSubject<String> subject = BehaviorSubject.create();
/** User loads view and network request begins */
Observable.just("value")
.delay(200, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.newThread())
.subscribe(subject::onNext);
Subscription portraitSub = subject.subscribe(
s -> System.out.println("Portrait: " + s));
/** onDestroy() */
portraitSub.unsubscribe();
/** Rotating... */
Thread.sleep(300);
/** onRestoreInstanceState() **/
Subscription landscapeSub = subject.subscribe(
s -> System.out.println("Landscape: " + s));
/** Output */
> Landscape: value
I have a working example RxApp that uses AsyncSubject to implement cache for network request and the code shows how to subscribe to a pending request. I'm a bit confused with Rx subjects as on the other they seem pretty handy but on the other hand it's recommended that they are to be used only in very seldom cases e.g. To Use Subject Or Not To Use Subject?. Would be great if some one could explain what really is the problem if they're used like in my example.

Categories

Resources