Consider the Android app architecture that is recommended by Google: https://developer.android.com/jetpack/docs/guide
This architecture is based on ViewModels and Observables, therefore I would categorize it as "Model-View-ViewModel" (MVVM) architecture.
This is quite different to React-Redux. Whereas the Android architecture encourages bi-directional dataflows between Views and ViewModels, Redux enforces a uni-directional dataflow in a circle.
Moreover, the Android architecture keeps state in several ViewModels, whereas Redux enforces a centralized store.
Now my question is how to implement Redux with native Android libraries.
My first attempt would be:
Implement a centralized store as a Kotlin Singleton object.
The store provides a sendAction-method that takes an action, puts it in a queue and then returns quickly.
Actions are pure Kotlin data classes because they should not contain any logic.
Within the store, implement a Kotlin-Coroutine that picks up actions from the queue and dispatches them with a huge switch-statement.
Use a Room-database + some ephemeral state as a model. In particular, the ephemeral state controls which Fragment/Dialog is shown at any given time.
Ensure that the state is only mutated by the Coroutine.
Use observable livedata (androidx.lifecycle.LiveData) to re-render the UI whenever the Room-database or the ephemeral state changes.
Since observables are not enough to control an Android UI, I would also need a function that compares the current fragment/activity with the expected state, and e.g. trigger a FragmentManager transaction if a deviation is detected.
However, a few points are not clear:
How to keep activity/fragments in sync with the global state? Perhaps I would use a single activity and replace fragments as needed, depending on the current state of the store.
How can I implement both async + sequential action dispatching? Perhaps I would implement a single Kotlin-Coroutine that picks up incoming actions from the queue and dispatches them straight on the UI thread.
How can we ensure that the entire UI is re-rendered before new actions are dispatched? Perhaps I would stall the dispatching coroutine until there are no more other runnables in the queue of the UI thread?
You are right to say there are some limitations with MVVM.
Redux-style architecture tends to get called 'MVI' or 'Unidirectional' in Android.
There are some attempts at implementing this architecture in Android already that you can take a look at to help answer your questions:
MvRx
Mosby
In addition, this summary page
has a list of articles/libraries describing the approach.
Related
I have come across articles that recommend switching to StateFlow.
Like the one here.
Also in the new Android studio, StateFlow support is automatically included in the functionality of data binding, including the coroutines dependencies.
Live data is already in use in most of the apps.
Should we migrate from LiveData to StateFlow? What are the benefits?
There is not much difference between State Flow and Live Data. The Main difference come in that State Flow requires an Initial value hence no need to check for nullability. The Second Difference come in unregistering the consumer; Live Data does this automatically when the view goes to STOPPED state while State Flow does not. To achieve similar behaviour as Live Data, you can collect the flow in a Lifecycle.repeatOnLifecycle block.
Benefits of State Flow
State flow is included in coroutines library and can be used in Multiplatform Projects
Using one API in your project(Flow), not two (LiveData and Flow).
It's Kotlin, Why Not
It depends on what you want,
If you want a manual, full and versatile control over the app , go for state flow
If you want a partially automatic or relatively easy-to-use method for your app , I will say - stick with live data
In case If you want to know my personal opinion, it's state flow, as i prefer control over easy-to-use. I don't mind writing a few extra lines for it as it can be useful for me sometimes.
Think of it like using a soda opener for soda and using a nail cutter
I can do it with both but the soda opener Is easy to use in this case but , don't have much versatility like nail cutter.
And at the end of the day , I use state flow everytime because, I am lazy to learn live data for some projects as state flow can do what live data can even though live data will be much easier.
And you should decide what you want to choose and if you're not as lazy as me , I recommend go with both and use the one which is suitable each time.
Cheers.
Flow is the best practice
Livedata is used to observe data without having any hazel to handle lifecycle problems. Whereas Kotlin flow is used for continuous data integration and it also simplified the asynchronous programming.
Take Room Library as an example. First, it used livedata to transmit data from the database to UI. It solved most of the existing problems. But when there are any future changes in the database livedata is helpless in this situation.
After a while, the room used Kotlin flow to solve this problem. With Flow as return-type, room created a new possibility of seamless data integration across the app between database and UI without writing any extra code
read this article on medium website
In Android, LiveData and State are two classes that can be used to hold and observe data in your app. Both classes are part of the Android Architecture Components library, which is a set of libraries for building robust, testable, and maintainable apps.
LiveData is a data holder that is lifecycle-aware, meaning it only delivers updates to observers that are in an active state. It is useful for holding data that needs to be observed and updated in the UI, such as data from a network request or a database query.
State is a data holder that represents an immutable state value that can be observed. It is useful for holding data that does not change often, or data that should not be modified directly.
Which of these classes is "best" to use depends on your specific needs and requirements. Here are a few factors to consider when deciding between LiveData and State:
Mutability: LiveData is mutable, meaning its value can be changed, while State is immutable, meaning its value cannot be changed directly.
Lifecycle awareness: LiveData is lifecycle-aware, while State is not.
Transformation: LiveData supports transformation through the use of the Transformations class, while State does not.
In general, if you need to hold and observe data that needs to be updated in the UI and you want the data to be lifecycle-aware, LiveData is a good choice. If you need to hold and observe data that is immutable or does not change often, State is a good choice.
It is also worth considering whether you need to transform or map the data being held and observed. If you do, LiveData is a better choice because it supports transformation through the Transformations class.
I have an app with 2 distinctly seperate modes, (setup and run timers).
The app allows multi factor inputs from a user on one fragment and once the user wants the timer started, the app switches to a running fragment to show information about their running timer and it's setup.
I've designed an MVVM architecture for this, with my own class that extends ViewModel, my shared viewmodel has two distinctly different types of logic, setup logic (to check, parse and revise inappropriate user inputs), and running timer logic (to manage all the logic, data and state for a running timer from a user's inputs).
My shared viewmodel class is not small as the process of checking all permutations of user input is complex. I'm wondering is it a bad idea to put all this logic into one viewmodel class? The setup portion is designed to be simple and all setup state is saved (so 10-20 seconds for the user to setup a timer seems appropriate), whereas the timer is designed to be allowed to run for hours, largely with the screen off.
Should I split the viewmodel logic into two different viewmodel classes to make a running timer more memory efficient?
I see a clear seperation of concerns and once I have my Room database designed and programmed, only the running timer will save data to the database. I want to keep the fragment classes as lightweight as possible. If this is a sensible design choice, ill need to be careful with memory leaks between the two states, otherwise im defeating the purpose.
Edited to differentiate between the ViewModel object and a Shared viewmodel idea
As a_local_nobody says, it's up to you to decide how to design your app and distribute the responsibilities.
In case you are looking for our opinion about your philosophical doubts, I have to say that although the concept of the Shared ViewModels is very widespread, I am not a big fan.
In my projects, the most common and logical thing is that each ViewModel manages the logic of a single view. I always treat Shared ViewModels as an exception to the rule, since abusing them usually leads to a very tightly coupled code, very difficult to test and with unexpected side effects.
When using Firebase Cloud Messaging on Android, it is often desirable to notify the current Activity of an incoming push notification. One of the recommended ways to do this has been to use LocalBroadcastManager to send an Intent from the FirebaseMessagingService implementation to the Activity (StackOverflow example answer).
However, as of version 1.1.0-alpha01 (2018-12-17), LocalBroadcastManager is deprecated:
LocalBroadcastManager is an application-wide event bus and embraces layer violations in your app: any component may listen events from any other. You can replace usage of LocalBroadcastManager with other implementation of observable pattern, depending on your usecase suitable options may be LiveData or reactive streams.
While it is highly likely that this class will remain available for a while longer, I would like to start cleaning up our applications anyway, so I want to migrate to something better before Google actually removes the old way.
Right now, there are two main roles that these local broadcasts have in our apps:
Update the UI with the new data from the push notification. The way this worked was that each Activity that cares about the incoming push data has a broadcast receiver that listens for the appropriate message and updates its own view data.
Force the user to log out if the server sends a notification to end the session. This works with each activity having an instance of a broadcast receiver that listens for a logout event, ends the Activity, and starts the Login Activity.
As I see it, these use-cases have issues with both of their suggested alternatives:
LiveData is easiest to use in an Activity or Fragment as part of a ViewModel. However, ViewModel is only meant to be used from those classes that directly deal with the UI. Accessing the ViewModel from within the FirebaseMessagingService takes an ugly hack and is a really bad idea from an architectural perspective. Also, different activities and fragments have different ViewModel objects, and I don't want the service to need to access them all.
I can create a Kotlin object (a.k.a. Singleton) with a bunch of LiveData properties, have the FirebaseMessagingService update those LiveData objects from the incoming messages, and have the Activity observe those changes and copy them into its own ViewModel's LiveData properties. The problem with that is twofold: first, it requires me to have two identical LiveData objects for each piece of data, one in the ViewModel and one in the object; and second, it doesn't help me with handling the "log out event", because LiveData is meant to handle changing data, not listening to a stream of events. (I may be able to handle the second issue using this LiveData Event Wrapper, but that still feels like a bad hack over something that isn't meant to work this way.)
While reactive streams, such as RxJava, will probably do what I need, I already forced my team to learn Kotlin, Android Databinding, Android ViewModel, and a bunch of other new stuff in the last few months, and I don't think they can take much more. RxJava is also a large thing to add for just this one use, and we have no plans to rewrite the entire application to take advantage of it in order to justify its addition.
One suggestion I found was to use Kotlin Coroutines with Channels or Flows. These can be used very similar to reactive streams, but (unlike RxJava) are intended to be used with Kotlin and benefit from Kotlin's improvements over Java. This option is especially attractive now that Google has announced that they are focusing on Kotlin for Android development instead of Java.
While this seems to me to be the best option, I have not managed to find any feedback from others about whether it works and if there are side-effects and/or pitfalls to such an implementation. The only thing I found was an open issue on the kotlinx.coroutines repository about the need for providing an example of an application like this. While I'd love to contribute such an example, I don't think I know enough about it to create a good example, and I don't want my production apps to be the guinea pig. I also don't know whether it is better (or proper) to use explicit couroutines with Channel or to use suspend with Flow for this case.
In summary:
Are Kotlin Coroutines and their associated concurrency structures a good way to handle communication between Android Service and Activity?
If yes, which Kotlin type makes more sense to use, Channel or Flow?
Coroutines don't really help with the handoff of data from one software component to another. They help with the processing multiple units of asynchronous work using syntax that appears as if they were synchronous. That's the bottom line for coroutines. They're analogous to async/await syntax in JavaScript. While you might use a coroutine to access data from asynchronous sources, it doesn't give you any primitves to proxy that data on to other components.
LiveData probably works just fine for what you're trying to do. Don't conflate ViewModel with LiveData - they solve different problems. While you're correct that ViewModel should only be accessed by code that deals with UI, that guideline doesn't extend to LiveData. It's perfectly reasonable to expose a LiveData that reflects current data from FirebaseMessagingService that is later picked up by a ViewModel, transformed, and passed on to a view. This LiveData could be a singleton, or obtained via whatever dependency injection infrastructure you choose.
Bear in mind that LiveData is really only supposed to be used for managing changes in state. It's not a "stream" of data that your app can listen to. You will need to make sure that your infrastructure is state-based in order for this to work out well. FCM itself is not state-based, but if you want your views to respond to messages from FCM, you'll need to retain enough context between each message to make sure your UI responds consistently to new messages (or the lack of messages altogether).
I have many Activities that have several fragments. Part of the work those fragments do is to make authenticated network calls. It is possible that the token will expire. When that happens, I will see the 401 in the networking layer. What is the preferred method to communicate back to up the stack to whichever fragment initiated the network activity that it needs to initiate the logoff procedure? In other words, when the networking layer gets a 401, I want to communicate that back to the UI that the user needs to be log offeven though many other Activities may have also been invoked since then. This is often done via custom exceptions being handled in the app but I was wondering if there was a specific pattern that was preferred other than that. Something using Live Data perhaps?
You should separate in layers. Each layer should be responsible of a single task. There are many ways to do this, but you could use the MVVM Google approach which consists in:
M (Model): data classes and repositories that persist/retrieve them.
V (View): Activities/Fragments that observe data objects (LiveData) exposed by the VM and also call actions of the VM.
VM (ViewModel): Exposes actions, e.g. login, internally calls a Repository which calls the API (e.g. using Retrofit2), when it receives a result, it updates the data objects observed by the V.
Have a look at:
ViewModel
LiveData
Codelab
I have been testing Livedata and AAC in general.
What is the core difference between LiveData and ObservableField?
Which one is the best and when should I use one over another?
The core difference is that ObservableField<T> is not lifecycle-aware and hence there cannot be any automatic subscription management. While LiveData<T> is lifecycle-aware and solves tons of headaches with subscription management when it comes to Activity/Fragment lifecycles.
There is no one way to answer what is the best to use. It is a personal choice, but I would suggest using LiveData<T> just to save yourself some time and avoid potential issues in the future.
Both LiveData and Observable could be used alternatively. LiveData is lifecycle aware and hence will notify only the observables which are "active".
Quoting official documentation on https://developer.android.com/topic/libraries/data-binding/architecture#livedata
Unlike objects that implement Observable—such as observable fields—LiveData objects know about the lifecycle of the observers subscribed to the data changes. This knowledge enables many benefits, which are explained in The advantages of using LiveData. In Android Studio version 3.1 and higher, you can replace observable fields with LiveData objects in your data binding code.
As mentioned both are usable for UI binding interchangeably. LiveData is a quick method but if you want more control on the binding Obserable is a way to go with.
Quoting official documentation on https://developer.android.com/topic/libraries/data-binding/architecture#observable-viewmodel
There are situations where you might prefer to use a ViewModel component that implements the Observable interface over using LiveData objects, even if you lose the lifecycle management capabilities of LiveData. Using a ViewModel component that implements Observable gives you more control over the binding adapters in your app. For example, this pattern gives you more control over the notifications when data changes, it also allows you to specify a custom method to set the value of an attribute in two-way data binding.
We get options to customize the binding in case of an Observable which might be helpful in some case.
Personal preference is to go with LiveData. In case some some customization or more control is needed on the binding go for Obervable
Similarities
Work well with data binding
Bound views automatically unsubscribe when in the background
POJO classes can also subscribe to changes
Differences
LiveData allows for POJO subscribers to be lifecycle aware. Meaning that if you have a property B that you want to update when A changes, you can opt to not receive updates when the attached view is inactive. This saves resources.
Background thread updates of ObservableField<T> are immediate. MutableLiveData.postData is delayed.
Values of LiveData<T> and ObservableField<T> are always nullable, but the primitive implementations ObservableInt, -Float, -Boolean etc are non-nullable.
MutableLiveData<T> doesn't take a constructor value to set on initialization.
When to use what
Do you have external events that may trigger cascading data change in the ViewModel when the app is in the backgrond? You should probably go for LiveData
Do you require value update on background threads to be immediate*? Use Observables
None of the above requirements? You can't go wrong with either but ObservableField is a simpler class.
*) "Immediate" in the sense that a get directly after a set/postValue is guaranteed to return the value you just set. None of the types are of course immediate when it comes to UI update.