【问题标题】:What is the android UI thread stack size limit and how to overcome it?什么是 android UI 线程堆栈大小限制以及如何克服它?
【发布时间】:2013-05-26 10:52:09
【问题描述】:

在绘制视图层次结构时,我得到 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 无法处理。事实上,使用 Hierarchy Viewer,我可以看到我最长的嵌套是 19 个视图 (!)

我的应用看起来有点像 Google Play 商店应用(带有滑动标签)。每个选项卡都是片段视图分页器内的嵌套片段 - 使用 v4 支持和 HoloEverywhere。显然,这就是为什么我的层次结构变得有点疯狂的原因。

我的问题:

  1. 真正的堆栈大小限制是多少?我发现无法测量 UI 线程的堆栈大小。网上有传言说是 8KB,但有没有办法在某些示例设备上准确地测量

  2. 堆栈大小限制是否随操作系统版本而变化?相同的层次结构不会在 4.0.3 设备上崩溃,但在 2.3.3 设备(相同硬件)上崩溃。这是为什么呢?

  3. 除了手动优化层次结构外,还有其他解决方案吗?我发现没有办法增加 UI 线程的 可笑 小堆栈。抱歉,60-100 堆栈帧限制是个笑话。

  4. 假设在#3 上没有奇迹解决方案,有什么建议应该在哪里进行核心层次优化?

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

【问题讨论】:

  • 一个类似于我疯狂想法#5的想法:stackoverflow.com/questions/6705425/…
  • 澄清一下,寻找筹码量背后的原因是估计有问题的市场规模。由于较新版本的操作系统似乎有更大的堆栈,我想找出我容易崩溃的地方,看看我是否可以忍受它
  • 您应该真正解决 19 级嵌套视图层次结构(这并不能真正证明自己的合理性)。 看看我能不能忍受它然后如何忍受它?您只是让设备崩溃(连同您的应用评分)? Gingerbread 占据了大约 40% 的市场份额,再加上糟糕的硬件将导致很大一部分市场无法满足您的需求(更糟糕的是,您可能正在处理随机设备崩溃)。
  • 好吧,将 19 优化到更少当然是有代价的。我应该走多低?我试图进行某种测量,这样我就可以准确计算出我想要玩的安全程度。随便说最多 15 个关卡对我来说有点过分了..

标签: android


【解决方案1】:

我相信主线程的堆栈是由 JVM 控制的 - 在 Android 的情况下 - Dalvik JVM。如果我没记错的话,相关常量在dalvik/vm/Thread.h 下的#define kDefaultStackSize 中找到

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

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

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

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

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

【讨论】:

【解决方案2】:

我会试一试thisthis,它将解决您的许多问题并帮助您进一步了解为什么在 uithread 中不需要更大的堆栈。希望对您有所帮助!

【讨论】:

    【解决方案3】:

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

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

    1. 使用 RelativeLayout 而不是嵌套 ViewGroups,尤其是 LinearLayouts 内的 LinearLayouts,以帮助扁平化视图层次结构。这不是一个万能的解决方案。事实上,嵌套RelativeLayouts 会影响性能(因为RelativeLayout 总是measure()s 两次,所以嵌套它们对测量阶段有指数影响)。

    2. 根据您的第五个问题,使用自定义视图/视图组。我听说有几个应用程序可以做到这一点,我认为甚至一些 Google 应用程序也可以做到这一点。

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

    【讨论】:

    • 搜索堆栈大小的原因是估计有问题的市场大小。由于较新版本的操作系统似乎有更大的堆栈,我想找出我容易崩溃的地方,看看我是否可以忍受它
    【解决方案4】:

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

    【讨论】:

    • 这到底有什么帮助?当您移动视图时,您是否希望事情保持不变?
    • @Luksprog 我曾经做过类似的事情。但我的观点是全屏尺寸。我只是通过在不同的视图上重绘画布源图像来使用画布缓冲区。这样我什至不必添加它们。
    • 有趣,我假设@Babibu 的建议是打破嵌套并将一些视图放在另一个视图 D 下,与顶层无关但与 B 大小相同。然后当 B 需要绘制自己,我们可以拿D的画布内容。我理解正确吗?这很有趣,但我也想知道事件.. 需要以某种方式转发它们
    • 还添加了对我最初疯狂想法5的另一种解释(在单独的答案中),告诉我你的想法
    【解决方案5】:

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

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

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

    原始绘图函数调用dispatchDraw(),它调用drawChild() - 只有这样你才能到达你孩子的draw()。这意味着每个嵌套中的 draw() 增加了 3 个函数调用。我们将尝试手动将其最小化为 1 个函数调用。

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

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

    看起来工作量太大了,简直是个骇客……

    【讨论】:

    • 这不是一个解决方案,把事情做对的麻烦并不能证明自己是合理的。
    • 我同意。它太复杂了,并不能证明自己是合理的。虽然想想很有趣 :) 而且我很确定它会完全解决堆栈问题(至少会让你有大约两倍的视图)
    猜你喜欢
    • 1970-01-01
    • 2015-11-11
    • 2011-10-02
    • 2022-07-21
    • 2014-03-12
    • 2014-10-08
    • 2016-02-15
    • 2019-09-17
    • 1970-01-01
    相关资源
    最近更新 更多