Proper Usage of Android Fragment - java

I have been reading up on the Android API and it seems like Fragments are meant to sort of modularize an Activity. So if an Activity has a ListView and DetailView then it should be split into two separate fragments and have the Activity act as the master controller.
In my previous project that I worked on we were using Fragments kind of like children of the Activity.
For example: Let's say there is a AutomobileActivity that is designed to save automobile input data to the cloud.
We have fragments like:
SedanFragment
TruckFragment
SportsUtilityFragment
These fragments take up the entire view of the Activity and only one is displayed at a time. While these fragments use methods in the Activity to call common webservices like saving automobile information, getting car information. They also do different things like the Truck may have an additional entry to set the "Bed Size" and SportsUtilityFragment may have "Tow Limit" etc.
So in a way we are leveraging a lot of re-use and modularizing, but it's not exactly what the Android API is detailing. Is this a bad way to use Fragments?

This is a very objective question and this will have a lot of different answers. In my opinion what you are doing is correct. The reason behind this is that they have a set of common webservices. If we go by the mvc approach they can all have the same controller(for calling webservices), a model class(Superclass- vehicle) with separate models which inherit from this vehicle model class. By doing this you can have an additional entry parameter which will be present in these models. Your view, the fragments can easily call the instance of these models. If you modularize in this manner, you will be making life very easy for yourselves.

Related

How to model parent-child relationship in Android MVVM VMs?

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

Architecture - should my Main Activity know about my app specific fragments?

I'm trying to learn more about proper architecture so I'm going to try to refactor some of my working code. I have an android app that swaps in a few different Fragments for the main view. Right now, the different fragments exist as properties in MainActivity. When the user picks a menu item I have to check what kind of Fragment is currently being shown (via instanceof) and perform some different tasks depending on what kind of fragment it is. It's pretty ugly and I feel like it should be better abstracted. In the future, the case statement will grow even more and need to be maintained. Also, most of the code is duplicated since I only really need to do something special for one fragment in particular. How can I abstract away these details out of MainActivity? I tried to make a base class for all my fragments but it didn't quite work out because some of the fragments extend MapFragment and some just extend Fragment. So I'm guessing an interface is the way to go but I'm not quite sure how to do it. Do I need to create an abstract factory to get these objects in MainActivity? What is the proper way to do this? After reading up a little bit on architecture, it seems to me that my MainActivity should only know about some abstractions for my Fragments and not the concrete classes themselves, is that correct? Thanks for any help you can provide.

GWT MVP implementation

I have few gwt mvp design related questions:
Can we use event bus to switch views from one presenter to other via controller using custom event?
If above is true, can the custom event (say changeViewEvent) contain name of next view, on the basis of which controller can take a decision, which presenter to show?
Is it a good design to make views reusable(as a widget) in an application, though i don't agree with this, but will be happy if someone has any thing to mention in favor of this.
PS: all my views make use of custom widgets and there is no gwt specific widgets(buttons, checkbox etc...) in views.
You can do anything you want, but you have to consider the consequences. For example, if you switch views without creating a history event, a user may be thrown out of your app when a user hits a back button expecting to see the previous view.
I very much like the Activities and Places design pattern. It takes care of all of the issues (history handling, bookmarks, tokens, etc.) You can also extend it to add animation effects when switching views on mobile devices - mgwt does that.
I have few gwt mvp design related questions :
Can we use event bus to switch views from one prsenter to other via controller using custom event ?
This is a bad practice, unless you have real good reasons to do that. Since you're changing the view without having an impact on the url, you won't know what is a the state of a view at a choosen moment, you cannot get back to a previous view in an easy way, and finally, people will have difficulties reading the code, since you're out of the "standard".
The only reference for you should be the url, you cannot assume data is loaded neither the applicatio is in a given state: each and any view may be the start of the navigation story for your users, so if you get information from any other source than the Place, you're probably doing wrong, especially if your source is an event. the only special case here is you don't want users to enter your app in a certain view state, so you 'impose' that another url is called before, and restrict the access to the view through an event starting from a given application state
If above is true, can the custom event (say changeViewEvent) contain name of next view, on the basis of which controller can
take a decission, which prsenter to show ?
as said before, you're reinventing the wheel. It's far better to adapt to the existing mechanism that is well thought and covers the majority of cases. You can put a json formatter to tokenize your url while developing so it's not a nightmare to put variables in. And when you're done, create a nicer regular url format
Is it a good design to make views reusable(as a widget) in an application, though i don't agree with this, but will be happy if
someone has any thing to mention in favor of this.
depends on what you call a View. if it's the activitie's view then youm might need that in very few cases, it's better to inherit a basic view and fork its children for each activity (even if the view does nothing, time will show it will evolve differently): this is better since the base view contains what is common without the specifics of each child activity.
Finally if you mean a composition of widgets when you say, a view, then you should definitely reuse them, it will make you obliged to improove your widgets and compositions to be in a constant improvement, this will heop you for the rest of the project, and maybe for other projects

