Create the UI thread manually in Android in order to increase its small stack size
Asked Answered
V

2

8

I'm struggling with StackOverflowErrors in the main UI thread (related question). My app targets Android 2.3+. On Android 4 everything is fine, but I'm getting crashes on 2.3.3 when the view layout is drawn.

The obvious answer is to optimize my view layout and reduce the number of nested views. This is a problem because the large number of views is due to me using the support library and fragments for tabs - which is the recommended approach. I don't want to change my design and drop fragments simply because an older ver of the OS has a small stack.

I'm looking for ways to increase the stack size of the UI thread. I've seen answers that this is not possible, and I understand that there is no simple setting in the manifest to do so. But, I'm still not satisfied that there is no manual workaround.

My best idea thus far:

Create a new worker thread (Thread class) with a larger stack. It is possible to increase the stack size for worker threads. Then, transform this thread somehow to the new UI thread. How?

  • Browsing through the Android sources for android.app.Activity.attach(..) I've seen that an activity attaches itself to a UI thread. Maybe there is some way to replace the thread it is attached to and change it to my "new" UI thread

  • Going a little deeper to android.app.ActivityThread.startActivityNow(..), maybe I'll be able to start my activity manually. Once the activity is created, it attaches itself automatically to the thread it is created on. Maybe if I run this from my new UI thread, it will work.

  • If I create a new UI thread, I will have to manually create the Looper for it. I'm getting a sense of what's need to be created from android.app.ActivityThread.main(..)

Other ideas:

  • Catch the StackOverflowError manually and when it happens move calls asynchronously so they have a smaller stack. I've seen this nice idea here but I was unable to make it work.

  • NDK threads (via JNI) have large stacks. I thought about using them for my benefit but I see no way to do this. All calls via JNI are performed on their own thread so I can't see how I can use NDK threads for UI access.

So..

Except for saying that this is a very bad idea altogether, any other suggestions or tips? Can you find anyone who did something similar? I couldn't..

Vargas answered 4/6, 2013 at 8:42 Comment(6)
Just interesting, how many nested views do you have?Pippy
I currently have 19. Without dropping fragments and using tricks like merge and combining layouts (which will make code disgusting) I'll save about 3-4. If I drop fragments and move to a view-only approach I'll save about 5. Anyways, being at 15 is still too much on the edge.. especially when nobody can tell exactly where the edge liesVargas
Maybe would be good to share whole layout tree and optimize it with the community help?Pippy
My design is nothing special and I've seen so many people stuck with the same problem. Even if I am successful in wiggling my way to under 15, it does not take care of the underlying problem. 19 views when using fragments should be perfectly fine.. I don't see it as being much at all. The tab fragment activity from the support library takes 5 views by itself. Then every fragment is at least 4. This means my actual work starts from 9. Having 10 views in my personal hierarchy is nothing extraordinaryVargas
can you please show an example of view with 19 levels of nested layouts?Pippy
Example of the layout: Note that this is before optimization, so you can probably shave at least 3 off without much effort. f.cloud.github.com/assets/3320805/596430/…Vargas
V
2

CommonsWare is sadly mistaken. Closer inspection shows that the default stack size on Android 2.x is 12KB and in Android 4.x has been increased to 16KB. This means the core team was aware of the small stack issue and fixed it. Sad to say, but these extra 4KB is what makes all the difference in crashes.

Furthermore, if you are using common libraries like ABS and support v4 - these libraries add the extra views to your layout whenever you are using fragments. Since most of us rely on these libraries, saying that fragments can easily be added with 1 view is simply incorrect. If you are using ABS+support lib you will pay 3 views per fragment. And you will start off with a minimum of 5 views (for ABS layout).

Regarding creating a UI thread manually, this may be possible and will probably not require to root the device or change its firmware. These were also bold statements that are probably incorrect.

Finally, I've decided against creating my own UI thread with large stack. Instead, I've chosen to completely drop the use of fragments from my code. This saved me a total of 5 views in my layout. Everything that fragments give you, can also be done by adding ViewGroups manually. It's just a little more code, but nothing too complex. It seems that fragments are a little useless and if you don't have the luxury of using them - that's the first place to cut.

Vargas answered 6/6, 2013 at 15:11 Comment(3)
"CommonsWare is sadly mistaken" -- not generally. "the default stack size on Android 2.x is 12KB and in Android 4.x has been increased to 16KB" -- good to know, though the use of stack space has also increased over the years, and so that data is of limited utility. "If you are using ABS+support lib you will pay 3 views per fragment" -- you are welcome to supply proof of your claim. Here is a project demonstrating that you are incorrect: github.com/commonsguy/cw-omnibus/tree/master/ViewPager/VPIUndercarriage
"And you will start off with a minimum of 5 views (for ABS layout)" -- the same project I link to above shows that the minimum depth is three (PhoneWindow$DecorView, LinearLayout, FrameLayout). Other views off the deepest branch do not affect stack consumption as much usually (though it will depend a bit on the widgets/containers in question). "this may be possible and will probably not require to root the device or change its firmware" -- you are welcome to provide proof of your claim.Undercarriage
quite the pissing contest isn't it :) When I have some free time I'll do my best to implement the UI stack trick without rootVargas
U
-1

