Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lateinit property context has not been initialized #5760

Closed
CoreFloDev opened this issue Mar 23, 2024 · 9 comments
Closed

lateinit property context has not been initialized #5760

CoreFloDev opened this issue Mar 23, 2024 · 9 comments

Comments

@CoreFloDev
Copy link
Contributor

Version

v4.0.0-beta.5

Summary

It started happening on v4.0.0-beta.5 previously, I was on v4.0.0-beta.2 without issues

Steps to reproduce the behavior

No response

Logs

FATAL EXCEPTION: main
    Process: com.pascap.connectedBody:sync, PID: 25941
    java.lang.RuntimeException: Unable to create service com.pascap.connectedBody.common.account.SyncService: kotlin.UninitializedPropertyAccessException: lateinit property context has not been initialized
    	at android.app.ActivityThread.handleCreateService(ActivityThread.java:5035)
    	at android.app.ActivityThread.access$1900(ActivityThread.java:315)
    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2305)
    	at android.os.Handler.dispatchMessage(Handler.java:106)
    	at android.os.Looper.loopOnce(Looper.java:226)
    	at android.os.Looper.loop(Looper.java:313)
    	at android.app.ActivityThread.main(ActivityThread.java:8751)
    	at java.lang.reflect.Method.invoke(Native Method)
    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
    Caused by: kotlin.UninitializedPropertyAccessException: lateinit property context has not been initialized
    	at com.apollographql.apollo3.ApolloInitializer$Companion.getContext$apollo_runtime_debug(ApolloInitializer.kt:17)
    	at com.apollographql.apollo3.network.ConnectivityManager_androidKt.platformConnectivityManager(ConnectivityManager.android.kt:67)
    	at com.apollographql.apollo3.network.NetworkMonitorKt.NetworkMonitor(NetworkMonitor.kt:18)
    	at com.apollographql.apollo3.ApolloClient.<init>(ApolloClient.kt:71)
    	at com.apollographql.apollo3.ApolloClient.<init>(Unknown Source:0)
    	at com.apollographql.apollo3.ApolloClient$Builder.build(ApolloClient.kt:637)
    	at com.pascap.connectedBody.common.di.CommonModule.provideApolloClient(CommonModule.kt:155)
    	at com.pascap.connectedBody.common.di.InjectCommonComponent$apolloClient$1.invoke(InjectCommonComponent.kt:83)
    	at com.pascap.connectedBody.common.di.InjectCommonComponent$apolloClient$1.invoke(InjectCommonComponent.kt:82)
    	at me.tatarka.inject.internal.LazyMap.get(LazyMap.kt:16)
    	at com.pascap.connectedBody.common.di.InjectCommonComponent.getApolloClient(InjectCommonComponent.kt:82)
    	at com.pascap.connectedBody.common.di.InjectCommonComponent.access$getApolloClient(InjectCommonComponent.kt:65)
    	at com.pascap.connectedBody.common.di.InjectCommonComponent$userRepository$1.invoke(InjectCommonComponent.kt:255)
    	at com.pascap.connectedBody.common.di.InjectCommonComponent$userRepository$1.invoke(InjectCommonComponent.kt:253)
    	at me.tatarka.inject.internal.LazyMap.get(LazyMap.kt:16)
    	at com.pascap.connectedBody.common.di.InjectCommonComponent.getUserRepository(InjectCommonComponent.kt:253)
    	at com.pascap.connectedBody.common.account.InjectAuthenticationComponent$accountSync$1.invoke(InjectAuthenticationComponent.kt:48)
    	at com.pascap.connectedBody.common.account.InjectAuthenticationComponent$accountSync$1.invoke(InjectAuthenticationComponent.kt:45)
    	at me.tatarka.inject.internal.LazyMap.get(LazyMap.kt:16)
    	at com.pascap.connectedBody.common.account.InjectAuthenticationComponent.getAccountSync(InjectAuthenticationComponent.kt:45)
    	at com.pascap.connectedBody.common.account.SyncService.onCreate(SyncService.kt:13)
    	at android.app.ActivityThread.handleCreateService(ActivityThread.java:5022)
    	at android.app.ActivityThread.access$1900(ActivityThread.java:315) 
    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2305) 
    	at android.os.Handler.dispatchMessage(Handler.java:106) 
    	at android.os.Looper.loopOnce(Looper.java:226) 
    	at android.os.Looper.loop(Looper.java:313) 
    	at android.app.ActivityThread.main(ActivityThread.java:8751) 
    	at java.lang.reflect.Method.invoke(Native Method) 
    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) 
    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
@CoreFloDev
Copy link
Contributor Author

I tested with 4.0.0-beta.4 and the bug isn't happening

@martinbonnin
Copy link
Contributor