Must every activity has own model?

I have just read the description of MVC desing pattern and I havesome questions: I am Android developer (junior), and I want to make my code more clear. So, should I use MVC for it? And must every activity has own model? Is there any good tutorial for it? Thank you.
It's already implemented. MVC pattern on Android
you need not to do anything, As Android is prebuilt MVC
MVC is kind of an idea more than a specific way of doing things (like a 1-to-1 relation between activities and models). The idea is to separate the model, view, and controller, so that stuff makes sense.
In Android, more than one activity can refer to a single model (for example, an activity with a list of houses you can search on, an "edit house" activity, and a map that shows them as points in their coordinates). So, to answer your second question: no, they don't need to have their own model.
And yes, you should use MVC, if it makes sense. Just think about your models as a separate entity from the actual application, and your activities as "users" of the models.
On Android, I've found the MVP (Model, View, Presenter) pattern to be a more direct correlation with the overall system architecture. Your activities comprise the Views, which in the MVP setup are responsible for managing their own events and controlling their own appearance. The presenter serves as a facilitator between the model and the view, providing the data when the View requests it. Depending on your needs, the presenters may or may not be a service. As for the View/Model ratio, it really depends on what you're trying to show on your screen at any one point. When android was running on phones only, it made sense to have pretty much a one to one correlation between Activities and your model. Now, the normal case is to have a one to one correlation between your model and your fragments, which your activity then marshalls about by showing the appropriate fragments.
If you want to do MVC, though, again, now that fragments are a tool in the toolbox this is much easier than it once was, especially with well developed event system (such as the one included in RoboGuice) - Think of your fragments as your Views, and your activities as controllers - Ordering your views about, providing them data from the model, and handling transitions to other controllers.
The choice of pattern depends on your needs - if one's application is to be heeavily service driven, MVP is probably a better way to go. If, however, the app is just a thin client over a database, then MVC might be easier. It's all up to you :)
'get started' resource for MVP : http://www.jamespeckham.com/blog/10-11-21/MVP_on_Android.aspx

Android - How to avoid duplicate code between activities