On Android 4 everything is fine

No, you just haven't crashed yet. I am not aware that they increased the stack size on Android 4.x. They may have made some widgets a bit more efficient, which is allowing you to survive a bit longer, for some operations.

This is a problem because the large number of views is due to me using the support library and fragments for tabs - which is the recommended approach

You are welcome to use ordinary Views in tabs, whether those tabs are from ViewPager (and a suitable indicator), the action bar, or even the ancient TabHost. Fragments are not required.

I'm looking for ways to increase the stack size of the UI thread

This is not possible except by modifying the Android framework and forcing ~800 million Android device users at gunpoint to root their phones and replace their framework. Even if it were possible, unless you are in position to test on hundreds of devices, you will have no idea if your hack will be stable.

Anyways, being at 15 is still too much on the edge.. especially when nobody can tell exactly where the edge lies

The "edge" is 8KB of stack space, if I remember the figure correctly. Talented programmers realize that you cannot express the "edge" precisely in terms of the number of nested views in the hierarchy, as stack consumption will vary based on operations being performed and OS level.

I've seen so many people stuck with the same problem

You have a rather low value for "so many". This hasn't been a significant problem in years, based on questions asked in public support areas.

The tab fragment activity from the support library takes 5 views by itself.

Since there is nothing in Android named a "tab fragment activity", we cannot help you much with this, other than to suggest using another source of tabs. For example, ViewPager with TabPagerIndicator (from ViewPagerIndicator) should be around two nested levels along the critical path.

Then every fragment is at least 4.

Talented programmers realize that fragments do not even need to have views, and so the minimum number of views for a fragment is 0. Talented programmers also realize that fragments that do have a UI need a minimum of 1 view, not 4, because you can return any View you want from onCreateView() -- there is nothing in the semantics of onCreateView() that requires 4.

For example, this sample project uses a ViewPager with TabPagerIndicator and fragments for the tabs. From root to critical-path leaf, the entire structure has 7 levels.

Having 10 views in my personal hierarchy is nothing extraordinary

Yes, it is, once you add the 3 levels intrinsic to all activities, as 13 levels definitely start to make me nervous. In my cited example, ViewPager, its tabs, and the fragments add 4 levels on top of that. If you are saying that you want to have 10 levels of hierarchy in a tab, I doubt that there is a tab solution slim enough for that, though action bar tabs are one possibility.

Hence, your solution is:

  • Simplify your 10-level per-tab hierarchy
  • If that alone is insufficient, choose a slimmer tab implementation
Undercarriage answered 4/6, 2013 at 10:17 Comment(6)
Thanks for the detailed answer. Talented programmers rely on widely used frameworks - in my case holoeverywhere on actionbarsherlock on support library v4. My activity only has android.support.v4.view.ViewPager in its layout and that starts me up with 5 views (PhoneWindow,Linear,Frame,WindowDecor,ViewPager). My fragments are created by FragmentPagerAdapter and if they have 1 LinearView in their layout I get extra 3 views (NoSaveStateFrame,ContextMenuDecor,Linear). This 8 fragment overhead isn't negligible.. and my own layout starts from thereVargas
Android 4 obviously has some serious optimizations since I've added another 5 dummy nested views into the layout and it handled it without any problem.. so I feel rather safe there with a 5 view "buffer"Vargas
And a final comment - a decent framework should not make me live on a strict budget of 15 views. Obviously I need more, it makes my code more modular. I would expect a top limit of 40 views so most normal apps would be well well below the edge without any special consideration. I believe that some "hacking" of the UI stack can get us there, that's all. It's not really that anyone is worried about the extra 8KB of memory in my stack?! I have 1 GB of RAM, this is ridiculous!Vargas
@talkol: "Talented programmers rely on widely used frameworks - in my case holoeverywhere" -- I would not describe HoloEverywhere as "widely used". ABS is. "This 8 fragment overhead isn't negligible" -- I never said it was. That being said, you may wish to take a look at my sample, as I get 7 levels where you have 8 (not sure why). "I believe that some "hacking" of the UI stack can get us there, that's all" -- you are welcome to your opinion. I'd fire anyone who tried, as stability and reliability would be at risk, even if you find some hack for this, which should not be possible.Undercarriage
I actually think the suggested way of optimizing the number of views is the real hack. It makes the code much less readable. It encourages combining layouts which is less modular. And it's much less stable since the "edge" is so unclear. I think the main problem lies with Android flawed design and honestly would fire the person who put the constant 8KB in a UI thread stack. I'm trying to overcome his mistake, that's all.Vargas
Google increased the stack size in Android 4. See https://mcmap.net/q/520606/-what-is-the-android-ui-thread-stack-size-limit-and-how-to-overcome-it .Nims

© 2022 - 2024 — McMap. All rights reserved.