Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 848
user6920323 Avatar asked May 11 '20 12:05

user6920323


People also ask

Is Kotlin lazy singleton?

Kotlin intentionally avoids the confusion people have with singletons in Java. And avoids the "wrong versions" of this pattern -- of which there are many. It instead provides the simpler and the safest form of singletons. Given the use of lazy() , if you have other members each would individually be lazy.

What are singletons in Kotlin?

Using Singletons in Kotlin. By using the keyword object in your app, you're defining a singleton. A singleton is a design pattern in which a given class has only one single instance inside the entire app.

Is Kotlin object singleton thread safe?

Deal with multiple threads(singleton thread safe) a Kotlin object actually relies on a Java static initialization block. t enables thread-safe lazy initialization without having to rely on a locking algorithm like the complex double-checked locking.

Can Kotlin object have constructor?

ConstructorsA class in Kotlin can have a primary constructor and one or more secondary constructors. The primary constructor is a part of the class header, and it goes after the class name and optional type parameters.


1 Answers

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 fun retrofit(): Retrofit {
        return 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
like image 191
Biscuit Avatar answered Sep 23 '22 10:09

Biscuit