I wrote this little snippet: ``` suspend fun l...
# android
r
I wrote this little snippet:
Copy code
suspend fun loudSignIn(): GoogleSignInAccount {
        val client = GoogleSignIn.getClient(this, gso)
        val contract = ActivityResultContracts.StartActivityForResult()
        return suspendCoroutine { cont ->
            registerForActivityResult(
                contract,
                contract.createIntent(this, client.signInIntent)
            ) { activityResult ->
                cont.resume(
                    GoogleSignIn.getSignedInAccountFromIntent(activityResult.data).asDeferred()
                )
            }.launch()
        }.await()
    }
But the Android API is WTF here that I'm getting a
Copy code
is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
What's the correct magic incantation here?
k
Copy code
registerForActivityResult
must be done before activity is resumed inside on create
i
This is never, ever going to work, as explained in the documentation: https://developer.android.com/training/basics/intents/result#register
When starting an activity for a result, it is possible—and, in cases of memory-intensive operations such as camera usage, almost certain—that your process and your activity will be destroyed due to low memory.
For this reason, the Activity Result APIs decouple the result callback from the place in your code where you launch the other activity. Because the result callback needs to be available when your process and activity are recreated, the callback must be unconditionally registered every time your activity is created, even if the logic of launching the other activity only happens based on user input or other business logic.
Your activity can and will be destroyed between when you register (which, like it says, always need to be done unconditionally as part of the creation of whatever object is getting your activity result) and when the result comes in. There's no way a suspend method will ever be able to survive across process death and recreation, so this won't ever be a viable, consistent way to get an activity result
If you want to move the handling of an activity result into a separate, testable class, that is possible and covered in the documentation: https://developer.android.com/training/basics/intents/result#separate
r
Huh, so my idea of registering a callback via channel isn't that best idea 😅
k
I personally have a somewhat helper class that whenever an activity is started I add the callbacks. But I also just have one Activity because there is no need for multiple activities (or fragments) with compose
c
It is a very bad design to couple different components with callbacks in almost all projects and languages than use event / messaging system. Even worse is that android encourage such mass. If Android was well designed, there should be at lease three event systems : First one is the system level event/messaging system that currently applies to receivers (but is every bad designed), secondly one is the application level messaging system for components with application to interact with, thirdly one is component level for view items to interact with. In another world, Android components should be event based, instead of observation based. The observer pattern can not solve coupling problem for complex system, but the chains of responsibility pattern can. Encouraging observer pattern in java ecosystem has lead java code to be very messy.
For some reason, the old way for registerForActivityResult is much better than the new way which requests a separate interface, while the old way carries data back to the invoking activity which is the right logic. The new one is some what misleading, because there can be multiple listeners with the parent activity.
And it is obviously the parent activity are responsible for dispatching the return data.
With the old way, the listeners / handlers are well organized.
i
Coupling any logic in your activity at all (rather than separate, testable components) is a bad idea, especially when it comes to manually multiplexing multiple callbacks from the same component like the old API forced you to do. I'd encourage you to read the guide on activity results again, taking particular notice of the sections on type safety, separating it out into separate classes, and the built in support for testing
c
If this api is well designed, why i should create this irrelevant object?
new ActivityResultContracts.StartActivityForResult(),
i
If you actually read the type safety section, you'd find out exactly what the contract is for
c
why I should know the safety section just for lauching an activity?
i
These are the Activity Result APIs, they're for receiving a result
c
Should I know how build an engine before driving a car?
Activity Results are what user sent.
There are no safety concern.
If there are safety concern, the client programmers should never see then at all.
i
I think you missed a word - type safety is the term
As in, parsing the Intent into a usable type specific to the result you are expecting
c
There is nothing to do with type safety.
i
and creating the Intent for you based on strongly typed inputs
c
Why I need typed inputs?
even if I need typed inputs, I don't need this extra object.
You force me to put an irrelevant object in my code, that is called adding coupling.
i
It actually decouples the Intent parsing and creation from the rest of your code, ensuring that each part is testable separately. You've just chosen not to actually use any of that functionality to simplify your code
c
I don't think I can agree with you.🙂
every activity stopped should return data to its parent.
regardless if its parent is acitivity or not.
I don't want to mix test with api design either.
mix everything into an api but not focus on it's isolated, core, essential function, it is in fact coupling.