РЕДАКТИРОВАТЬ 12.03.14: Опубликовано несколько недель, но активности нет. Достаточно ли ясен мой вопрос? Или TL;DR? К сожалению, поскольку я новый участник, я не могу предложить награду :-(
Мой вопрос можно резюмировать так:
- Как я могу определить, что представление, содержащее асинхронно загруженные элементы (например, веб-изображения), полностью загружено и отрисовано, не прибегая к небольшому взлому — см. ниже.
- Есть ли детерминированный способ узнать, когда представление закончило загрузку, не используя просто длинное защитное значение задержки для
View.postDelayed()
?
Пожалуйста, прочитайте предысторию и особенности этого вопроса, если вы думаете, что можете помочь.
Предыстория вопроса
Я использую стороннюю библиотеку в своем приложении, чтобы попытаться сделать следующее:
- Сторонняя библиотека асинхронно загружает некоторые данные через REST API.
- Library processes JSON data and uses Picasso API to download image URLs contained within the JSON response and then load them into a customised
View
(library view, not mine) which I then display in myActivity
.- Note: the Picasso calls are encapsulated by the library that I am using and the 3rd party library opted not to use the callbacks provided by Picasso which indicates when the images have loaded. I merely have access to a callback when the underlying JSON request has returned successfully.
- После того, как пользовательский
View
загрузится и полностью отрендерится, я хочу сохранить этотView
в хранилище как PNG.
Моя проблема возникает на шаге 3: я не могу детерминистически определить, когда пользовательский View
закончил загрузку, чтобы я мог сохранить PNG после полного отображения представления. Пикассо по своему замыслу асинхронно загружает изображения в объекты Android View
, и я думаю, что в результате методы, которые я пробовал ниже, не сработали:
Используйте
GlobalLayoutListener
, чтобы определить, когда представление завершило загрузку, как предложено здесь.myView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // Try to begin saving PNG here savePNG(); });
- This didn't work as
onGlobalLayout()
fires an unpredictable number of times before theView
is completely rendered depending on how quickly the images are being loaded in the background. - Я использовал
View.postDelayed(Runnable, delay)
вonGlobalLayout()
, но, опять же, невозможно узнать, как долго должна быть задержка из-за мощности устройства и пропускной способности сети, плюс это вводит своего рода состояние гонки.
- This didn't work as
Во многих ответах на подобные вопросы о SO предлагалось просто опубликовать
savePNG()
как Runnable в очереди сообщений пользовательского интерфейса. Я думаю, что это хорошо работает для большинства сценариев, но не тогда, когда изображения асинхронно загружаются в представления через Интернет. В большинстве случаев этот подход приводит к попытке сохранить изображение до того, как представление будет полностью загружено и отображено. Конечно, я могуpostDelayed
, но это приводит к той же неопределенности в отношении длины задержки и условий гонки, что и в предыдущем пункте.Я попытался дождаться события
MessageQueue.IdleHandler.queueIdle()
, чтобы выполнитьsavePNG()
, но, к сожалению, это также происходит до того, как все ресурсы будут загружены и отображены.Сохраните последний раз, когда был вызван
onGlobalLayout
, и пусть другой поток периодически проверяет «нет обновлений в XXX мс», чтобы определить, что представление успокоилось. Это не лучшее решение, и приложение может выглядеть излишне медленным, но я, по крайней мере, мог заставить его работать.Я попытался подклассифицировать
View
и переопределитьdispatchDraw()
(предложено здесь) и перезвонить моему приложению - та же проблема, что иonGlobalLayout
- это срабатывает несколько раз, прежде чем представление будет полностью загружено и отображено.
Вау, длинный пост, но, надеюсь, это означает, что я не буду выглядеть так глупо, если есть очень очевидная «подводная лодка», которая все это время смотрела мне в лицо :-)