11

I am struggling to translate this retrofit class into Kotlin. It is basically a singleton that works as a client and I am not sure of my Kotlin implementation. UserAPI and ProfileAPI are just interfaces.

public class RetrofitService {

private static final String BASE_URL = "https://test.api.com/";
private ProfileAPI profileAPI;
private UserAPI userAPI;
private static RetrofitService INSTANCE;

/**
 * Method that returns the instance
 * @return
 */
public static RetrofitService getInstance() {
    if (INSTANCE == null) {
        INSTANCE = new RetrofitService();
    }
    return INSTANCE;
}

private RetrofitService() {
    Retrofit mRetrofit = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl(BASE_URL)
            .build();
    profileAPI = mRetrofit.create(ProfileAPI.class);
    UserAPI = mRetrofit.create(UserAPI.class);
}

/**
 * Method that returns the API
 * @return
 */
public ProfileAPI getProfileApi() {
    return profileAPI;
}

/**
 * Method that returns the API
 * @return
 */
public UserAPI getUserApi() {
    return userAPI;
}

}

And this is my Kotlin implementation. As I understand this, the init block will be executed first when the class is instantiated.

class RetrofitService private constructor() {
/**
 * Method that returns the API
 * @return
 */
private val profileApi: ProfileAPI
private val userAPI: UserAPI

companion object {
    private const val BASE_URL = "https://test.api.com/"
    private var INSTANCE: RetrofitService? = null

    /**
     * Method that returns the instance
     * @return
     */
    fun getInstance(): RetrofitService? {
        if (INSTANCE == null) {
            INSTANCE = RetrofitService()
        }
        return INSTANCE
    }
}

init {
    val mRetrofit = Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl(BASE_URL)
            .build()
    profileApi = mRetrofit.create(ProfileAPI::class.java)
    UserAPI = mRetrofit.create(UserAPI::class.java)
}
}

But something tells me this is not the right way or it can be done better. Is there anything I can improve here?

UPDATE!!!

Based on comments and answer I have this implementation now

object RetrofitService {
private const val BASE_URL = "https://test.api.com"

private fun retrofitService(): Retrofit {
    return Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .baseUrl(BASE_URL)
            .build()
}

val profileApi: ProfileAPI by lazy {
    retrofitService().create(ProfileAPI::class.java)
}

val userApi: UserAPI by lazy {
    retrofitService().create(UserAPI::class.java)
}
}

Then I would use it like this

RetrofitService.profileApi

Would that be alright?

user6920323
  • 141
  • 1
  • 5

2 Answers2

15

You could use something like:

object MyApi {

    private const val BASE_URL = " https://www.MYAPI.com/"

    private val moshi = Moshi.Builder()
        .add(KotlinJsonAdapterFactory())
        .build()

    private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .addCallAdapterFactory(CoroutineCallAdapterFactory())
        .client(clientBuilder.build())
        .baseUrl(BASE_URL)
        .build()

    val retrofitService: MyApiService by lazy {
        retrofit().create(MyApiService::class.java)
    }

    //If you want more service just add more val such as
    val otherService: MyOtherService by lazy {
        retrofit().create(MyOtherService::class.java
    }

}

//To use it you just need to do:
MyApi.retrofitService
MyApi.otherService

  • object ClassName is a singleton it'll only instance it once and reuse for next call
  • by lazy using this keyword, your retrofitService will only be initialised the first time you call and then you'll reuse that same value, for more details look here
Biscuit
  • 4,840
  • 4
  • 26
  • 54
  • Thanks @Biscuit . I updated my question with your suggestions. Does it look better now? – user6920323 May 11 '20 at 13:31
  • Yes you're update is totally fine and that's how I could do it as well , I've added it in my answer – Biscuit May 11 '20 at 14:37
  • Won't there be new instance of retrofit for every service? We are calling retrofit() for every initialization of services – Rinat Diushenov Sep 04 '20 at 16:08
  • Yes indeed, a new instance will be created for each service, so for my example only 2 retrofit object will be created, you can probably change the function `retrofit()` to a `val retrofit: Retrofit by lazy` to fix this – Biscuit Sep 08 '20 at 14:52
  • How should I pass my API user and password before I use this object? – Kamil Oct 15 '22 at 12:39
4
//try to adapt this code sample to your code

object RetrofitConfig {
// use lazy to insure that only one instance of retrofit will be used - no duplication
private val retrofit : Retrofit by lazy {
    Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .baseUrl("put_your_url_here")
        .build()
}

// here put your services interface if you have more
val movieService : MovieService by lazy {
    retrofit.create(MovieService::class.java)
}

val showService : ShowService by lazy {
    retrofit.create(ShowService::class.java)
}
}
Wassim Ben Hssen
  • 519
  • 6
  • 23