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

Является ли план Android действительно экспоненциально жестким?

Некоторые обычно используемые макеты для Android, такие как RelativeLayout и LinearLayout (когда веса отличны от нуля) имеют onMeasure() реализации, которые измеряют своих детей дважды, что приводит к экспоненциальному времени работы при вложенности. Это легко проверить, испустив записи журнала из листа View onMeasure()... он называется 2 ^ глубиной раз.

Может кто-нибудь дать четкое и конкретное описание, почему это так? Самое главное, это экспоненциальное поведение из-за важной части общего контракта, или это просто деталь реализации который может быть оптимизирован? Если это считается неизбежным, просьба привести пример, который требует его.

Такой пример очень помог бы мне и другим которые ворчат, что "держите свои макеты мелкие", мандат обременителен и кто задается вопросом будет ли это просто еще не оптимальные алгоритмы в основных библиотеках, или действительно ли существует фундаментальная трудность блокировка исправления.

Возможно, минимальный пример будет состоять кнопки внутри LinearLayout внутри другого LinearLayout (с match_parent и весом = 1 везде, чтобы вызвать полное экспоненциальное поведение), с некоторыми дополнительными параметрами или обстоятельствами что ясно, что все четыре вызова to Button.onMeasure() действительно значимы и необходимы.

Мое первое предположение заключалось бы в том, что только два линейных действительно необходимы обходы - первый обход, чтобы собрать все предпочтительные размеры, второй обход для распределения провисания и/или усадку. Другие двигатели компоновки в мире, такие как Tex и Swing похоже, способны регулярно обрабатывать очень глубокие иерархии имея множество ограничений выравнивания и растяжек, без какого-либо экспоненциального раздутия, и я представляю, как они работают.

Обратите внимание: мне не нужны ответы, объясняющие, как происходит экспоненциальное раздутие. Я понимаю, что, и уже было несколько должностей, где это было спросил и ответил:

Мой вопрос заключается в том, является ли рекурсивное двойное измерение принципиально необходимо/обосновано, и если да, то я хотел бы получить ясное объяснение/пример, показывающий, почему.

Edit 2013/8/22: Я думаю, может быть, мой вопрос все еще не проходит. Я попытаюсь прояснить и объяснить свою мотивацию, более смело на этот раз.

Макет не является экспоненциально трудной задачей, о чем свидетельствуют эффективные механизмы компоновки в мире, такие как Tex и Swing.

Итак, что случилось с LinearLayout, и как сообщество разработчиков Android будет действовать в ответ? Я прошу не в духе возложения вины, а скорее понять и решить, как лучше всего двигаться вперед.

Я могу представить 4 возможности:

  • Исправить ошибку производительности в основной библиотеке, не меняя контрактов
  • Измените контракты по мере необходимости и исправьте ошибку производительности в основная библиотека
  • Напишите альтернативу LinearLayout, которая имеет (а именно, распределение лишнего пространства среди детей в определенных пропорциях), но без ошибки производительности и использования его для новых приложений.
  • Продолжайте микроуровневать наши макеты, чтобы ошибка производительности для остальной части нашей карьеры развития Android.

(4) не является серьезным вариантом для меня лично. Кроме того, мне кажется ясным, что изменение поведения LinearLayout на данный момент нецелесообразно, поэтому я не верю (2) также является серьезным вариантом.

Это оставляет (1) и (3). Я способен и желаю сделать это лично, но какой? Очевидно, что (1) гораздо предпочтительнее, если возможно - так, возможно ли это? Кажется, это ключевой вопрос блокировки, на который нужно ответить чтобы определить, как двигаться вперед.

Я провел некоторое время в базовом коде и документ, и это не становится ясным, поэтому я задаю здесь вопрос.

4b9b3361

Ответ 1

С точки зрения измерения детей в два раза, я понимаю, что это то, что происходит с LinearLayouts, особенно когда задействованы веса. Лучшее объяснение, которое я нашел для этого, происходит от RomainGuy в одной из его презентаций.

У него есть слайд об этом и кратко говорит об этом в 17:45. Не стесняйтесь перематывать, чтобы получить немного контекста. Вы можете найти видео, на которое я ссылаюсь здесь: Devoxx'10 - Погружение в Android

