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

我正在绘制视图层次结构时收到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) ... 

研究指出我的视图层次结构太深,Android无法处理。 实际上,使用Hierarchy Viewer ,我可以看到我最长的嵌套是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()期间在堆栈上浪费更少?

Solutions Collecting From Web of "什么是android UI线程堆栈大小限制以及如何克服它?"

我相信主线程的堆栈是由JVM控制的 – 在Android的情况下 – Dalvik JVM。 如果我没有弄错的相关常量dalvik/vm/Thread.h#define kDefaultStackSize下的dalvik/vm/Thread.h #define kDefaultStackSize

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

  • API 3( Android 1.5 )= 8KB

  • API 4-10( Android 1.6 – Android 2.3.7 )= 12KB

  • API 14-17( Android 4.0 – Android 4.2.2 )= 16KB

那么你可以拥有多少嵌套视图:

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

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

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

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

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

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

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

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

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

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

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

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

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

原始的draw函数调用dispatchDraw()调用drawChild() – 然后才能进入你孩子的draw() 。 这意味着每个嵌套中的draw()会添加3个函数调用。 我们将尝试手动将其最小化为1个函数调用。

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

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

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