8

The Code A is from the project play-billing-samples, you can see it.

I don't know why the author design localCacheBillingClient as lateinit, it cause the code is little complex, if (::localCacheBillingClient.isInitialized == false) {...} are invoked many times.

I think Code B can works well, right?

Code A

class BillingRepository private constructor(private val application: Application) :
        PurchasesUpdatedListener, BillingClientStateListener {

     lateinit private var localCacheBillingClient: LocalBillingDb


    val subsSkuDetailsListLiveData: LiveData<List<AugmentedSkuDetails>> by lazy {
        if (::localCacheBillingClient.isInitialized == false) {
            localCacheBillingClient = LocalBillingDb.getInstance(application)
        }
        localCacheBillingClient.skuDetailsDao().getSubscriptionSkuDetails()
    }

    val inappSkuDetailsListLiveData: LiveData<List<AugmentedSkuDetails>> by lazy {
        if (::localCacheBillingClient.isInitialized == false) {
            localCacheBillingClient = LocalBillingDb.getInstance(application)
        }
        localCacheBillingClient.skuDetailsDao().getInappSkuDetails()
    }

   fun startDataSourceConnections() {
        Log.d(LOG_TAG, "startDataSourceConnections")
        instantiateAndConnectToPlayBillingService()
        localCacheBillingClient = LocalBillingDb.getInstance(application)
    }
    ...
}

Code B

class BillingRepository private constructor(private val application: Application) :
        PurchasesUpdatedListener, BillingClientStateListener {

    private val localCacheBillingClient: LocalBillingDb by lazy {
         LocalBillingDb.getInstance(application)
    }

    val subsSkuDetailsListLiveData: LiveData<List<AugmentedSkuDetails>> by lazy {
        localCacheBillingClient.skuDetailsDao().getSubscriptionSkuDetails()
    }


    val inappSkuDetailsListLiveData: LiveData<List<AugmentedSkuDetails>> by lazy {
        localCacheBillingClient.skuDetailsDao().getInappSkuDetails()
    }

   fun startDataSourceConnections() {
        Log.d(LOG_TAG, "startDataSourceConnections")
        instantiateAndConnectToPlayBillingService()        
    }
    ...
}
palaѕн
  • 72,112
  • 17
  • 116
  • 136
HelloCW
  • 843
  • 22
  • 125
  • 310
  • 1
    Don't know man, why author liked to use the lateinit and reflections. I'd go with the lazy property as well. Use lateinit only for the external initialization only. https://stackoverflow.com/q/36623177/11377112 – Animesh Sahu May 16 '20 at 03:34
  • 1
    You can only guess why the person who wrote that code chose that approach. I personally agree that it misuses `lateinit`, but that's my opinion. – Egor May 16 '20 at 04:38

1 Answers1

1

Kotlin - Property initialization using "by lazy" vs. "lateinit"

I believe the answer lies in this thread and precisely below point.

lateinit var can be initialized from anywhere the object is seen from, e.g. from inside a framework code, and multiple initialization scenarios are possible for different objects of a single class. by lazy { ... }, in turn, defines the only initializer for the property, which can be altered only by overriding the property in a subclass. If you want your property to be initialized from outside in a way probably unknown beforehand, use lateinit.

Humble Bee
  • 1,130
  • 2
  • 12
  • 19
  • 1
    You missed the real one - the last point made in that answer is really the reason to avoid using `lazy` and prefer `lateinit` as the author did in code `A`. Basically in code `B`, if the `localCacheBillingClient` is never used, it will retain a reference to `application` and thus cause memory leaks when the application is killed. – smac89 May 25 '20 at 03:11