В основном он говорит, что на первом проходе они вычисляют общую ширину или высоту в зависимости от ориентации LinearLayout, добавляют веса детей и выясняют, сколько места осталось, затем на втором проходе с эту информацию они могут надлежащим образом разделить все остальное пространство на всех детей. Достаточно просто.

Я также хотел бы указать на то, что да, хотя очень верно, что иерархия иерархии макетов имеет меньшую производительность, если вы добавляете только 1 или 2 дополнительных слоя, вы, вероятно, не увидите большое влияние на производительность для пользователя. Как только он выложил, это было сделано. Даже в ListView, если вы правильно используете данный "convertView" и настроили ViewHolder, вы получите хорошую производительность.

Я бы посоветовал вам использовать DDMS и сделать дамп компоновки некоторых приложений Google. Они очень сложны и часто бывают на удивление глубокими, но они по-прежнему получают хорошие результаты. Не будьте глупыми с вашими макетами, но если это сэкономит вам время, добавление дополнительного макета - это не конец света.

Ответ 2

Отсюда: http://developer.android.com/guide/topics/ui/how-android-draws.html

Родительский вид может вызывать меру() более одного раза на своих дочерних элементах. Например, родитель может измерять каждый ребенок один раз с неопределенными измерениями, чтобы узнать, насколько они велики, а затем снова называть меру() с реальными числами, если сумма всех дочерних неограниченных размеров слишком велика или слишком мала ( то есть, если дети не соглашаются между собой относительно того, сколько места они получают, родитель будет вмешиваться и устанавливать правила на втором проходе).

Кажется, они рассматривают проход измерений как диалог между родителем и детьми. Другими словами, они выбрали максимальную гибкость вместо оптимизации. По-прежнему кажется, что они могут оптимизировать базовые макеты.

Ответ 3

Я пришел сюда сегодня вечером, чтобы спросить об этом. Это разочаровывает то, что никто, кажется, даже не понимает ваш вопрос. Подумав об этом еще пару часов, я думаю, что я знаю ответ.

Рассмотрим следующий пример:

enter image description here

Красная коробка представляет собой LinearLayout с вертикальной ориентацией. Зеленая коробка представляет другой LinearLayout с горизонтальной ориентацией. Я ожидал бы, что макет будет действовать следующим образом:

1) Красный LinearLayout будет измерять высоты зеленого LinearLayout и большого синего виджета внизу, затем сравнить их веса, соответственно разделить любое оставшееся вертикальное пространство и снова измерить детей. (Важно знать здесь, что "измерение" представления фактически устанавливает его размер.)

2) Зеленый LinearLayout затем будет измерять ширину трех своих детей, сравнить их веса, разделить горизонтальное пространство и "измерить" снова.

Ловушка заключается в том, что для того, чтобы красный макет мог измерять высоту зеленого макета, зеленый макет должен знать высоту его детей.

Теперь вы можете подумать, что LinearLayout будет достаточно умным, чтобы оптимизировать многие его вычисления. Например, нет логической причины, чтобы зеленый макет измерял ширину своих детей при определении собственной высоты. И если все его дети имеют высоту fill_parent, тогда вообще не нужно выполнять какие-либо вычисления.

Но API не позволяет LinearLayout быть таким умным. Основная проблема заключается в том, что невозможно измерить только один размер представления. Вы можете получить их отдельно после того, как они были измерены, но фактическое измерение выполняется Просмотр # onMeasure (int, int). Аргументы этого метода закодированы с помощью View.MeasureSpec, и нет способа кодировать "игнорировать это измерение". Таким образом, зеленый LinearLayout глупо вычисляет оба измерения всех своих детей, когда красный макет его измеряет, а затем повторяет весь процесс снова, когда он делает свой собственный макет. Ширины не будут меняться во второй раз, но они все равно должны быть пересчитаны, потому что, опять же, нет способа сообщить макету или его дочерним объектам только одно измерение.

Итак, чтобы ответить на ваш вопрос... нет, это необязательно должно быть таким образом, по крайней мере для многих распространенных случаев использования. Это недостаток Android API.

Если Google будет добавлять новые методы к View для измерения размеров отдельно, реализация по умолчанию должна опираться на onMeasure(int, int), что может быть еще хуже для производительности. Но если в кодировке View.MeasureSpec есть неиспользуемые биты, тогда может быть возможно добавить флаг "ignore", который позволит оптимизировать будущие версии LinearLayout.