Should we unregister Flow collection in Fragment/Activity? - android-lifecycle

In the Android official documentation, it says the following about StateFlow vs Livedata:
LiveData.observe() automatically unregisters the consumer when the view goes to the STOPPED state, whereas collecting from a StateFlow or any other flow does not.
and they recommend to cancel eacy flow collection like this:
// Coroutine listening for UI states
private var uiStateJob: Job? = null
override fun onStart() {
super.onStart()
// Start collecting when the View is visible
uiStateJob = lifecycleScope.launch {
latestNewsViewModel.uiState.collect { uiState -> ... }
}
}
override fun onStop() {
// Stop collecting when the View goes to the background
uiStateJob?.cancel()
super.onStop()
}
As far as I know about structured concurrency, when we cancel the parent job, all children jobs are cancelled automatically and flow terminal operator collect should not be an exception. In this case, we are using lifecycleScope that should cancel when the lifecycleOwner is destroyed. Then, why do we need to manually cancel flow collection in this case? what am I missing here?

When using lifecycleScope you do not need to cancel the jobs or scope yourself, as this scope is bound to the LifecycleOwner's lifecycle and gets cancelled when the lifecycle is detroyed. The flow is then cancelled too.
It changes as soon you are using your own CoroutineScope:
val coroutineScope = CoroutineScope(Dispatchers.Main)
override fun onCreate() {
// ...
coroutineScope.launch {
latestNewsViewModel.uiState.collect { uiState -> ... }
}
}
override fun onDestroy() {
// ...
coroutineScope.coroutineContext.cancelChildren()
}
In that case you would need to take care of cancelling the scope (or job's) yourself.

According to #ChristianB response, we don't need to cancel coroutines that are attached to a lifecycle. the collect() is running inside a coroutine and will cancel too, when coroutines cancels.
Then, why do we need to manually cancel flow collection in this case?
what am I missing here?
When we want to use coroutines with a custom lifecycle (not using fragment/activity lifecycle). For example, I've created a helper class that needed to do sth inside itself. It launches a coroutine and it was managing (cancel/join...) its job.

Related

Is using Task.detached a best practice and correct approach, to make sure the Task is executed in non-main thread?

Currently, I have the following code
class ShopViewController: UIViewController {
#IBAction func buy(_ sender: Any) {
Task {
// Will run in main thread, because ShopViewController is
// marked as #MainActor
let success = await Store.INSTANCE.purchase(selectedShop)
}
}
I want the Task executes in non-main thread, I refactor the code to the following
class ShopViewController: UIViewController {
#IBAction func buy(_ sender: Any) {
Task.detached { [weak self] in
// Will run in non main thread.
guard let self = self else { return }
let success = await Store.INSTANCE.purchase(self.selectedShop)
}
}
Now, the Task runs in non-main thread.
But, I was wondering, is using Task.detached a best practice and correct approach, to make sure the Task is executed in non-main thread?
There are a few considerations:
Which actor should this Task use?
Using the main actor with Task { … } is fine. As soon as it hits the await keyword, it will suspend execution on the main actor, free it to go do other stuff, and await the results from purchase. In fact, in practice, you are probably doing something with the success result (e.g. updating the UI), so you probably want to use Task { … } to keep it on the current actor.
Which actor should purchase use?
If Store.INSTANCE.purchase requires a particular actor, it would define which actor it wants to use, not the caller. It might be an actor type. It might be decorated with #MainActor. This is dictated by how Store.INSTANCE and purchase are defined. The actor used by buy(_:) is largely immaterial.
Does it matter which actor purchase uses?
You say that purchase is primarily performing network i/o. In that case, it doesn’t matter if it is on the main actor or not. If it was doing something slow and synchronous (e.g., parsing some exceptionally large responses, image processing, etc.), when maybe you would want to explicitly push those tasks off of the current actor, but otherwise it doesn’t matter.
Bottom line, the use of Task.detached is technically permissible, but generally would not be used in this case.

SingleLiveEvent post, if called multiple times, then only the last event is dispatched (but I need all events in my view)

I'm using SingleLiveEvent to communicate my ViewModel and my Activity. Something like that (pseudocode):
class MyActivity: BaseActivity{
fun onCreate(){
//Init viewmodel and so on
viewModel.commands.observe(this, { command ->
logger.debug("Command received","------>>>>>>>"+command.javaClass.simpleName)
processCommand(command)
})
}
}
And my ViewModel is something like:
class MyViewModel(application: Application) : BaseAndroidViewModel(application) {
val command: SingleLiveEvent<CustomCommands> = SingleLiveEvent()
init{
loadOneThing()
command.postValue(CustomCommands.MessageCommand("one thing loaded"))
loadAnotherThing()
command.postValue(CustomCommands.MessageCommand("another thing loaded"))
}
}
The problem that I'm having, is that the Activity is receiving only the last command, and that is per design. SingleLiveEvent is a Child class from LiveData, and the documentation says the following for the method postValue:
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
Interestingly, if I set a breakpoint on the line that posts the commands, the emulator/device/main thread has time enough to process the first command, and the second command is sent too. But when executing the app without breakpoints, if the tasks that the viewmodel does between commands are done very fast (no rest requests or things like that, but some calculations), the main thread does not have time enough to finish the first command, and the second command is ignored.
But I really need the View to receive all events/commands that the ViewModel sends.
I suppose the SingleLiveEvent is not the right tool for that use case, nor is LiveData, because of the problem of already consumed events being resent when the device is rotated and so on.
Somebody knows a better approach to do this?
Thanks in advance!
I have faced same problem today. I'm also using SingleLiveEvent for commands/event. I have solved this problem using
commands.value = event instead of commands.postValue(event). Then I wonder why it behaving like that. I found this article. In the article,
But for postValue, the value will be updated twice and the number of times the observers will receive the notification depends on the execution of the main thread. For example, if the postValue is called 4 times before the execution of the main thread, then the observer will receive the notification only once and that too with the latest updated data because the notification to be sent is scheduled to be executed on the main thread. So, if you are calling the postValue method a number of times before the execution of the main thread, then the value that is passed lastly i.e. the latest value will be dispatched to the main thread and rest of the values will be discarded.
I hope it help someone that faced same problem.
have you tried using EventObserver?
/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
*/
open class Event<out T>(private val content: T) {
#Suppress("MemberVisibilityCanBePrivate")
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
/**
* An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
* already been handled.
*
* [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
*/
class EventObserver<T>(private val onEventUnhandledContent: (T) -> Unit) : Observer<Event<T>> {
override fun onChanged(event: Event<T>?) {
event?.getContentIfNotHandled()?.let {
onEventUnhandledContent(it)
}
}
}
Use it with live data
val someEvent: MutableLiveData<Event<Unit>>= MutableLiveData()
when you need to some event
fun someEventOccured(){
someEvent.value = Event(Unit)
}
Fragment file, observe the Event
viewModel.someEvent.observe(this, EventObserver {
//somecode
})

how to use suspendCoroutine to turn java 7 future into kotlin suspending function

What is the best approach to wrap java 7 futures inside a kotlin suspend function?
Is there a way to convert a method returning Java 7 futures into a suspending function?
The process is pretty straightforward for arbitrary callbacks or java 8 completablefutures, as illustrated for example here:
* https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md#suspending-functions
In these cases, there is a hook that is triggered when the future is done, so it can be used to resume the continuation as soon as the value of the future is ready (or an exception is triggered).
Java 7 futures however don't expose a method that is invoked when the computation is over.
Converting a Java 7 future to a Java 8 completable future is not an option in my codebase.
Of course, i can create a suspend function that calls future.get() but this would be blocking, which breaks the overall purpose of using coroutine suspension.
Another option would be to submit a runnable to a new thread executor, and inside the runnable call future.get() and invoke a callback. This wrapper will make the code looks like "non-blocking" from the consumer point of view, the coroutine can suspend, but under the hood we are still writing blocking code and we are creating a new thread just for the sake of blocking it
Java 7 future is blocking. It is not designed for asynchronous APIs and does not provide any way to install a callback that is invoked when the future is complete. It means that there is no direct way to use suspendCoroutine with it, because suspendCoroutine is designed for use with asynchronous callback-using APIs.
However, if your code is, in fact, running under JDK 8 or a newer version, there are high chances that the actual Future instance that you have in your code happens to implement CompletionStage interface at run-time. You can try to cast it to CompletionStage and use ready-to-use CompletionStage.await extension from kotlinx-coroutines-jdk8 module of kotlinx.coroutines library.
Of course Roman is right that a Java Future does not let you provide a callback for when the work is done.
However, it does give you a way to check if the work is done, and if it is, then calling .get() won't block.
Luckily for us, we also have a cheap way to divert a thread to quickly do a poll check via coroutines.
Let's write that polling logic and also vend it as an extension method:
suspend fun <T> Future<T>.wait(): T {
while(!isDone)
delay(1) // or whatever you want your polling frequency to be
return get()
}
Then to use:
fun someBlockingWork(): Future<String> { ... }
suspend fun useWork() {
val result = someBlockingWork().wait()
println("Result: $result")
}
So we have millisecond-response time to our Futures completing without using any extra threads.
And of course you'll want to add some upper bound to use as a timeout so you don't end up waiting forever. In that case, we can update the code just a little:
suspend fun <T> Future<T>.wait(timeoutMs: Int = 60000): T? {
val start = System.currentTimeMillis()
while (!isDone) {
if (System.currentTimeMillis() - start > timeoutMs)
return null
delay(1)
}
return get()
}
You should be now be able to do this by creating another coroutine in the same scope that cancels the Future when the coroutine is cancelled.
withContext(Dispatchers.IO) {
val future = getSomeFuture()
coroutineScope {
val cancelJob = launch {
suspendCancellableCoroutine<Unit> { cont ->
cont.invokeOnCancellation {
future.cancel(true)
}
}
}
future.get().also {
cancelJob.cancel()
}
}
}

Is it possible to determine when iOS widget hides?

Is there any way to catch the moment when a user hides the notification panel with a widget? I want to save some information into the database at that moment (I want it to be similar to applicationDidEnterBackground:). Any other ideas about how to save data at the last moment would also be appreciated.
Usually, your widget would be a UIViewController instance, conforming to the NCWidgetProviding protocol.
That means, that you can take advantage of UIViewController's functionality and execute your code in
- (void)viewWillDisappear:(BOOL)animated;
or
- (void)viewDidDisappear:(BOOL)animated;
I tested it and it worked.
#Andrew is correct that the normal UIViewController lifecycle methods will be called when your widget goes off screen, but your controller will also be deallocated shortly thereafter, and its process suspended. So if you need to do some I/O, you have no guarantee it will complete.
The recommended way to keep your extension's process alive is to request a task assertion using performExpiringActivityWithReason:usingBlock:.
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSProcessInfo.processInfo().performExpiringActivityWithReason("because", usingBlock: { (expired) -> Void in
if expired {
NSLog("expired")
} else {
// save state off to database
}
})
}

where to use delegates in c#

i have better understanding in how the delegates and events work , but i dont know
where it is of full use when we develop a Library or a application.
thanks
Delegates are significant as they are needed to work with events (not just this).
Lets say we have a winform F1..A function within F1 opens another form F2. and if a function within F1 has to be executed based on users activity on F2 we'll definitely have to make use of delegates and events.
1)A delegate type has to be declared (Signature has to match with eventhandler which you are planning to attach)
2)F2 will need to have an event as class member of delegate type
3)Just as you create an instance of F2 in F1 , attach the event handler (using += )
Delegates are similar to function pointers of C/C++ but are of class type and not primitive type.
The basic use of delegates is to perform some call back or asynchronous methods or to pass different methods at runtime. (substituting the logic)
Example for performing asynchronous call back. It is more appropriate when you have multiple threads running.
public class MyClass
{
public delegate void CallBackDelegate(int result);
public void CallsBack(CallBackDelegate callBack)
{
// your code here - do something useful
callBack(result);
}
}
public class DelegateExample
{
// some more useful code here
void ExampleMethod()
{
// more code
MyClass myclass = new MyClass();
myclass.CallsBack(CallMeBack);
// Continue with your work and don't wait for call back.
}
void CallMeBack(int result)
{
// do something useful
}
}
This is a very general question, but a typical use of delegates and events would be in a GUI library. For example, you could set up events to fire when a button is clicked on an application, or when a value is changed in a field, and a developer could then use delegates to specify one of their own functions to be called in response to that button click, or to perform validation on the changed data.

Resources