2

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:

  1. 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.

  2. 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.

  3. 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.

HeMightBeTodd
  • 117
  • 1
  • 9

2 Answers2

3

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.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks for the response. onCreateView() is unquestionably being called every time the fragment is attached, independent of the fact that it remains instantiated as a single object. Most of the business of settting the views is done in onCreateView(), were it not called I would transition into a new view containing the same information as the previous one. This is confirmed by a variety of logging statements already existing in the code. I'll checkout out the Traceview. (and then probably just not worry about it) – HeMightBeTodd Mar 25 '12 at 18:51
  • @HeMightBeTodd: You can try caching the views yourself, then. I had expected that `Fragment` did that -- it certainly does in the `setRetainInstance(true)` path. – CommonsWare Mar 25 '12 at 19:04
  • After a couple of tests it looks like across the total time of the fragment data update and view transition, roughly 70% of the inclusive CPU time is consumed by ViewRoot.performTraversals(), the most demanding children being ViewRoot.draw() (80% of parent) and View.measure() (15% of parent) being called during inflation of the layout in onCreateView(). This is in comparison to the MOST expensive business logic relating directly to my app, 2 calls to a SQL database, which together take 3.5% of the inclusive time. – HeMightBeTodd Mar 25 '12 at 19:50
  • So, it works, but doesn't seem ideal. I really have no idea where to start if I'd like to cache the views myself, could you perhaps point me in the direction of a tutorial or Stackoverflow thread? I am also concerned because I am testing and debugging on a fairly modern phone, 1.3Ghz and 512M of ram, I fear it will bog down older devices. – HeMightBeTodd Mar 25 '12 at 19:51
  • @HeMightBeTodd: "I really have no idea where to start if I'd like to cache the views myself" -- use a data member. If it's `null` in `onCreateView()`, initialize the widgets. If it's not `null`, reuse the widgets. – CommonsWare Mar 25 '12 at 19:56
  • ... yes, of course. For some reason it had never occurred to me that I could reuse the same layout, even though I knew I was holding onto the same Fragment object. I'm currently experiencing a somewhat difficult to trace IllegalStateException ("The specified child already has a parent. You must call removeView() on the child's parent first") but I imagine I can hunt that down on my own. Thanks for the help. – HeMightBeTodd Mar 25 '12 at 20:44
  • @CommonWare: Oh, tricky! If you execute the detach() -> attach() without interference, you're thrown an exception because the view you're holding on to already has a parent when the fragment manager tries to attach it. However, if you make a call to getParent() and remove the view, Android removes it BEFORE the fragment transition animation. Meaning, the screen drops to black as the new view is animated onto the screen, even though the removeView() is called in the onCreateView() of the incoming... view. – HeMightBeTodd Mar 25 '12 at 21:06
  • As opposed to (2) in my initial question, would it be a bad idea to hold onto 2 inflated copies of the layout and swap THOSE back and forth in onCreateView()? – HeMightBeTodd Mar 25 '12 at 21:08
  • @HeMightBeTodd: That would be rather unusual, but what you are trying to do is rather unusual. Personally, I'd be a bit more comfortable if you had two copies of the entire fragment and swapped between them via the transition. – CommonsWare Mar 25 '12 at 21:19
  • is correct in his initial assessment, this is not a productive optimization. It turns out almost all of the CPU time is simply the phone rendering the view for display, regardless of view origin. Manually caching all views to avoid garbage collection and multiple inflations from XML saved on average 20-25ms, and maximum 55ms. – HeMightBeTodd Mar 25 '12 at 21:53
0

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.

HeMightBeTodd
  • 117
  • 1
  • 9