Подтвердить что ты не робот

Каков размер размера стека нити пользовательского интерфейса 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)
...

Исследование указывает на то, что иерархия моего представления слишком глубока для Android. Действительно, используя Иерархический просмотрщик, я вижу, что мое самое длинное вложение - 19 (!)

Мое приложение выглядит как приложение Google Play Store (с вкладками салфетки). Каждая вкладка представляет собой вложенный фрагмент внутри плейера просмотра фрагментов - с поддержкой v4 и HoloEverywhere. Очевидно, именно поэтому моя иерархия немного сошла с ума.

Мои вопросы:

  • Каков реальный размер размера стека? Я не нашел способа измерить размер стека потока пользовательского интерфейса. Некоторые слухи в сети говорят, что 8 КБ, но есть ли способ правильно измерить это на некоторых образцах?

  • Изменяется ли ограничение размера стека с ОС ver? Такая же иерархия не сбой на устройстве 4.0.3, а сбой на устройстве 2.3.3 (идентичное оборудование). Почему это?

  • Есть ли какое-либо решение, кроме оптимизации иерархии вручную? Я не нашел возможности увеличить смехотворно небольшой стек потока пользовательского интерфейса. Извините, но ограничение кадров в 60-100 кадров - это шутка.

  • Предполагая, нет ли чудо-решение на # 3, какие-либо рекомендации о том, где должна выполняться оптимизация иерархии ядра?

  • Сумасшедшая идея - я заметил, что каждый уровень представления добавляет около 3 вызовов функций (View.draw, ViewGroup.dispatchDraw, ViewGroup.drawChild). Может быть, я могу сделать свою собственную реализацию ViewGroup (настраиваемые макеты), которая менее расточительна для стека во время draw()?

4b9b3361

Ответ 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

Я бы дал этот и этот выстрел, он решит многие ваши вопросы и поможет вам понять, почему в uithread не нужен большой стек. Надеюсь, это поможет!

Ответ 3

Я не знаю, что такое ограничение размера стека, и, честно говоря, я не думаю, что поиск этого будет очень полезен. Как предполагает ваше второе предложение, это может сильно зависеть от того, какая версия Android и/или Dalvik VM присутствует на устройстве.

Что касается оптимизации ваших макетов, некоторые параметры включают в себя:

  • Используйте RelativeLayout вместо вложенности ViewGroups, особенно LinearLayouts внутри LinearLayouts, чтобы помочь сгладить вашу иерархию просмотров. Это не универсальное решение; на самом деле, вложенные RelativeLayouts могут помешать работе (потому что RelativeLayout всегда measure() дважды, поэтому их вложенность оказывает экспоненциальное влияние на фазу измерения).

  • Используйте пользовательские представления/группы представлений в соответствии с вашим пятым вопросом. Я слышал о нескольких приложениях, которые это делают, и я думаю, что даже некоторые из приложений Google делают это.

  • Если вы обнаружите в своей иерархии вид бесполезных детей, вы можете попробовать использовать тег <merge> в некоторых своих макетах (я сам не нашел много применений для них).

Ответ 4

Сумасшедшая идея 5 - Может быть, это не так безумно. Я объясняю вам эту теорию, и вы пытаетесь каким-то образом реализовать ее. Допустим, у нас есть 3 вложенных представления A > B > C. Вместо того, чтобы C быть вложенным в B, он вложен в D (какое-то несвязанное представление), и когда B пойдет, чтобы привлечь его, ему нужно вызвать B.draw(). Конечно, проблемы, с которыми вы можете столкнуться, - плохая компоновка. Но для этого можно найти решения.

Ответ 5

Лучшее объяснение моей сумасшедшей идеи 5. Я не говорю, что это хорошая идея:), но я хотел уточнить, как это можно сделать.

Мы создадим собственные реализации базовых макетов (LinearLayout, FrameLayout, RelativeLayout) и используем их вместо оригиналов.

Новые макеты расширят исходные, но переопределяют функцию draw().

Оригинальная функция draw вызывает dispatchDraw(), которая вызывает drawChild() - и только затем вы попадаете в draw() вашего ребенка. Это означает, что draw() в каждом гнездо добавляет 3 вызова функций. Мы попытаемся минимизировать его в 1 вызов функции вручную.

Это отвратительная часть. Вы знакомы с функциями inline? Теоретически мы пытаемся сделать вызовы dispatchDraw() и drawChild() inline. Поскольку java действительно не поддерживает встроенный интерфейс, лучшим способом было бы сделать их вручную (фактически скопируйте код внутри в одну отвратительную функцию).

Где это становится немного сложнее? Реализация, которую мы будем использовать, будет поступать из Android-источников. Проблема в том, что эти источники, возможно, немного изменились между версиями Android. Поэтому нужно было бы сопоставить эти изменения и сделать набор переключателей в коде, чтобы вести себя точно так же, как в оригинале.

Кажется, слишком много работы, и это один адский взлом.