This is a bit of a general question, but I will give a specific example for you.
I have a bunch of activities in an App. In all of the activities, there is a Facebook button. When you click the button it takes you to a specific Facebook page. I wish for the button to behave exactly the same way on every page.
Right now, in every single Activity, I create an onClickListener() for the Facebook button and make the intent and start the activity. It's the same code in every single Activity.
What is the best way to write this code once and include it in multiple activities? Is there anyway to include other .java files?
One solution that I know would work, is to make a base CustomActivity class that extends Activity and then have all activities extend CustomActivity. Then put my onClickListener() code in CustomActivity. I'm new to Java though and I wasn't sure if that was the best approach or not. Some of my Activities already extend other custom activity classes as is, so extending things that extend more things might get kinda messy, I dunno.
UPDATE
Playing the devil's advocate here: Lets say I went with the inheritance route and I create some CustomActivity that I want my Activities to extend. CustomActivity would contain a bunch of general code that I need to use for all Activities, including but not limited to the Facebook button functionality. What happens when there is an Activity that I need to use generic code from the CustomActivity but there is no Facebook button in that specific Activity?
A common base class is perhaps the best approach. (It doesn't work quite so well if some of your activities extend Activity and some extend Activity subclasses (such as ListActivity).
An alternate approach is to create a separate class that implements the logic of your click listener. This doesn't eliminate all duplicate code — each activity still needs to instantiate and register a listener — but the logic for what to do will only need to be written once in the listener class.
In either alternative, you might consider assigning the android:onClick attribute to the button. That way you don't need to register a click listener; you just need to implement the target method in each activity. This is particularly useful with the base class approach.
UPDATE
Suppose you go the inheritance route and you want an activity with no Facebook button. If you are using the android:onClick technique, then you don't have to do anything different in your code — since no button will invoke your onClick method, the method will just sit there doing nothing. If you are installing an OnClickListener in code, then you just need to test that the button exists (i.e., that findViewById() did not return null) before registering the listener.
Generally a common base class is NOT the best approach (although it's certainly valid).
This took me (and every OO programmer who "gets" OO that I know of) a while to really grok, but you should use inheritance as sparingly as you possibly can. Every time you do it you should ask yourself if there is REALLY no other way to do this.
One way to find out is to be very strict with the "is-a" test--if you call your base activity a "Facebook Activity", could you really say that each child "is" a Facebook activity? Probably not. Also if you decided to add in Twitter to some of the pages (but not others), how do you do this?
Not that inheritance is completely out! A great solution might be to extend a control to launch your facebook activity and call it a facebook button--have it encapsulate all the stuff you need to do to connect to facebook. Now you can add this to any page you want by simply dragging it on (I'm pretty sure android tools let you add new components to the pallet). It's not "Free" like extending your activity class, but in the long run it will cost you a lot less stress.
You probably won't believe me now, we all need to learn from our own experience, just keep this in mind and use it to evaluate your code as you evolve it over time.
--edit, comment response--
You can encapsulate any facebook activity you think you will use a lot in it's own class--get it to a bare minimum so you can just add it to any class in a one-liner.
At some point, however, you may decide that it's STILL too much boilerplate, I totally understand. At that point you COULD use an abstract base activity like you suggest, but I wouldn't hard-code it to handle facebook explicitly, instead I'd have it support behaviors such as facebook (and maybe others), and turn-on these behaviors as desired. You could then tell it NOT to add the facebook behavior to a given screen if you like, or add in Twitter to some of them.
You can make this boilerplate minimum, for instance if you want "Standard" functionality, you shouldn't have to do anything special, if you wish to disable facebook you might start your constructor with:
super(DISABLE_FACEBOOK_BEHAVIOR);
and if you want one that also enables Twitter you could use:
super(DISABLE_FACEBOOK_BEHAVIOR, ENABLE_TWITTER_BEHAVIOR);
with a constructor like AbstractAction(BehaviorEnum... behaviors).
This is more flexible and you actually can say that each if your activities IS-A "behavior supporting activity" with a clear conscience.
It is, of course, a perfectly good approach to be less flexible at first and refactor into a pattern like this later when you need to, just be on the look-out for your inheritance model causing problems so you don't let it mess you up for too long before you fix it.
Well, extending things is the principle of OOP, so I don't think this is a problem to have more than one level of subclasses. The solution you thought about is in my opinion the best.
Absolutely. Use inheritance to gain some reusability as you should with OOP. You'll find, as you progress, that there are gonna be more and more things you'd like to reuse in your activities -- things more complex than an onClickListener for a FB button -- so it's a great idea to start building a nice, reusable "super" activity that you can inherit from.

Categories