0

I have a Retrofit Kotlin singleton where I need a context to access the cacheDir, what would be the best current approach to solve this?:

import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import okhttp3.Cache
import okhttp3.CacheControl
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import java.io.File
import java.util.concurrent.TimeUnit

private const val BASE_URL = "https://5c5c8ba5345018a0014aa1b24.mockapi.io/api/test"

/**
 * Build the Moshi object that Retrofit will be using, making sure to add the Kotlin adapter for
 * full Kotlin compatibility.
 */
private val moshi: Moshi = Moshi.Builder()
        .add(KotlinJsonAdapterFactory())
        .build()

// Create a cache object
const val cacheSize = 1 * 1024 * 1024 // 1 MB
val httpCacheDirectory = File(cacheDir, "http-cache")
val cache = Cache(httpCacheDirectory, cacheSize.toLong())

// create a network cache interceptor, setting the max age to 1 minute
private val networkCacheInterceptor = Interceptor { chain ->
    val response = chain.proceed(chain.request())

    val cacheControl = CacheControl.Builder()
        .maxAge(1, TimeUnit.MINUTES)
        .build()

    response.newBuilder()
        .header("Cache-Control", cacheControl.toString())
        .build()
}

// Create the logging interceptor
private val loggingInterceptor = HttpLoggingInterceptor()
    .setLevel(HttpLoggingInterceptor.Level.BODY)

// Create the httpClient, configure it
// with cache, network cache interceptor and logging interceptor
// TODO: don't use loggingInterceptor in release build.
private val httpClient = OkHttpClient.Builder()
    .cache(cache)
    .addNetworkInterceptor(networkCacheInterceptor)
    .addInterceptor(loggingInterceptor)
    .build()

// Create the Retrofit with the httpClient
private val retrofit = Retrofit.Builder()
    .baseUrl("http://localhost/")
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .client(httpClient)
    .build()

/**
 * A public interface that exposes the [getWeatherForecasts] method
 */
interface WeatherForecastsApiService {
    @GET(BASE_URL)
    suspend fun getWeatherForecasts(): List<WeatherForecastEntity>
}

/**
 * A public Api object that exposes the lazy-initialized Retrofit service.
 */
object WeatherForecastApi {
    val RETROFIT_SERVICE : WeatherForecastsApiService by lazy {
        retrofit.create(WeatherForecastsApiService::class.java)
    }
}
David
  • 3,971
  • 1
  • 26
  • 65
  • If you use DI you can use @ApplicationContext https://stackoverflow.com/questions/63072927/how-to-inject-application-context-in-a-repository-with-hilt – Evyatar Cohen Jun 19 '22 at 08:32
  • I'm wondering if that wouldn't be an overkill for my case where I'm not using DI yet, what does DI do what I cannot do manually in this particular case? – David Jun 19 '22 at 08:50
  • If you haven't used DI yet, I highly encourage you to do so and learn it, it makes classes more independent of their dependencies, It lays a groundwork for good app architecture and simplifies injecting context and other "variables" witout unnecessary boilerplate code (and makes testing easier!) – JustSightseeing Jun 19 '22 at 10:08
  • but if you don't want to learn about DI yet, take a look [here](https://stackoverflow.com/questions/2002288/static-way-to-get-context-in-android) – JustSightseeing Jun 19 '22 at 10:14
  • Thanks, yes, I found that one and used that simple solution for now because this is only a small demo app I'm building, I'm aware of DI for bigger projects though. – David Jun 19 '22 at 10:33

1 Answers1

0
  1. Simply store a static instance of your application class in it an access it where you want -> not ideal solution but it works* Source

open class MyApplication : Application() {

override fun onCreate() {
    super.onCreate()
    instance = this
}

companion object {
    lateinit var instance: MyApplication
        private set
    fun getApplicationContext(): Context? {
        return instance.applicationContext
    }
}

}

Use it like this:

MyApplication.instance.cacheDir()

*you would introduce a direct dependency between your components and your project's Application class, making it impossible to use these components with another Application class and impossible to declare these components inside a separate gradle module for example. - LINK

  1. Using DI for example with Hilt -> to much overhead for small projects?
  2. Custom Kotlin singleton implementation -> readability and complexity drawback.
David
  • 3,971
  • 1
  • 26
  • 65