Android kotlin debugging coroutines - android

Before coroutines I used callbacks and debugging always gave me a lot of information. I could get the url my API call is going to, Headers I put into Request, interceptors I used, etc..
Now I use coroutines. All I can get when debugging is the final result of request (fail/sucess) with the result data. Nothing of all these valuable info I need is not there.
shortened example of my code:
restService.getVersionInfo().getResult(
success = {
when {
checkIsMandatory(it.lastMandatoryVersion) -> status.postValue(
Status.NewVersionInfo(it.description, true, it.url)
)
else -> initialization()
}
},
error = {
initialization()
}
I pout breakpoints to error or sucess. Am I missing something or coroutines really have this disadvantage. Please inform me

Related

How can I access the request parameters in the onResponse and the onFailure callbacks

I have a Retrofit call where I want to handle HTTP and Retrofit errors when calling an API.
So when a failure happens, I need to cache the request into a RoomDB/SQLite for when the API comes back online, or connection improves there is a routine that sends all those requests to the API.
x.enqueue(object : Callback<PayloadResponse> {
override fun onResponse(
call: Call<PayloadResponse>,
response: Response<PayloadResponse>
) {
...
val errorMessage = when {
response.code() != HttpURLConnection.HTTP_OK -> {
// Need the original Payload here so I can insert that data into RoomDB/SQLite
"An error occured duing API Call (NOT OK) &{response.code()}"
}
...
Same situation I need for the onFailure() callback.
Can I access the original request in these contexts? If so how?
Once I have encountered the same problem. My approach was debugging first parameter of retrofit callback's onResponse and onFailure.
call: Call<PayloadResponse>
call.request()
It contains all kind of information about your call / request e.g url, parameters, method. However, retrieving request data is not straightforward, consistent, and prone to bugs.
Then, I started using Kotlin coroutine which gives async process with sync nature of coding.
interface RetrofitApi{
#GET("your-route")
suspend fun sampleApiMethod(request:SampleRequest):Response<SampleCustomObject>
}
Your retrofit api methods may look like above.
CoroutineScope(Dispatchers.Main).launch {
val request = SampleRequest()
val deferred = async { apiService.sampleApiMethod(request) }
val result = deferred.await()
if(result.code() == 200){
// Do something
}else{
// Cache SampleRequest()
}
}
You can modify as much as to make it look better and utilize optimized light weight threads. It is one of the ways for me to handle such situations. It's just a hint, you may modify to have structurally and architecturally correct design in your project.

What is the best way to make a rxJava netwrok call and move work forward on UI thread based on response

I am making a network call using rxJava2, and based on the response (either success or error), I have to move my work forward on UI thread.
I have written the code below. It seems working fine.
WApi wApi = ServiceGenerator.createService(WApi.class, sURL);
dataManager = InventoryModule.getDataManager();
rx.Observable<GetFeature> getFeatureObservable =
dataManager.executeGetFeature(caseId, wrapperApi);
if (getCV2FeatureObservable != null) {
try {
getFeatureObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError(throwable -> {
Log.e(TAG, "Err::" + throwable.getMessage());
// init default values because of error response
initDefaultValues();
// No data to use from WS call. Move forward with
//cookie from SSO auth
cookieReceived(userID, cookieData, patchNo);
})
.onErrorResumeNext(rx.Observable.empty())
.subscribe(getFeature -> {
// use the data from WS response
processAndUpdateFeature(getFeature);
// move forward with cookie from SSO auth
cookieReceived(userID, cookieData, patchNo);
});
} catch (Exception e) {
Log.e(TAG, e.getLocalizedMessage());
}
}
Still I need opinions, Am I doing it right? Am I missing something? or can I use other operators and make it better? the way I am placing my UI work into corresponding operators, will it work properly in both error or success response?
The only questionable choice is all the complications you're doing on errors.
instead of using doOnError + onErrorResumeNext I suggest you move your logic to the Subscriber:
getFeatureObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getFeature -> {
// use the data from WS response
processAndUpdateFeature(getFeature);
// move forward with cookie from SSO auth
cookieReceived(userID, cookieData, patchNo);
}, { throwable -> {
Log.e(TAG, "Err::" + throwable.getMessage());
// init default values because of error response
initDefaultValues();
// No data to use from WS call. Move forward with
//cookie from SSO auth
cookieReceived(userID, cookieData, patchNo);
});
Your thread switching (subscribeOn and observeOn) is fine.
EDIT:
One more thing: Unless the processAndUpdateFeature or initDefaultValues or cookieReceived can throw an error, the try-catch block seems unnecessary.
In my experience I have had best luck using AsyncTask for network operations.
There are many advantages. 1) Use publish progress to show progress bar advancement. 2) You can have a single place to handle errors in a consistent way while also making the 'success' flow do different things. 3) AsyncTask is an Android construct so you have a good chance of it working consistently between versions.

Kotlin Coroutine returning after response.await()

I am trying to make an android app connecting to api, and for that I am using Kotlin Coroutines and Retrofit. I was following this tutorial (https://android.jlelse.eu/android-networking-in-2019-retrofit-with-kotlins-coroutines-aefe82c4d777) trying to setup my own api, but I stumbled on a problem. I can't get any data from api because I can't process response.
I don't know much about coroutines, so I don't understand whats the problem here. If I run debug and go line by line it is working perfectly each time, but if I run the app, it only prints TestPoint1. Also it doesn't throw any error and response is always 200 OK. I have tried to combine
val standings = service.getStandings()
and
val response = standings.await()
into one line, after which it doesn't work on debug either. It continues on code after launched coroutine.
val service = ApiFactory.footballApi
GlobalScope.launch(Dispatchers.Main) {
val standings = service.getStandings()
try {
Log.d("TAG", "TestPoint1")
val response = standings.await()
Log.d("TAG", "TestPoint2")
if(response.isSuccessful){
//store data
}else{
Log.d("MainActivity ",response.errorBody().toString())
}
}catch (e: Exception){
Log.d("TAG", "Error")
}
}
Switch the Dispatchers.Main to Dispatchers.IO . You can't make that request on the main thread .
The coroutines require a coroutine context in order to know in what thread they are going to run . For that , the Dispatchers class offer you some options . Currently you are making the request in the Dispatchers.Main which you cannot do because fetching data from the API , requires another thread . IO is the right thread for network calls .
Note :
Also please check : Internet permission, Internet connection .

Retrofit 2.5.0 not executing request after third time

I have the following code,
#Singleton
class LoginHandler {
private Observable<Response<User>> loginObservable;
private void attemptLogin(LoginRequest loginRequest) {
LoginSubscriber loginSubscriber = new LoginSubscriber();
loginObservable = api.login(loginRequest);
loginObservable
.observeOn(AndroidSchedulers.mainThread())
.subscribe(loginSubscriber);
}
}
with
#POST(PATH_AUTH)
Observable<Response<User>> login(#Body LoginRequest loginRequest);
This gets executed each time the user wants to login from a deep link.
I can log in and log out two times, but on the third time, the request just does not get executed. I also checked with the Charles Proxy tool, if the request is actually sent, but there was nothing to see.
Logging the loginObservable object shows that each api.login() call returns a different Observable object, which is correct for the subscription to be executed. Logging at doOnSubscribe(() -> ...) is also executed, just not the request itself.
I've browsed through SO and the only solution I found was to add a timeout(...) so that the user is able to get back to the login mask, once the timeout strikes. However when retrying to log in, the problem is still present.
So my question is, how could I debug this and find out, what is happening under the hood of retrofit and why is the request not executed after the third attempt?
Thank you very much in advance

getEntity call results in crash (using odata4j on a WCF service)

I am trying out odata4j in my android app to retrieve data from a DB that can be accessed from a WCF service.
ODataConsumer co = ODataConsumer.create("http://xxx.xx.xx.xxx:xxxx/Users");
for(OEntity user : co.getEntities("Users").execute())
{
// do stuff
}
However this crashes at the call to getEntities. I have tried a variety of other calls as well, such as
Enumerable<OEntity> eo = co.getEntities("Users").execute();
OEntity users = eo.elementAt(0);
However this also crashes at eo.elementAt(0).
The logcat doesn't tell me anything, and the callstack seems to be Suspended at ActivityThread.performLaunchActivity.
Entering "http://localhost:xxxx/Users" in my web browser on the other hand works as expected and returns the users in my DB in xml format.
Any ideas on how I can debug this?
To log all http requests/responses:
ODataConsumer.dump.all(true);
The uri passed to the consumer .create call should be the service root. e.g. .create("http://xxx.xx.xx.xxx:xxxx/"); Otherwise your code looks fine.
Note the Enumerable behaves like the .net type - enumeration is deferred until access. If you plan on indexing multiple times into the results, I'd suggest you call .toList() first.
Let me know what you find out.
Hope that helps,
- john
I guess the call should be:
ODataConsumer co = ODataConsumer.create("http://xxx.xx.xx.xxx:xxxx");
for(OEntity user : co.getEntities("Users").execute())
{
// do stuff
}
create defines service you want to connect but Users is the resource you want to query.
Can you try this way.
OEntity oEntity;
OQueryRequest<OEntity> oQueryRequest= oDataJerseyConsumer.getEntities(entityName);
List<OEntity> list= oQueryRequest.execute().toList();
for (OEntity o : list) {
List<OProperty<?>> props = o.getProperties();
for (OProperty<?> prop : props) {
System.out.println(prop.getValue().toString());
}
}

Categories

Resources