Search code examples
javaandroidkotlinsingletonretrofit2

Retrofit singleton in Kotlin


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?


Solution

  • 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