Preface - The code I have created so far already delivers the desired user interface, my question is about phone resources and whether there is a 'better' implementation. Also, I am relatively new to Android development and may be misunderstanding some basic concepts.
I am developing an app with tabs, and some of the tabs have multiple views. At the moment each view is managed by a fragment, and when an user is on a tab and opens a new view, the views transitions horizontally.
(the app is a port to Android of an existing iPhone app, and while the people I am working for readily understand the need to make an Android app as Android-native as possible, they want the basic user interface and control to remain the same so that if an iPhone user buys an Android phone and purchases another copy of their app, there will be no fundamental difference)
The primary tab is different however--it reloads itself. The app is essentially a study aid; imagine that each view is a card representing information, and the user if flipping through cards.
This is accomplished by having the 'card' fragment request that the fragment manager detach and then re-attach itself. This is accomplished by calling a method in the FragmentActivity that executes the following (simplified from the actual code):
FragmentTransaction ft = .... .beginTransaction();
ft.setCustomAnimations(int out,int in)
ft.detach(relevantFragment);
ft.attach(relevantFragment);
ft.commit();
.... .executePendingTransaction();
where 'out' is fromXDelta="0", toXDelta="-100%"
and 'in' is fromXDelta="100%", toXDelta="0"
Consequently, the old view slides out to the left, while simultaneously the new view slides in from the right.
This works just fine, in fact in terms of user experience it is exactly what I want. However, the fragment we are reloading has an extremely complex view hierarchy and my understanding of fragments is that the entire thing is going to be garbage collected and re-instantiated on each transition, which the user may easily invoke a large number of times (100+) while using the app to study.
Almost all of what I have read indicates I should avoid heavy object creation/collection as it will cause my app to devour an user's battery, regardless of whether processor is strong enough to perform without any visual lag.
.
ACTUAL QUESTION: Is there any way to emulate this kind of behaviour that does not force the phone to fully destroy and re-create the view every view seconds as the user clicks through?
Things I have considered:
Eliminate the transactions/animations and simply have the fragment load new information. This would be easy of course, but is not what my employers want.
Create two identical copies of the view hierarchy in the XML, and toggle them back and forth from View.VISIBLE and View.GONE, using a custom ScaleAnimation to attempt to emulate this behaviour. I'm not sure whether this would work, but at first glance it seems rather... sloppy.
This is an unnecessary and/or silly optimization that I should not be worried about, due to how the phone and/or Android system operates, and I simply don't realize I am wasting my time.
Any advice, suggestions, or clarifications on how the operating system work are much appreciated.
my understanding of fragments is that the entire thing is going to be garbage collected and re-instantiated on each transition
If you look at your code, this is impossible, at least at the fragment level. You are not letting go of the Fragment object, nor are you supplying some means for Android to create a brand new identical fragment. Hence, the Fragment will not be garbage-collected.
It is conceivable that Android will call onCreateView() again for your fragment and discard the old views, though that seems unlikely. You can test this by setting a breakpoint or adding a logging statement.
Almost all of what I have read indicates I should avoid heavy object creation/collection as it will cause my app to devour an user's battery
Done millions of times, definitely. Done "100+" times, it will not "devour" the battery. Nibble, perhaps.
Things I have considered:
I would focus on #3. At most, determine if indeed the fragment's view hierarchy is being recreated, in the form of multiple calls to onCreateView(). If it does, then use Traceview to determine exactly how much time this operation is consuming.
To summarize the discussion with CommonsWare in the comments above (and subsequent investigation of ways to address the matter), this is not a productive optimization. Preserving the view hierarchy for reuse, rather than allowing it to be destroy and recreated, only saves a few milliseconds of of CPU time. The vast majority is consumed by the phone's rendering of the views, not their instantiation.
CommonWare is correct in his initial answer (mostly). The Fragment.onCreateView() IS called every time a fragment is attached, but there is no productive way to optimize the view rendering. Nothing to be done, nothing to worry about.
Related
After API 28 the order of execution of onSaveInstanceState() and onStop() is changed.
Referring to this paragraph from the documentation:
The reason for that change is the ability to firstly execute fragment transactions and then save state. Referring to this paragraph from the documentation:
What are the gains after this change?
Are there other reasons behind this change?
It's indeed a matter of consistency, because method onStop() can still modify the instance state.
When moving onSaveInstanceState() to the end of the timeline, those changes will not be lost.
While I am not a Google employee and have not contributed to this, the change also affected activities if I correctly recall, and I'd imagine the reasoning has to do with the fact that before this change, the call to save the state, was maybe going to happen if/when the Framework considered somewhat appropriate after onPause.
The behavior was quite unpredictable and made the lifecycle methods unreliable to trust.
With (Google Devs) refactoring of the entire FragmentManager and its behavior, this change was most likely driven by consistency. (and unknown to me, perhaps, other technical details we're not familiar with).
In short, the framework is now consistent in the calls, as they are guaranteed to occur after onStop (for API 28+), which ensures that when you re-create the activity/fragment, the state will be correct (and saved).
Like I mentioned at the beginning, I am not a Google developer, but the way it was before was a clusterf**k of uncertainty, and this change brought some hope to save the Fragment apis from their ultimate demise.
Sometime in the middle of this, using multiple activities was not cool anymore, and we all wanted a single activity with many fragments, and nav component to seamlessly move across the stack (not having to worry to much about "defects" in the original design), as this and many other details were polished across the journey that got us here, to a more stable Fragment platform if you so desire to use it.
Before all these "fixes", the Framework API was prone to many errors and inconsistencies sometimes leading to runtime crashes. I can imagine how this and many other fixes, were aimed at reducing this API to a perfectly functional feature, so that other tools like Navigation Component, Lifecycles, ViewModels, etc. could all work in harmony without having to worry about "oh, the Fragments are special and need further logic to handle its buggy behavior".
Despite all this, Fragments are slowly starting to become "an old thing" as more apps start to incorporate Jetpack Compose.
Recently on the app I have started working, I thought to go full OOP and try out Fragments to make the UI most flexible and reusable as possible.
At least that was the plan.
Here is where I encountered some issues.
I have a BottomNavigationView in my app that has four tabs. Therefore, 4 different fragments.
HomeFragment
InboxFragment
FavoritesFragment
ProfileFragment
I believe, if I'm using bottomNavigationView, this much fragmentation is unavoidable.
Then when I started to fill out the HomeFragment with content, I find that the file is getting too complicated and huge to easily examine specific parts. This is where I get the idea to create fragments of features that may get used in other activities/views.
So, one such individual fragment is AdvertisementFragment.
Here's where things get interesting. AdvertisementFragment uses code that employs Timer.schedule/Thread.sleep; something to delay a certain function called update.
But the code wasn't being delayed, it sped up instead. Checking the Logcat showed messages like
Frame time is 8.006377 ms in the future! Check that graphics HAL is generating vsync timestamps using the correct timebase.
OR
Skipped 2 frames! The application may be doing too much work on its main thread.
I only create the dummy apps for my employer, i.e. If you will, say a "working" demo. I'm just a UI designer for them.
Now when I saw these messages popup in my logcat, my first thought was to experiment, how much my device can take before the app crashes. I frantically click away on the four tabs of my bottomNavigationView and sure enough I see the first problem.
Nothing is being loaded in the fragments, its just a white background. All the components, Fragment or Not, weren't showing up, in any of the fragments. Then after a few more seconds it crashed. I test the same on two physical devices, and sure enough, it was not the emulator that crashed, my code was the problem.
Even though, it is not my job for optimizing the code, it is going to be a real pain for the developers/users of the app, for the mess that will emerge out of my faulty working model. Hell, if the developers had to redo that, it's going to be more costly.
Which is why, I'm here, is using Fragments within Fragments a good idea? I know about saving Fragment states, but can severely Fragmented code be a nuisance to the memory and optimization? If you are talking about saving states of every fragment then would it not consume more memory?
Say, I have a HomeFragment, InboxFragment, etc, from above, which I load into MainActivity. I can switch between these parent fragments with a bottomNavigationView. Say, HomeFragment itself has 3-5 fragments and rest of the code in HomeFragments is specific to HomeFragment. In other words, let's assume that 40% of code in HomeFragment is Fragments, and the 60% is code specific to HomeFragment. How does that impact performance? What if other parent fragments (InboxFragment, ProfileFragment, etc) have similar constitution of Fragments and Activity specific code?
Can this much Fragmentation be easily/somehow optimized? Or is it outright bad practice?
hey people,
I have almost finished writing my first android app. It is a note taking app with add, edit view screens etc...
Initially I was handling moving between screens in a single activity by creating new layout objects such as tables etc... and displaying them.
However after some more reading I have changed my method of moving between screens by using different activities for different screens. However each activity that is called as an intent retrieves a large number of variables from the main activity via setExtra and passes back a large number of variables as well.
Now I want my app to be as efficient as possible and I personally think that handling it all in one activity is less memory hungry and processor intensive although this has the negative of meaning variables are always present (and consuming memory) unlink in a separate activity where they are killed on finish().
But you guys are more knowledgeable then me so what do you think is the best way to do it?
If you launch a new activity for the new screens then you will add that activity to the stack. That way a user can press back and get back to the previous activity. Just changing the layout removes this functionality. I doubt very much you'll have performance issues either way.
Best practice would be to start a new activity, best performance might be to use your current approach.
All your activities will reside and run from the same process. So there is no reason you need to pass around a pile of variables. You could (for example) stick them in a singleton which represents your context. When one activity hands over to another it fills in the singleton and the next one picks up its data from there.
Of course if you ever intend an external activity to interact with your ones you may have to rethink this approach, but I think you'd be fine to keep your views as separate activities. Even if memory is ever so slightly higher, I think it's better to do things correctly and only worry about optimization if and only when it becomes obvious you need it.
This pattern is similar to the pattern Main Servlet (the Front Controller) that is used for developing web applications.
The main idea of this pattern: we have one Activity that manages multiple views and this activity is responsible for representing current content. Not all views need functional of activity (e.g. life-cycle methods) so the main question is: if I can go without activity why do I have to use it?
I have found the following disadvantages of using this pattern:
Official source doesn't recommend to Overload a Single Activity Screen
but they don't explain why.
We cannot use TabActivity, ListActivity, MapActivity. But there are some tricks to go without them.
If different screens have different menu it's a problem to make that without activities.
It is necessary to keep history by ourselves. But it's not so difficult to develop.
I have found the following advantages of using this pattern:
It's faster to change the content of current activity than to start another activity
We are free to manage history as we want
If we have only one activity-context it's simpler to find and solve problems with memory leaks
What do you think about this pattern ? Could you provide any other advantages/disadvantages ?
We cannot use TabActivity, ListAcivity, MapActivity. But there are some tricks to go without them.
You have to use MapActivity if you want to use MapView. You have to use PreferenceActivity if you want to use preference XML.
It is necessary to keep history by ourselves. But it's not so difficult to develop.
The difficulty in managing your own history will depend greatly on what the history needs to be. Implementing history for a simple wizard will be fairly easy. However, that is a particularly simple scenario. There is a fair amount of history management code in Android that you would have to rewrite for arbitrary other cases.
You also forgot:
#5. You will be prone to leak memory, because you will forget to clean up stuff, and Android will not clean up stuff (since it assumes that you will be using many small activities, the way they recommend).
#6. Your state management for configuration changes (rotation, dock, SIM change, locale change, multiple displays, font scale) will be more complicated because now you also have to figure out what extra stuff (e.g., history) need to be part of the state, and you have deal with all of them at once rather than activity-at-a-time.
#7. Having multiple entry points for your application becomes more challenging (e.g., multiple icons in launcher, app widget linking to some activity other than the main one, responding to etc.).
It's faster to change the content of current activity than to start another activity
For most modern Android devices, the speed difference will not be significant to most users, IMHO.
If we have only one activity-context it's simpler to find and solve problems with memory leaks
Except that you still have more than "one activity-context". Remember: your activity, large or small, is still destroyed and recreated on configuration changes.
What do you think about this pattern ?
Coase's "nature of the firm" theory says that businesses expand until the transaction costs for doing things internally become higher than the transaction costs for having other firms do the same things.
Murphy's "nature of the activity" theory says that the activity expands until the transaction costs of doing things internally become higher than the transaction costs for having other activities do the same things. Android developers will tend towards a "user transaction" model for activities -- things that are tightly coupled (e.g., steps in a wizard) will tend to be handled in single activity, and things that have little relationship (e.g., browse vs. search vs. settings vs. help vs. about) will tend to be handled in distinct activities.
This will be horrible to maintain if new functionality is added later on.
I'm also not convinced it will be so much faster that the user could notice.
Having components as smaller pieces that are easier to change or swap out is definitely the way to go.
I am working on an application that tries to streamline data entry from a very repetitive process:
Enter some details that require full-screen graphics and would be confusing if scrolled
Enter some more atomic details
Enter yet more atomic details
Apply the accumulated data
Go back to step 1
I am pretty sure that i can represent this as 3 separate Activities and then just fire up new Intents for each activity in each cycle. What I can't yet get a sense of is whether this is a viable approach.
Question 1: If I do a fire-and-forget approach, how much of the resource management is going to be handled by Android? Will it just happily deallocate/reuse/etc. activities behind the scenes? Or is this something i have to manage myself?
Question 2: Is there a way to cause the reusing of activities so that only one instance of each activity is ever allocated and is just reused for each cycle?
Question 3: Can one manipulate the activity stack so that there aren't ~100 (approximated number of expected cycles) cycles worth of activities on the stack? I'd like to be able to use the back key no more than three times and exit out of the data entry portion to a summary page.
Question 4: Can anyone suggest alternate approaches to the cycles of activities problem? I have considered view flippers and tabs, but wasn't sure that would be better or not.
Will it just happily
deallocate/reuse/etc. activities
behind the scenes?
Yes.
Is there a way to cause the reusing of
activities so that only one instance
of each activity is ever allocated and
is just reused for each cycle?
Try FLAG_ACTIVITY_REORDER_TO_FRONT on your Intent to launch the activity. Based on the docs, it should give you your desired behavior.
Can one manipulate the activity stack
so that there aren't ~100
(approximated number of expected
cycles) cycles worth of activities on
the stack?
100? You must be expecting some very patient users.
Regardless, FLAG_ACTIVITY_REORDER_TO_FRONT should cover that too.
Can anyone suggest alternate
approaches to the cycles of activities
problem? I have considered view
flippers and tabs, but wasn't sure
that would be better or not.
Tabs aren't great for things where you're trying to enforce a flow, since tabs are designed for random (not sequential) access. ViewFlipper/ViewSwitcher could work, though then you have to manage BACK button functionality and make sure you're not effectively leaking memory within the activity, since you're expecting people to be using it for an extended period.