"Lateinit property `app` has not been initialized" Error after Hilt Migration
I'm migrating our codebase from Dagger 2 to Hilt. I'm kind of a noob. I've gotten everything to build and compile. There is a crasher on app launch with this error:
Logcat
2022-07-27 11:31:26.297 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.appname.company, PID: 17599
java.lang.RuntimeException: Unable to create application com.appname.BaseApplication: kotlin.UninitializedPropertyAccessException: lateinit property app has not been initialized
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6767)
at android.app.ActivityThread.access$1500(ActivityThread.java:256)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2091)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7870)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property app has not been initialized
at com.appname.BaseApplication$Companion.getApp(BaseApplication.kt:746)
at com.appname.BaseApplication$Companion$sharedPrefs$2.invoke(BaseApplication.kt:839)
at com.appname.BaseApplication$Companion$sharedPrefs$2.invoke(BaseApplication.kt:838)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at com.appname.BaseApplication$Companion.getSharedPrefs(BaseApplication.kt:838)
at com.appname.BaseApplication$Companion.getOverrideUrl(BaseApplication.kt:894)
at com.appname.BaseApplication$Companion.getBaseUrl(BaseApplication.kt:908)
at com.appname.db.network.NetworkService.<init>(NetworkService.kt:47)
at com.appname.DaggerBaseApplication_HiltComponents_SingletonC$Builder.build(DaggerBaseApplication_HiltComponents_SingletonC.java:308)
at com.appname.Hilt_BaseApplication$1.get(Hilt_BaseApplication.java:22)
at dagger.hilt.android.internal.managers.ApplicationComponentManager.generatedComponent(ApplicationComponentManager.java:40)
at com.appname.Hilt_BaseApplication.generatedComponent(Hilt_BaseApplication.java:33)
at com.appname.Hilt_BaseApplication.onCreate(Hilt_BaseApplication.java:41)
at com.appname.BaseApplication.onCreate(BaseApplication.kt:182)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1223)
The property app
in BaseApplication is referenced all over the codebase.
BaseApplication.kt
@HiltAndroidApp
open class BaseApplication : Application() {
....
// (Injections in case they are relevant) //
@Inject
lateinit var hsAnalytics: HsAnalytics
....
@Inject @field:Named(NetworkService.Keys.GRAPH_QL_OKHTTP_CLIENT)
lateinit var okHttpClient: OkHttpClient
@Inject
lateinit var apolloClient: ApolloClient
....
// onCreate()
override fun onCreate() {
super.onCreate()
// Assign static "app" variable before ANYTHING else. <- This note was in the code base from an old dev that is no longer around
app = this
....
}
....
companion object {
....
// TODO: Should consider whether or not these should be static <- Notes from old dev who isn't around anymore
//<editor-fold desc="Utility methods to help non-context classes retrieve context-related objects">
@JvmStatic
lateinit var app: BaseApplication
private set
....
}
}
AppModule.kt
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Singleton
@Provides
fun provideApplication(@ApplicationContext application: BaseApplication): BaseApplication {
return application as BaseApplication
}
@Provides
@Singleton
internal fun provideContext(application: BaseApplication): Context = application
}
Any idea why app
suddenly isn't being instantiated?
Update - NetworkService.kt
@InstallIn(SingletonComponent::class)
@Module
class NetworkService @Inject constructor() {
object Keys {
const val GRAPH_QL_OKHTTP_CLIENT = "graph_ql"
const val ACTIVITY_FEED_RETROFIT = "activity_feed_retrofit"
const val ACTIVITY_FEED_OKHTTP = "activity_feed_client"
}
private val baseUrl: String = BaseApplication.baseUrl
private val feedBaseUrl: String = BaseApplication.feedBaseUrl
lateinit var api: NetworkServiceAPI
lateinit var feedApi: FeedNetworkServiceAPI
lateinit var harInterceptor: HarInterceptor
@Provides
@Singleton
internal fun provideAPI(retrofit: Retrofit): NetworkServiceAPI {
return retrofit.create(NetworkServiceAPI::class.java)
}
@Provides
@Singleton
internal fun provideFeedAPI(@Named(Keys.ACTIVITY_FEED_RETROFIT) retrofit: Retrofit): FeedNetworkServiceAPI {
return retrofit.create(FeedNetworkServiceAPI::class.java)
}
@Provides
@Singleton
internal fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit {
val contentType = "application/json".toMediaType()
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(defaultJson.asConverterFactory(contentType))
// .addConverterFactory(GsonConverterFactory.create(gson))
.client(okHttpClient)
.build()
}
@Provides
@Singleton
@Named(Keys.ACTIVITY_FEED_RETROFIT)
internal fun provideFeedRetrofit(@Named(Keys.ACTIVITY_FEED_OKHTTP) okHttpClient: OkHttpClient): Retrofit {
val contentType = "application/json".toMediaType()
val converter = defaultJson.asConverterFactory(contentType)
return Retrofit.Builder()
.baseUrl(feedBaseUrl)
.addConverterFactory(converter)
.client(okHttpClient)
.build()
}
@Provides
@Singleton
@Inject
internal fun provideClient(chuckerInterceptor: ChuckerInterceptor,
harInterceptor: HarInterceptor): OkHttpClient {
val builder = OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addCertificateTransparencyInterceptor()
builder.addInterceptor(object : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val url = chain.request().url.toString()
Timber.d("Calling URL: $url")
val requestBuilder = chain.request().newBuilder()
// Add every default header to all connections that are made
val headers = Brand.getInstance().getHttpHeaders(url)
for ((key, value) in headers) {
requestBuilder.addHeaderRemovingSymbols(key, value)
}
return chain.proceed(requestBuilder.build())
}
})
.addNetworkLoggingInterceptor(chuckerInterceptor)
.addInterceptor(harInterceptor)
if (DEBUGGABLE) {
// In Debug, log network requests
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BASIC
builder.addInterceptor(logging)
.addInterceptor(CurlInterceptor { message -> Timber.d(message) })
}
return builder.build()
}
@Provides
@Singleton
@Inject
@Named(Keys.ACTIVITY_FEED_OKHTTP)
internal fun provideActivityFeedClient(
chuckerInterceptor: ChuckerInterceptor,
harInterceptor: HarInterceptor): OkHttpClient {
val builder = OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addCertificateTransparencyInterceptor()
builder.addInterceptor(object : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val url = chain.request().url.toString()
Timber.d("Calling URL: $url")
val requestBuilder = chain.request().newBuilder()
// Add every default header to all connections that are made
val headers = Brand.getInstance().getHttpHeaders(url)
val apiToken = BaseApplication.sharedPrefs.getString(AppConstants.API_TOKEN, null)
apiToken?.let { headers["Api-key"] = it }
headers.forEach { (key, value) ->
requestBuilder.addHeaderRemovingSymbols(key, value)
}
return chain.proceed(requestBuilder.build())
}
})
.addNetworkLoggingInterceptor(chuckerInterceptor)
.addNetworkInterceptor(harInterceptor)
if (DEBUGGABLE) {
// In Debug, log network requests
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BASIC
builder.addInterceptor(logging)
.addInterceptor(CurlInterceptor { message -> Timber.d(message) })
}
return builder.build()
}
@Provides
@Singleton
@Inject
@Named(Keys.GRAPH_QL_OKHTTP_CLIENT)
internal fun provideGraphQlClient(
chuckerInterceptor: ChuckerInterceptor,
harInterceptor: HarInterceptor): OkHttpClient {
val builder = OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.addCertificateTransparencyInterceptor()
builder.addInterceptor(object : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val url = chain.request().url.toString()
Timber.d("Calling URL: $url")
val requestBuilder = chain.request().newBuilder()
// Add every default header to all connections that are made
val headers = Brand.getInstance().getHttpHeaders(url)
val useSuperToken = false
val superUserToken = BaseApplication.appResources.getString(R.string.activity_feed_super_user_token)
val apiToken = if (useSuperToken && DEBUGGABLE && superUserToken.isNotEmpty()) {
superUserToken
} else {
BaseApplication.sharedPrefs.getString(AppConstants.API_TOKEN, null)
}
if (apiToken != null) headers["Authorization"] = "Bearer $apiToken"
headers.forEach { (key, value) ->
requestBuilder.addHeaderRemovingSymbols(key, value)
}
return chain.proceed(requestBuilder.build())
}
})
.addNetworkLoggingInterceptor(chuckerInterceptor)
.addNetworkInterceptor(harInterceptor)
if (DEBUGGABLE) {
// In Debug, log network requests
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BASIC
builder.addInterceptor(logging)
.addInterceptor(CurlInterceptor { message -> Timber.d(message) })
}
return builder.build()
}
private fun OkHttpClient.Builder.addCertificateTransparencyInterceptor(): OkHttpClient.Builder = apply {
addNetworkInterceptor(certificateTransparencyInterceptor())
}
private fun OkHttpClient.Builder.addNetworkLoggingInterceptor(interceptor: ChuckerInterceptor): OkHttpClient.Builder = apply {
if (BaseApplication.sharedPrefs.getBoolean(AppConstants.DEBUG_ENABLE_NETWORK_LOGGING, false)) {
addNetworkInterceptor(interceptor)
}
}
@Provides
@Singleton
@Inject
fun providesNetworkLoggingInterceptor(@ApplicationContext context: Context): ChuckerInterceptor {
return ChuckerInterceptor(context)
}
@Provides
@Singleton
@Inject
fun providesHarInterceptor(@ApplicationContext context: Context): HarInterceptor {
return HarInterceptor(context)
}
@Provides
@Singleton
@Inject
fun providesApolloClient(@Named(Keys.GRAPH_QL_OKHTTP_CLIENT) okHttpClient: OkHttpClient): ApolloClient {
val apiUrl: String = BaseApplication.apiBaseUrl
return ApolloClient.builder().serverUrl("$apiUrl/graphql")
.okHttpClient(okHttpClient).build()
}
@Provides
@Singleton
internal fun provideGson(): Gson {
return GsonBuilder()
.setLenient()
.serializeNulls()
.create()
}
@Provides
@Singleton
@Inject
fun providesNetworkService(api: NetworkServiceAPI,
feedApi: FeedNetworkServiceAPI,
harInterceptor: HarInterceptor): NetworkService {
this.api = api
this.feedApi = feedApi
this.harInterceptor = harInterceptor
return this
}
companion object {
val defaultJson = Json {
encodeDefaults = true
ignoreUnknownKeys = true
isLenient = true
allowSpecialFloatingPointValues = false
}
}
}
fun Request.Builder.addHeaderRemovingSymbols(key: String, value: String?): Request.Builder {
Timber.d("Header: $key : $value")
value?.let { return addHeader(key, it.replace(Regex("[^\\u0020-\\u007e‘’]"), "")) }
return this
}
Comments
Post a Comment