martinbonnin commented Mar 23, 2024

Hi 👋 Thanks for sending this and apologies about the crash. I'm assuming this is happening during unit tests?

The tldr; is that 4.0.0-beta.5 introduced NetworkMonitor in order to make it possible to:

  1. wait until network is available before retrying subscriptions
  2. fail fast if no network is available

In order to simplify the setup it uses androidx.startup to retrieve the current context but this fails during unit tests.

A preliminary fix is here. It should fix your immediate issue.

Until the next release, you can workaround by setting up a NoOpNetworkMonitor:

  val noOpNetworkMonitor = object: NetworkMonitor {
    override val isOnline: Boolean
      get() = true

    override suspend fun waitForNetwork() {}

    override fun close() {}
  }

  val apolloClient = ApolloClient.Builder()
      .serverUrl("https://...")
      .networkMonitor(noOpNetworkMonitor)
      .build()

Edit: another workaround is to depend on apollo-runtime-jvm instead of apollo-runtime

I'm also wondering if it would make sense to make this behaviour opt-in instead. It's more setup work but at least it's explicit. Any thoughts?

@martinbonnin martinbonnin pinned this issue Mar 23, 2024
@martinbonnin martinbonnin changed the title Crash when using apollo on an android service lateinit property context has not been initialized Mar 23, 2024
@CoreFloDev
Copy link
Contributor Author

But in that case the network is available, it doesn't make sense that it crash. I don't need androidx.startup so I don't add it as dependency. The code isn't running in a test environment, it is running on a device with access to network.

My application doesn't except the network to be available and I don't see the point to do more requests to get the same things that's not really environmental friendly... I would scrap that feature as your goal should be to be as efficient as possible

Your fix worked with 4.0.0-beta.5 Thanks 👍

@martinbonnin
Copy link
Contributor

To be 100% clear, no extra request is being done by default. The retry mechanism I was mentioning is 100% opt-in and I expect most usages to be for subscriptions:

 val apolloClient = ApolloClient.Builder()
     .retryOnError { it.operation is Subscription }
     .serverUrl("...")
     .build()

It crashes in your case because androidx.startup is not found and/or the initializer runs too late for some reason.

I'd say being network state aware can be more efficient as it reduces latency and can also reduce extra calls to the networking hardware (no need to try a call if we know in advance it's going to fail).

All in all, I'd keep the feature but agree with you the androidx.startup dependency is weird, I'll make this opt-in. Thank for the feedback!

@martinbonnin
Copy link
Contributor

PR to make NetworkMonitor opt-in and remove the androidx.startup is here. (Preliminary docs)

Let me know what you think!

@CoreFloDev
Copy link
Contributor Author

I still find annoying that this functionality is on the core of apollo, I would have put it on a separate module that users can add if they want

Have you checked how the android connectivity manager knows that internet is available?

@martinbonnin
Copy link
Contributor

I still find annoying that this functionality is on the core of apollo, I would have put it on a separate module that users can add if they want

Can you elaborate what the concerns are? Is it APK size? External dependencies? Something else?

Have you checked how the android connectivity manager knows that internet is available?

I didn't dig there and TBH I expect the details to be pretty complicated. Answering the question of "do I have network connectivity?" is probably one of the most difficult questions in the universe 😅

@CoreFloDev
Copy link
Contributor Author

My concerns is that feature is very dodgy and you probably don't want to have the apollo core to become dodgy

Well last time I check the connectivity manager was doing a network request to google.com to check if internet is available. That means that if for whatever reason google.com isn't available well your app doesn't work...

@martinbonnin
Copy link
Contributor

Hi!

If by dodgy you mean not well defined, I agree detecting network connectivity is not an easy problem. But it's an important one, especially for realtime and subscriptions use cases and having one option is better than none.

If by dodgy you mean there are privacy issues, I'm happy to revisit with more specifics and/or describe the tradeoff more explicitly in the docs.

That means that if for whatever reason google.com isn't available well your app doesn't work

To be clear, for this scenario to happen, the user would have to opt-in twice :

  • Opt-in ApolloClient.Builder.networkMonitor(NetworkMonitor(context))
  • Opt-in ApolloClient.Builder.failFastIfOffline(true)

I'll add more language to the KDoc but overall I think this is OK. No additional dependency is added. If you're building an Android App, the ConnectivityManager is already there as a system service. Also Coil and probably many other libs and apps have been using ConnectivityManager for a while.

I'll close this for now. If you or anyone else have more specifics (a link or article would be great) then please comment below and I'll reopen.
Thanks!

@martinbonnin martinbonnin closed this as not planned Won't fix, can't repro, duplicate, stale Mar 27, 2024
@martinbonnin martinbonnin unpinned this issue Apr 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants