我正在绘制视图层次结构时收到 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。显然,这就是我的等级制度变得有点疯狂的原因。
我的问题:
什么是真正的堆栈大小限制?我发现无法测量UI线程的堆栈大小。网上有传闻说8KB,但有没有办法在某些样本设备上准确测量
堆栈大小限制是否随OS ver而变化?相同的层次结构在4.0.3设备上不崩溃,但在2.3.3设备上崩溃(相同的硬件)。那是为什么?
除了手动优化层次结构外,还有其他解决方案吗?我发现无法增加UI线程的荒谬的小堆栈。对不起,但60-100堆栈框架限制是一个笑话。
假设#3上没有奇迹解决方案,那么核心层次结构优化应该在何处提出建议?
疯狂的想法 - 我注意到每个视图层都添加了大约3个函数调用(View.draw,ViewGroup.dispatchDraw,ViewGroup.drawChild)。也许我可以制作自己的ViewGroup实现(自定义布局),在draw()期间在堆栈上浪费更少?
答案 0 :(得分:25)
我相信主线程的堆栈是由JVM控制的 - 在Android的情况下 - Dalvik JVM。如果我没有弄错,相关的常数可以在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
您可以拥有多少嵌套视图:
无法准确说出来。假设堆栈大小如上所述,这取决于您在最深嵌套中有多少函数调用(以及每个函数需要多少个变量等)。似乎有问题的区域是从android.view.ViewRoot.draw()
开始绘制视图的时间。每个视图都会调用其子项的绘制,并且它与最深的嵌套一样深。
我至少会对上面所有边界组中出现的设备进行经验测试。似乎使用模拟器可以得到准确的结果(尽管我只将原生x86模拟器与真实设备进行了比较)。
请记住,优化实际小部件/布局的方式也可能会影响这一点。话虽如此,我相信每个布局层次结构都会占用大部分堆栈空间,增加了大约3个嵌套函数调用:draw()
- > dispatchDraw()
- > drawChild()
这个设计在2.3 - 4.2之间没有太大变化。
答案 1 :(得分:2)
答案 2 :(得分:1)
我不知道堆栈大小限制是什么,坦率地说我不认为搜索它会有多大用处。正如您的第二个建议所示,它很可能取决于设备上存在哪个版本的Android和/或Dalvik VM。
至于优化布局,一些选项包括:
使用RelativeLayout而不是嵌套ViewGroups,特别是LinearLayouts中的LinearLayouts,以帮助展平视图层次结构。这不是一个通用的解决方案;事实上,嵌套RelativeLayouts可能会影响性能(因为RelativeLayout总是measure()
两次,因此嵌套它们会对测量阶段产生指数影响。)
根据您的第五个问题,使用自定义视图/ ViewGroups。我听说有几个应用程序执行此操作,我认为即使是一些Google应用程序也会执行此操作。
<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()
功能吗?理论上,我们尝试内联调用inline
和dispatchDraw()
。由于java并不真正支持内联,最好的方法是手动使它们内联(实际上将代码内部复制粘贴到一个恶心的函数中)。
这在哪里变得有点复杂?我们将采取的实施将来自Android来源。问题是Android版本之间的这些来源可能略有变化。因此,必须映射这些更改并在代码中创建一组开关,以便与原始行为完全相同。
看起来太多的工作,这是一个黑客的地狱..