Where does Retrofit pull data from server? - android

I'm learning Coroutines of Kotlin and I'm a beginner of Retrofit.
The Code A is from the artical.
It seems that Retrofit get data from local server http://localhost/, I havn't run local web server.
Where does Retrofit get data ?
Code A
private val service: MainNetwork by lazy {
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(SkipNetworkInterceptor())
.build()
val retrofit = Retrofit.Builder()
.baseUrl("http://localhost/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofit.create(MainNetwork::class.java)
}
fun getNetworkService() = service
/**
* Main network interface which will fetch a new welcome title for us
*/
interface MainNetwork {
#GET("next_title.json")
suspend fun fetchNextTitle(): String
}

Itself!
If you look at the code: .addInterceptor(SkipNetworkInterceptor()). Interceptors process every call that is made through Retrofit.
Some people use it to log data about calls for debugging. Some people use it to automatically provide authorization tokens.
In this case, it is providing fake data.
You can read about that here:
https://github.com/googlecodelabs/kotlin-coroutines/blob/master/coroutines-codelab/finished_code/src/main/java/com/example/android/kotlincoroutines/util/SkipNetworkInterceptor.kt

You should write this IP which is the equivalent of localhost in base url.
val retrofit = Retrofit.Builder()
.baseUrl("http://10.0.2.2:3000/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
But you should check your port number on localhost. After checking, you should add your port number to the part that says 3000. Replace with 3000.

Related

How can i handle retrofit errors or get url request for to detect problems

I am trying to learn develop android applications and I'm trying to implement the latest approaches. So i use as many jetpack libraries as possible. (Dagger-Hilt, Coroutines, Retrofit etc)
Here is my question:
i have AppModule object for dependency injection.
Here is my retrofit object:
#Singleton
#Provides
fun provideConverterApi(): ConverterAPI {
return Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ConverterAPI::class.java)
}
How can i get error messages from there or for example i need to see the url i use for the request, how can i do that?
You're doing great, to add a logger for your network call use this way:
.addConverterFactory(GsonConverterFactory.create())
.addInterceptor(HttpLoggingInterceptor().apply {
level = if (DEBUG) BODY else NONE
})
.build()
Base on #Amjad Alwareh, remember to add the dependence of the HTTP logging interceptor.
implementation "com.squareup.okhttp3:logging-interceptor:${okHttpVersion}", // 3.12.1 or other version
Maybe the DEBUG should be BuildConfig.DEBUG

How Observable is created in retrofit without calling it from scheduler?

I am working on rx-java and retrofit and I have some queries related to it.I am not able to understand that when we call below code then how Observable(Observable) is created ?
According to my understanding it should be called only during the time of scheduling and subscribing.
CryptocurrencyService cryptocurrencyService = retrofit.create(CryptocurrencyService.class);
Observable<Crypto> cryptoObservable = cryptocurrencyService.getCoinData("btc");
There is a call adapter method in Retrofit Builder which convert Call responses to corresponding one in RxJava
Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()
.build()

Retrofit 2.6.0 exception: java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.Deferred

I have a project with Kotlin coroutines and Retrofit.
I had these dependencies:
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
Today I have updated Retrofit to 2.6.0 in the project. In https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter it is written that it is deprecated now. In https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05 it is written that Retrofit currently supports suspend.
So, I removed retrofit2-kotlin-coroutines-adapter:0.9.2 and in Retrofit client changed these lines:
retrofit = Retrofit.Builder()
.baseUrl(SERVER_URL)
.client(okHttpClient)
.addConverterFactory(MyGsonFactory.create(gson))
//.addCallAdapterFactory(CoroutineCallAdapterFactory()) - removed it.
.build()
When run, the first request catches an exception:
java.lang.IllegalArgumentException: Unable to create call adapter for kotlinx.coroutines.Deferred<com.package.model.response.UserInfoResponse>
for method Api.getUserInfo
As I understood, instead of CoroutineCallAdapterFactory() I could use CallAdapter.Factory(), but it is abstract.
If in Api class I change a request adding suspend in the beginning:
#FormUrlEncoded
#POST("user/info/")
suspend fun getUserInfo(#Field("token") token: String): Deferred<UserInfoResponse>
override suspend fun getUserInfo(token: String): Deferred<UserInfoResponse> =
service.getUserInfo(token)
I get this exception:
java.lang.RuntimeException: Unable to invoke no-args constructor for kotlinx.coroutines.Deferred<com.package.model.response.UserInfoResponse>. Registering an InstanceCreator with Gson for this type may fix this problem.
Reading https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05 I saw:
New: Support suspend modifier on functions for Kotlin! This allows you
to express the asynchrony of HTTP requests in an idiomatic fashion for
the language.
#GET("users/{id}") suspend fun user(#Path("id") long id): User
Behind the scenes this behaves as if defined as fun user(...):
Call and then invoked with Call.enqueue. You can also return
Response for access to the response metadata.
Currently this integration only supports non-null response body types.
Follow issue 3075 for nullable type support.
I changed requests so: added suspend and removed Deferred:
#FormUrlEncoded
#POST("user/info/")
suspend fun getUserInfo(#Field("token") token: String): UserInfoResponse
override suspend fun getUserInfo(token: String): UserInfoResponse =
service.getUserInfo(token)
Then in interactor (or simply when called the method getUserInfo(token)) removed await():
override suspend fun getUserInfo(token: String): UserInfoResponse =
// api.getUserInfo(token).await() - was before.
api.getUserInfo(token)
UPDATE
Once I encountered a situation when downloading PDF files required removing suspend in Api class. See How to download PDF file with Retrofit and Kotlin coroutines?.
In my case I was missing the CoroutineCallAdapterFactory in my Retrofit initialization. Retrofit v2.5.0
Before:
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(httpClient)
.addConverterFactory(MoshiConverterFactory.create())
.build()
After: (working code)
val retrofit = Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(httpClient)
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.build()

How to write a generic retrofit response and be able to log / debug it when deserialization does not work?

I struggled with this problem in the last hours and I looked for a guide.
I have an update request that I want to write in a generic way, which means that I would like to get a JSON of any kind, like a bag of property, and be able to work with it later in the application.
The problem I'm facing is that whenever I try to get it as JSON or String (and not as Model or ResponseBody) the app crashes. I think the arrest is due to decoding based on what I see.
This is the Retrofit builder.
public Retrofit getRetrofit(OkHttpClient okHttpClient) {
Retrofit retrofit = new Retrofit
.Builder()
.baseUrl("https://reqres.in/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(MoshiConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
return retrofit;
}
The REST interface is super simple.
public interface IRestService {
#GET("api/users?page=2")
Observable<String> queryPage();
}
I also tried to debug this issue, and I added my logger interceptor, that obviously does not work, and I have no idea why:
public OkHttpClient getOkHttpClient(Interceptor requestInterceptor) {
// HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
// loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
HttpLoggingInterceptor loggingInterceptor =
new HttpLoggingInterceptor((msg) -> {
Log.d(HttpLoggingInterceptor.class.toString(), msg);
});
//loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
LoggingInterceptor customLoggingInterceptor = new LoggingInterceptor();
return new okhttp3.OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS)
.addInterceptor(requestInterceptor)
.addInterceptor(loggingInterceptor)
.addInterceptor(customLoggingInterceptor)
.build();
}
Please let me know if there is anything I am doing wrong in getting the JSON out in a generic form, without a model.
And it would also be a great help if I could get some advice on how to debug this kind of problem, since the logger did not register anything in Android Studio.
Sorry if none of these questions is silly, but I've just started building my first app in Android. I have experience in many other languages.
Instead of using String for your Observable in the service(interface), use Object as the type. And when you get the data, you can always use Gson().toJson(body.toString()) to convert it to the string. Also, most probably your app is crashing for String because you don't have the correct converter factory (Scalar Factory). You can check this for the same.

How to handle dots in json key fields, while using retrofit?

I am just simply trying to use retrofit to perform my rest api calls. The issue that I am facing is that when parsing the json, some of the key fields contain dots. For example:
{ "data": { "name.first": "first name"} }
Is it possible to configure Retrofit (or GsonConverter) to be able to handle this and how do I go about doing so?
This is neither Retrofit nor the GsonConverter's responsibility but rather Gson which sits underneath doing the actual JSON (de)serialization.
You can use Gson's #SerializedName annotation to work around names which cannot be represented in Java:
#SerializedName("name.first")
public final String firstName;
if you are using Moshi as your JSON convertor, replace it with GSON convertor factory.
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create()) //add this
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.client(
getOkHttpClient(
NetworkModule.networkModule.context,
enableNetworkInterceptor(baseUrl)
)
)

Categories

Resources