Pull to refresh
14
0
Степан Фурдей @DOCT0R

User

Send message
Пока не делимся. Но это ж не значит, что потом не захотим :) Если сразу будет Content Provider, писать придется меньше в итоге. Но это не основаня причина, почему я люблю Content Provider'ы в связке с CursorLoader'ами. Они упрощают жизненный цикл GUI. Вам не нужно самостоятельно заботиться о том, чтобы правильно и вовремя закрыть курсоры. При изменении конфигурации и пересоздании Activity Loader не разрушается. Новый экземпляр Activity присоединится к существующему экземпляру Loader'а и получит либо уже готовый курсор, либо дождется окончания запроса, отправленного еще предыдущим экземпляром Activity. Они естественным образом добавляют асинхронность в Ваш проект. Т.е. вы создали CursorLoader, получили курсор из ContentProvider'а, отрисовали ListView к примеру. Дальше где-то в другом фрагменте данные изменились (или вообще пришли обновления от API через сервис). Тот фрагмент/сервис независимо ни от кого обратился к ContentProvider'у, поменял данные в таблице. ContentProvider отправил оповещения Вашему CursorLoader'у, что данные изменились. Ваш Loader сам перечитал данные, обратившись к ContentProvider'у, получил новый курсор, заменил курсор у ListView — и Вы получили согласованность данных в разных фрагментах при том, что фрагменты между собой никак не взаимодействовали. В планшетном приложении таких фрагментов на экране может быть десяток, и здесь их независимость, асинхронность и при этом согласованность реально спасает. Это очень хорошо масштабируемая схема. И еще один плюс, о котором я писал в прошлом комментарии — это согласованность данных на уровне хранилища. У меня в приложении 3 источника данных — SQLite, файлы и REST API. ContentProvider служит фассадом для них всех, и GUI слой понятия не имеет, где данные реально хранятся. Нужно будет добавить/убрать/изменить хранилище — я смогу это сделать без каких-либо изменений GUI, т.к. у меня есть промежуточная абстракция данных, на которой изменения будут локализованы.
Пишем сами. Мое личное мнение — андроид — не та система, где стоит создавать библиотеки над библиотеками. Родные компоненты работают достаточно неплохо, а сторонние надстройки добавляют тормозов и багов. У нас полноценный REST-протокол с GET, POST, PUT и DELETE. В случае POST, PUT, DELETE первым шагом делаются изменения в локальной БД. POST приводит к появлению новой записи с заполнением реквизитов, пришедших из Activity. Запись помечается как status = «INSERTING». PUT — меняет существующую и помечает ее как «UPDATING». Delete только помечает запись как «DELETING». Этим занимается ContentProvider. Затем ContentProvider дергает сервис, чтобы сервис в фоновом потоке вызвал API. Сам ContentProvider при этом отправляет слушателям URI этой таблички уведомление о том, что данные изменились (статусы ведь поменялись) и завершает работу, не дожидаясь ответа сервиса. При этом если в GUI реально есть слушатели этой таблички, то они ее перечитают и покажут юзеру, что с таким-то объектом сейчас идет какая-то работа. Этот элемент будет недоступен для изменений, кликов, будет отмечен серым цветом и может даже крутящееся колесико будет в версии 1.2 :) Сервис же неспеша дернет API, получит ответ, положит его в таблицу путем вызова того же Content Provider'а уже из сервиса. POST, PUT обновят уже существующую запись, DELETE окончательно ее удалит. В случае POST, PUT Content Provider также меняет статус записи на «READY». После этих изменений Content Provider снова отправляет слушателям URI этой таблички уведомление о том, что данные изменились. Элементы GUI ее перечитают и покажут юзеру, что данные опять изменились (объект готов к работе, можно убирать колесико и разрешать кликать на него). На каждом из этих этапов данные персистенты и отвязаны от жизненного цикла GUI. И дает возможность строить GUI логику максимально гибко, т.к. нигде в этом цикле мы не блокировали GUI целиком, а только отдельные элементы, связанные с непосредственно обрабатываемыми данными. Это позволяет запускать одновременно любое количество операций в фоне, если конечно логика приложения это допускает.
Сейчас работаю над проектом по паттерну B. В основном выбор на него пал как раз из-за того, что Content Provider — это фассад, сильно упрощающий логику активити и фрагментов. В GUI самое «сложное» — это отреагировать на признак «строка не синхронизирована». «Сложное» в кавычках, т.к. сложность заключается только в том, что в отдельных случаях нужно применять другие View, и все. Данные обновляются => идет оповещение слушателям => Loader'ы их перечитывают и GUI вы перерисовываете. Удобнее сложно придумать. Опять же нет проблем с выходом из активти и повторным входом в нее — у вас все результаты в кеше, вы все перечитаете и продолжите работу без потери каких-либо данных. Но самое главное — это фассад. В моем приложении данные хранятся не только в БД, а еще и в файловой системе, там довольно специфично все. И этот фасад дает шикарное разделения слоя GUI и DAO. Причем Content Provider у меня ничего не делает, кроме того, что следит за синхроноостью данных в базе, в файловой системе и вовремя вызывает сервис для синхронизации с сервером. Какая-либо логика работы с этими разношерстными данными вынесена в Content Helper'ы — промежуточный слой между Activity и Content Provider'ами. Эти хелперы отвечают за построения правильных запрсов к провайдерам и при необходимости вызывают и переиспользуют друг друга.
Сам Android такую возможность не поддерживает. Обходные варианты: нарисовать собственный layout меню, который будет показываться или прятаться по нажатию кнопки, например в режиме диалога, также можно создавать меню кодом, используя SpannableString или переопределить функционал MenuInflater'а, чтобы в момент вызова getMenuInflater().inflate(R.menu.your_activity_menu, menu); построение UI выполнялось своим кодом, а там уже можно загрузить и установить шрифт для надписи. Подробно это описано здесь, но такой способ не будет работать с библиотекой совместимости. Такой путь усеян граблями, я бы не рекомендовал его использовать.
Отдельное спасибо за упоминание метода onPrepareOptionsMenu, я его упустил в статье. Этот метод вызывается перед каждой отрисовкой меню, и было бы некрасиво вставлять в каждый цикл отрисовки сравнительно тяжелую операцию проверки состояния сети. Если говорить об onPrepareOptionsMenu, то тогда проверку состояния сети следует вынести в onCreate, и там выставлять boolean-флаг, который затем использовать в onPrepareOptionsMenu.
Для обмена списками покупок облако использовать не обязательно, достаточно просто отправить через одну из соц. сетей, как это сделано например в Shopping Список покупок
Спасибо за Ваш комментарий, обязательно учту при доработке статьи
Спасибо, отдельная картинка к статье и отдельная для скачивания действительно лучше смотрится. Доработал статью.
Действительно. Исправил, спасибо
Под артефактом тут подразумевают визуальный брак картинки. Сбоев/вылетов/замедления приложения не будет, но часть пикселей из области «bad patch» может пропасть при сжатии. Если все пиксели в тянущейся области одинаковы, как на нашей серой или белой рамках, артефактов не будет никогда. Они возможны, только если тянущаяся область содержит неоднородный рисунок.
Полностью с Вами согласен. Да, статья не про дизайн, и я старался сделать побольше «фишек» для демонстрации имеющихся возможностей.
А вот на что оно точно повлияет, так это на потребление памяти, так как картинки грузятся целиком. Поэтому да, без необходимости оставлять «bad patches» не стоит.
Давайте посмотрим на доку:
Show bad patches: Adds a red border around patch areas that may produce artifacts in the graphic when stretched. Visual coherence of your stretched image will be maintained if you eliminate all bad patches.
Тут сказано, что «bad patch» может породить артефакт при растягивании. А может и не породить. Этот инструмент не может сказать вам этого точно, он только предупреждает. Когда могут быть артефакты? Например, у нас рамка с градиентом, как в этой статье в button_focused.9.png. Допустим, мы делаем тянущимися 10 пикселей градиента, а содержимое растягивает наш элемент на 100 пикселей. Что мы увидим в результате? Вместо плавного градиента будет 10 полосок шириной по 10 одинаковых пикселей каждая. И наоборот, если градиент будет свернут в 1 пиксель, мы вместо него увидим резкий переход цвета. Это не значит, что «bad patch» в приложении ни в коем случае не должно быть. Просто нужно понимать, что может случиться, и тестировать внешний вид в разных вариантах. Кстати, среди ресурсов в самом Android таких «bad patch» полно.
Да, расскажу про локализацию и постараюсь подобрать наиболее полезные ссылки на официальную документацию
Извините за неудобства. Выделил иконку, как мог, чтобы ее можно было сохранить на свой компьютер и импортировать в проект. Если щелкнуть на пространстве между ">" и "<" правой кнопкой, то можно сохранить изображение.

Information

Rating
Does not participate
Location
Донецк, Донецкая обл., Украина
Date of birth
Registered
Activity