I have couple of (nested) fragments, for which I'd like to persist arbitrary data. I don't want to use regular singletons, because then the memory would never be freed, even after leaving particular part of the app.
I've been researching Dagger and Mortar, but I can't think of a way to have child object graph that is tied to a fragment, but which is also retained across configuration changes - most examples just show object graph per activity, but that doesn't help at all (since it'd be destroyed farily quickly).
The only thing I could think of are either storing the graph in a retained fragment, which doesn't actually help, or in Application class. But then how can I know when should I free the graph?
Overall my problem is how to use Dagger's child object graph in Android ecosystem, where everything except from Application is destroyed every once in a while
Related
I'm trying to persist a list of objects across activities and fragments using the Singleton Design Pattern, but at certain times, I think when a process or activity gets destroyed by Android because of memory resources, the Singleton object is destroyed and my app crashes. I've come across this post:
How do I make Android's Singleton object persistent
and to quote:
I think a new top-level activity is made in a new process (reading
between the lines in
http://developer.android.com/reference/android/app/Activity.html#ProcessLifecycle).
Android is more likely to kill of the process of an Activity that
lives in the background, and that includes when a top-level activity
drops behind another.
I think a better solution might be to have your Singleton
transparently initialise itself if null, loading and saving any state
from disk if necessary (a pretty standard singleton paradigm). There's
no way to solve your problem with a single instance across the old and
new activities.
Is there no way to ensure data persistence across activities and fragments without having the potential of reinitializing the data from a database?
Shared Preferences is best for storing your application data. You can try and start here https://www.tutorialspoint.com/android/android_shared_preferences.htm
Option 1 : Shared Preferences
Option 2 : Using Global Constant
If you want data after application close you can go for Option 1
After application close no need data you can go for Option 2
I'm working on an Android piano "quiz" app - users tap on the piano keys and then click the yellow "check" button to submit the answer for evaluation and see the correct answer drawn on the piano. The main QuizActivity has this layout:
The upper part of the screen hosts a couple of controls (Text, submit buttons, etc.).
The lower part of the screen is occupied by a custom PianoView component, that handles drawing of the piano keyboard.
According to the MVVM principles, the PianoView should have its own PianoViewModel, that stores its state (i.e. currently pressed keys, highlighted keys, etc...) in a KeysStateRepository.
The enclosing QuizActivity should also have a QuizActivityViewModel, that handles the various controls (submitting an answer, skipping a question...).
The QuizActivityViewModel needs to be able to query the selected keys from the PianoView (or rather from its KeysStateRepository), submit them to the Domain layer for evaluation and then send the results back to the PianoView for visualization.
In other words, the QuizActivity's ViewModel should own/be a parent of the PianoView's ViewModel to facilitate communication and data sharing.
How can I model this parent-child relationship to communicate between the ViewModels?
AFAIK a ViewModel cannot depend on another ViewModel (What would I pass as the ViewModelStoreOwner to obtain a ViewModel in another Viewmodel?). I don't think it's possible to achieve with Dagger-Hilt at least.
Three solutions to work around this problem came to mind, all of them unusable:
1 - The official way of sharing data between Views
The Android dev docs recommend using a shared ViewModel to facilitate sharing of data between two Fragments / Views. However, this does not fit my use-case. The PianoView (or its ViewModel) should be the sole owner of its state with a Repository scoped to its ViewModel. Otherwise, the PianoView component would not be reusable. Consider for example another Activity, where I'd like to have two independent PianoView instances visible:
Reusing a Shared ViewModel from the quiz activity would be obviously wrong, because it contains irrelevant methods and logic (i.e. submitting quiz answers) and would not fit the two-keyboard scenario.
2 - Application-scoped repository
A similar problem was tackled on Reddit with a proposed solution of using a shared instance of the repository. However, using a #Singleton KeyStateRepository would once again prevent the two independent keyboards to display different data.
3(EDIT) - 2 duplicate repositories replicated by an Event Bus
I could in theory create 2 independent ViewModels and 2 KeyStateRepository instances. The ViewModels would subscribe to an event bus. Each time a ViewModel invokes a mutable operation on its repository, it would also fire an event and the operation would get replicated via the other ViewModel subscribed to the same event bus.
However, this feels like a fragile & complicated hack. I'd like to have a simple MVVM-compatible solution. I can't believe a simple parent-child relationship for two UI components is something unattainable in MVVM.
I think you got a decent answer from Pavlo up there, I'll just clarify what he meant with other words.
KeyStateRepository is a storage for the state of piano keys. There's nothing stopping you from making it to support N number of Pianos at the same time, this would solve the scenario where you have NNN Pianos on Screen, each with different keys pressed.
The PianoView should be contained in a Fragment, that should be your "unit". Why? Because you want a ViewModel to handle the state and events coming to/from the view. And a Fragment is the Android artifact provided for that regard. Think of it as an annoying piece of baggage you need. Android Devs used to call these things "Policy Delegates" because you delegate to these (a Fragment/Activity) some things you cannot do without "the framework" (the Android Framework, that is).
With this in mind, you have an Activity whose viewModel/State is handled independently. What State/Events do this viewModel handle? Things that are not in the PianoFragment/View(s). E.g. if you wanted to handle the back navigation, or a "record" button at the top, this is the activity's domain. What happens inside the "PianoView/Fragment" is not this activity's problem.
Now the Fragment that will contain the actual PianoView can be designed to contain "more than one" or just one. If you go for more than one, then the PianoContainerFragment will be designed with a ViewModel designed to handle more than one PianoView (so each view will have a "name/key") and the KeyStateRepo will be able to handle the "CRUD" operations of any Piano View you throw at. The ViewModel will sit in between, dispatching events for different "subscribed" views.
If you elect to go for "one fragment contains one piano view", then it's a similar architecture, but now handling the multiple "fragments" in one "activity" is now responsibility of the Activity (and its view model). But remember, the PianoViews (via a Fragment either shared or not) talk to a ViewModel that can be shared among piano views, that talks to a common KeyState Repo. The activity coordinates the views and other Android things (navigation, etc.) but the views operate independently, even of each other.
You don't really need a shared viewModel I think, in fact, I wouldn't do it until really needed, the more you separate things, the less the chances of "violating" one of the fancy patterns... but if you elect to use the PianoViewModel as a shared among all views, that's perfectly acceptable, you're going to have to include the Piano "Name" to differentiate whose events are for whom.
In other words (showing with ONE PianoViewModel for ASCII Simplicity),
// One QuizActivityViewModel, Multiple Fragments:
Activity -> PianoFragment (PianoView)|
| <-> PianoViewModel <-> KeyRepo
PianoFragment (PianoView)| /
-> QuizActivityViewModel <----------------------/
Here the QuizActivity creates N fragments (in a list maybe?). These fragments internally initialize their pianoView and connect to a PianoViewModel (can be shared like in the graph above) or each can have its own. They all talk to the same Repo. The repo is your "single source of truth about what each "piano". What keys are pressed, and anything else you can think of (including a name/key to make it unique).
When QuizActivity needs to evaluate the state of these, it will ask (via its own viewModel) for the state of NN pianos.
Or
// 1 Act. 1 Frag. N Views.
Activity -> PianoFragment (PianoView)|
(PianoView)| <-> PianoViewModel <-> KeyRepo
-> QuizActivityViewModel <---------------------------/
With these, the QuizActivity (which created the pianos to begin with as well), also knows the keys of the pianos that will/are displayed. It can talk to its viewModel that talks to the same KeysRepo (you only have one of these and that's fine). So it can still handle the "nav" buttons and it can ask (via its QuizActVM) what the current state of the Keys are (for all involved pianos). When a Piano key event is fired in a PianoView, the PianoViewModel will receive the event (what key was touched, in what piano); the KeyStateRepo will record this, and perhaps update a flow {} with the events coming from the pianos...
The Flow will be expressed in a sealed class which will contain enough information for both QuizActivity + VM (to perhaps perform a real-time validation), and to the PianoViewModel to update the state and push a new state to the PianoFragment (Which will update the state of its view(s)).
This is all common to either method. I hope this clarifies the sequence.
Does this make sense to you?
Edit
In a multiple architecture activity if you wan't PianoViews to have ViewModels and your ActivityViewModel to know about them - don't use Dagger injection with them but create PianoViewModels inside an ActivityViewModel and assign some callback to them on the stage of creation - thus you will have an access to them and will be able to listen to their events and influence their behaviour as well as save their state, from inside the ActivityViewModel. It is not an uncommon and in some cases even a correct approach. Dagger - is a mere instrument that is not intended to be used everywhere, but only there were it is needed. It is not needed to create PianoViewModels - you can inject all the needed stuff into the ActivityViewModel and pass all the needed elements to PianoViewModels constructors.
Also you don't need to wrap your Views into Fragments if you don't want to.
Edit end
You are making wrong assumptions based on a flawed architectural approach.
I am curious why do you need ActivityViewModel at all. View model should exist only for the elements that have some View. Current android development suggests that the Activity should not have a view representation and serve as a mere container of other views(Single activity principle). Depending on your architecture Activity may handle showing the loading state(progress bar) and some errors, but once again it should not contain anything that is being handled by other views. Thus PianoView should be a PianoFragment with its own ViewModel that handles access to its repository on the data layer via interactor on the domain layer.
The shared view model would work in case you would need one, and you would be using the Single activity principle with multiple fragments. Because Jetpack Navigation has the support of the shared view model out of the box. In the case of a shared view model - each fragment would have its own view model along with a shared one for communication. Each navigation graph could have a separate shared view model only for the fragments it contains.
Also regarding KeyStateRepository - you need only one of those(or a Dagger #Scoped multiple copies - but I do not recommend it). The only change should be - the addition of an extra key for each separate PianoView - to distinguish them inside a KeyStateRepository. To easily achieve that you may be using Room or some other file/memory/database cache mechanism.
Hence the initial problem of your app is not an inverted dependency of ActivityViewModel on a PianoViewModel, but a flawed architecture of the app and its inner interactions. If you want to continue work with your current architecture - there is no easy answer to your question, and almost every chosen solution would not be 'clean' enough to justify its usage.
I would do the following, if you don't want to tie the PianoViewModel to your ActivityViewModel, I'd just create an interface, which the ActivityViewModel implements, and the PianoVM could have a nullable reference to that interface. This way neither the implementation, nor the existence of the component would be required for the PianoViewModel to work.
How you get the ActivityViewModel is another question. Check out by activityViewModels() implementation for fragments, you probably can do the same with by viewModels() passing in the viewModelStore of the activity instead
I have quite a bit of experience in Java, and up until I noticed this bizarre behaviour in my first android app I thought any references I create will always point to the same Object unless some code with access to the reference changes it.
I had an activity with to two fragments under it inside a ViewPager. In the activity I maintain references to the two fragments (private fields) to interact with them. On orientation change my fragments are stopped (and apparently destroyed as well) so I am creating them again from the activity because they have complex state that I don't wanna bother "parceling" or "serializing" into a bundle... This may not be best-practice, but hat's not my question though. It's more about how the Android JVM and libraries manage to automatically change the activity's private reference to the fragment (which I'm setting in the onCreate method) to point to an automatically constructed fragment using the default constructor. I understand that android can destroy fragments and construct new ones later and invoke onSavedInstanceState or whatever to allow the developer to recover their fragment's state; but how does it also find and change the existing reference in the activity to point to the newly constructed fragment.
This seems to be a JVM-level feature (more than just simple reflection), that allows Android to figure out all existing references to the fragment (or is it? what am I missing?) in order to modify them when a fragment is re-created. Whatever the case, is this and Android-specific JVM feature? Does a similar thing exist in other Java implementations such as Oracle Java or OpenJDK?
Please keep your answer about the weird automatic reference juggling part, and not about how I'm managing the activity/fragment life-cycles (I'm aware I could do better, and I'd love to read your suggestions, but keep those in the comments or the second part of your answer). I've done a lot of debugging and that's what I observed: the fragment reference was indeed changing, and I managed to fix it by manually resetting the reference to the reference I explicitly set in the onCreate method - a seemingly redundant operation.
Currently I've got a TouchDB instance embedded in my main activity and have now hit a point where a separate fragment needs access to it and mearly passing data from the main activity to other activities isn't enough. Is there an recommended way of sharing the same db instance between activities/fragments? For instance in the image below which is similar to my app, fragment b requires access to the db, but on mobiles it'll be attached to Activity B and tablets Activity A (the main activity in my case).
Would extracting the db implementation out of my main activity and putting it in a singleton class be the recommended way? Or instantiate and destroy the db in each activity/fragment? If the later is the prefered approach is my understanding right in that, apart from the main activity it would be best to have the db implementation in the fragments rather than their related activity?
You should definitely extract your DB code from your view code. After that, you have a lot of options depending on the needs of your application.
Patterns that might be useful depending on your needs:
Inversion of Control (IoC)
Dependency Injection
Factory
Singleton
A side note on Singletons, which you mentioned as a possible solution. There is very little difference between a Singleton and a global static object. They share similar weaknesses - they introduce code dependencies, and code that uses them can be very difficult to write automated tests for. The main difference is that Singletons can control when the underlying object is first created - which probably has minimal impact on your application. (If your db code takes 10 seconds to load and you want to show a splash screen during the load, then the Singleton could be helpful.)
I have a simple Android application that uses an instance of a class, let's call it DataManager, to manage access to the domain classes in a Façade-like way. I initially designed it as a singleton which could be retrieved using static methods but I eventually got irritated with the messiness of my implementation and refactored to what I reckoned was a simpler and cleaner idea.
Now the idea is that for each file that is opened, one DataManager is created, which they handles both file I/O and modification of the domain classes (e.g. Book). When starting a new Activity, I pass this one instance as a Serializable extra (I haven't got on to using Parcelable yet, but expect I will when I have the basic concept working), and then I grab the DataManager from the Intent in the onCreate() method of the new Activity.
Yet comparison of the objects indicates that the object sent from one activity is not identical (different references) to the object retrieved from the Bundle in the second Activity. Reading up on Bundle (on StackOverflow, etc.) suggests that Bundles cannot do anything other than pass-by-value.
So what is likely to be the cleanest and safest strategy for passing an object between Activities? As I see it I could
Forget about passing by reference and live with each Activity having its own DataManager object. Pass back the new DataManager every time I close an activity so that the underlying activity can use it. (The simple solution, I think.)
Go back to using a singleton DataManager and use a static method to get it from each Activity. (Not keen on using singletons again.)
Extend Application to create a sort of global reference to DataManager. (Again, not keen on the idea of globals.)
Is that a fair summary? Is there some other healthy strategy I could use?
Another approach would be to create a service. The first activity would start the service and bind to it, when you launch a new intent, unbind the first activity and when second activity starts, bind to the service.
This way you don't have to ever stop the service or worry about passing data between activities.
Java does not have pass by reference so that option is out, I would suggest dependency injection for passing data between the activities. Otherwise definetely the singleton would be the way to go.
The prescribed one is Going by implementing Parcellable interface, thats the way to pass Objects between Activities.. and the 2nd and better choice is to make a Singleton to be sure its single Object.
Create your DataManager as a Singleton that implements Service. Bind the service to your application in the manifest xml (see the link), and you will have a persistent singleton your activities can access without issues.
Passing parcellable arguments can quickly get very messy if you need to get a lot of data. The singleton approach, although usually considered an anti-pattern, works like a charm in cases like these. Just remember to not create multiple singletons that interact with one another.
I would suggest using an Application Subclass. It allows you to hold a single reference to the DataManger class and is persistent as long as your app lives.
A singleton with a static field will also work, but there are some place in the documentation where it says that the content of static fields is not a safe place to store your data. As I understand static fields should persist as long as your ClassLoader stays in memory. Therefore a singleton should only vanish if the whole process leaves the memory and in that case the application class will also leave the memory, but it will call the onDestroy methods of the Application and that enables you to safely close your DataManager and persist important data to memory.
That said to your two variations.
The Android way to go would be to make your DataManager a ContentProvider. This will make it possible to access your Data from every Activity without holding a global reference. I'm not sure how you would build a caching Content Provider that stays in memory and is not reinstantiated too often.