什么是android UI线程堆栈大小限制以及如何克服它?

时间:2013-05-30 18:24:42

标签: android

我正在绘制视图层次结构时收到 java.lang.StackOverflowErrors

at android.view.View.draw(View.java:6880)
at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
at android.view.View.draw(View.java:6883)
at android.view.ViewGroup.drawChild(ViewGroup.java:1646)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:1373)
...

Research指向我的视图层次结构太深,Android无法处理。实际上,使用层次结构查看器,我可以看到我最长的嵌套是 19 视图(!)

我的应用看起来有点像Google Play商店应用(带滑动标签)。每个选项卡都是片段视图寻呼机内的嵌套片段 - 使用v4支持和HoloEverywhere。显然,这就是我的等级制度变得有点疯狂的原因。

我的问题:

  1. 什么是真正的堆栈大小限制?我发现无法测量UI线程的堆栈大小。网上有传闻说8KB,但有没有办法在某些样本设备上准确测量

  2. 堆栈大小限制是否随OS ver而变化?相同的层次结构在4.0.3设备上崩溃,但在2.3.3设备上崩溃(相同的硬件)。那是为什么?

  3. 除了手动优化层次结构外,还有其他解决方案吗?我发现无法增加UI线程的荒谬的小堆栈。对不起,但60-100堆栈框架限制是一个笑话。

  4. 假设#3上没有奇迹解决方案,那么核心层次结构优化应该在何处提出建议?

  5. 疯狂的想法 - 我注意到每个视图层都添加了大约3个函数调用(View.draw,ViewGroup.dispatchDraw,ViewGroup.drawChild)。也许我可以制作自己的ViewGroup实现(自定义布局),在draw()期间在堆栈上浪费更少?

5 个答案:

答案 0 :(得分:25)

我相信主线程的堆栈是由JVM控制的 - 在Android的情况下 - Dalvik JVM。如果我没有弄错,相关的常数可以在dalvik/vm/Thread.h

下的#define kDefaultStackSize中找到

通过dalvik源历史浏览堆栈大小:

您可以拥有多少嵌套视图:

无法准确说出来。假设堆栈大小如上所述,这取决于您在最深嵌套中有多少函数调用(以及每个函数需要多少个变量等)。似乎有问题的区域是从android.view.ViewRoot.draw()开始绘制视图的时间。每个视图都会调用其子项的绘制,并且它与最深的嵌套一样深。

我至少会对上面所有边界组中出现的设备进行经验测试。似乎使用模拟器可以得到准确的结果(尽管我只将原生x86模拟器与真实设备进行了比较)。

请记住,优化实际小部件/布局的方式也可能会影响这一点。话虽如此,我相信每个布局层次结构都会占用大部分堆栈空间,增加了大约3个嵌套函数调用:draw() - > dispatchDraw() - > drawChild()这个设计在2.3 - 4.2之间没有太大变化。

答案 1 :(得分:2)

我会给thisthis一个镜头,它会解决你的许多问题并帮助你进一步理解为什么在uithread中不需要更大的堆栈。希望它有所帮助!

答案 2 :(得分:1)

我不知道堆栈大小限制是什么,坦率地说我不认为搜索它会有多大用处。正如您的第二个建议所示,它很可能取决于设备上存在哪个版本的Android和/或Dalvik VM。

至于优化布局,一些选项包括:

  1. 使用RelativeLayout而不是嵌套ViewGroups,特别是LinearLayouts中的LinearLayouts,以帮助展平视图层次结构。这不是一个通用的解决方案;事实上,嵌套RelativeLayouts可能会影响性能(因为RelativeLayout总是measure()两次,因此嵌套它们会对测量阶段产生指数影响。)

  2. 根据您的第五个问题,使用自定义视图/ ViewGroups。我听说有几个应用程序执行此操作,我认为即使是一些Google应用程序也会执行此操作。

  3. 如果您在视图层次结构中发现任何无用的子项,您可以尝试在某些布局中使用<merge>标记(但我自己并没有找到很多用途)

答案 3 :(得分:0)

疯狂的想法5 - 可能不是那么疯狂。我向你解释这个理论,然后你尝试实现它。假设我们有3个嵌套视图A&gt; B> C.而不是C嵌套在B中使它嵌套在D(一些不相关的视图)中,当B将自己绘制时,他需要调用B.draw()。当然,你可能遇到的问题是布局不好。但是有可能找到解决方案。

答案 4 :(得分:0)

更好地解释我的疯狂想法5.我不是说这是一个好主意:)但我想澄清它是如何完成的。

我们将为基本布局(LinearLayout,FrameLayout,RelativeLayout)创建我们自己的实现,并使用它们而不是原始。

新布局将扩展原始布局,但会覆盖draw()函数。

原始绘制函数调用调用dispatchDraw()的{​​{1}} - 然后才能到达您孩子的drawChild()。这意味着每个嵌套中的draw()添加了3个函数调用。我们将尝试将其最小化为1个函数调用。

这是令人作呕的部分。您熟悉draw()功能吗?理论上,我们尝试内联调用inlinedispatchDraw()。由于java并不真正支持内联,最好的方法是手动使它们内联(实际上将代码内部复制粘贴到一个恶心的函数中)。

这在哪里变得有点复杂?我们将采取的实施将来自Android来源。问题是Android版本之间的这些来源可能略有变化。因此,必须映射这些更改并在代码中创建一组开关,以便与原始行为完全相同。

看起来太多的工作,这是一个黑客的地狱..