Pull to refresh

Тёмная сторона ContentProvider'ов

Reading time2 min
Views12K
ContentProvider — класс Android для обмена данными между приложениями. Именно так и написано в явадоках: A content provider is only required if you need to share data between multiple applications. Но кто же читает документацию, пока всё работает? Очевидно, только тот, кто набил достаточно шишек, наступая на всевозможные грабли.

Итак, в этой заметке я хотел бы поделиться своим негативным опытом использования ContentProvider'ов в качестве источника данных внутри приложения. Почему же собственно их использование для доступа к данным внутри программы неоправданно?


Обработка исключений

Самая главная, на мой взгляд, проблема состоит в том, что getContentResolver().query(..) возвращает либо курсор с данными, либо null. Если исключение появится внутри ContentProvider'а, вызывающий код никак об этом не узнает. Максимум, что можно узнать, выполнив проверку на null — это сам факт того, что что-то пошло не так.

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

Конечно же, есть workaround. Можно записывать в курсор информацию об ошибке, и обрабатывать её в вызывающем коде. Но это слишком искусственное решение.

Сложные структуры данных

Cursor хорош ровно до тех пор, пока возвращаемые данные представимы в виде таблицы. Но что делать, если необходима более сложная структура?

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

Прогресс выполнения

Часто возникает необходимость отследить прогресс выполнения операции. Например, чтение большого объёма данных из файла. ContentProvider'ы такой возможности не предоставляют.

Можно завести приватное поле, писать в него текущий прогресс и возвращать его отдельным запросом. Но тут сразу возникнут проблемы с многопоточностью и параллельным выполнением провайдером нескольких одинаковых запросов от разных клиентов.

Остановка выполнения

Код, выполняемый в ContentProvider'е, скорее всего не удастся остановить. Если же не использовать AsyncQueryHandler, то вообще никакого контроля за выполнением операции у нас не будет.

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

Заключение



Вывод из вышесказанного я могу сделать такой. ContentProvider'ы имеет смысл использовать, только если внутри них будут выполняться атомарные операции, не занимающие много времени и возвращающие строго однородные данные, которые могут быть использованы другими приложениями. Вот всех других случаях использование ContetProvider'ов мне представляется неоправданным.
Tags:
Hubs:
Total votes 12: ↑7 and ↓5+2
Comments7

Articles