Итак, в этой заметке я хотел бы поделиться своим негативным опытом использования ContentProvider'ов в качестве источника данных внутри приложения. Почему же собственно их использование для доступа к данным внутри программы неоправданно?
Обработка исключений
Самая главная, на мой взгляд, проблема состоит в том, что getContentResolver().query(..) возвращает либо курсор с данными, либо null. Если исключение появится внутри ContentProvider'а, вызывающий код никак об этом не узнает. Максимум, что можно узнать, выполнив проверку на null — это сам факт того, что что-то пошло не так.
Для работы внутри одного приложения такой подход решительно не годится, так как порой исключение поднимается с самого нижнего уровня данных вплоть до пользовательского интерфейса. Например, сообщение о недоступности веб-сервиса или об ошибке авторизации.
Конечно же, есть workaround. Можно записывать в курсор информацию об ошибке, и обрабатывать её в вызывающем коде. Но это слишком искусственное решение.
Сложные структуры данных
Cursor хорош ровно до тех пор, пока возвращаемые данные представимы в виде таблицы. Но что делать, если необходима более сложная структура?
Например, веб-сервис может возвращаться данные постранично, при этом на первой странице будет какая-то мета-информация, хотя бы количество страниц. Можно соединять несколько курсоров в один, а в вызывающем коде разбирать их обратно. Но это сильно усложнит логику как на уровне модели, так и на уровне презентера.
Прогресс выполнения
Часто возникает необходимость отследить прогресс выполнения операции. Например, чтение большого объёма данных из файла. ContentProvider'ы такой возможности не предоставляют.
Можно завести приватное поле, писать в него текущий прогресс и возвращать его отдельным запросом. Но тут сразу возникнут проблемы с многопоточностью и параллельным выполнением провайдером нескольких одинаковых запросов от разных клиентов.
Остановка выполнения
Код, выполняемый в ContentProvider'е, скорее всего не удастся остановить. Если же не использовать AsyncQueryHandler, то вообще никакого контроля за выполнением операции у нас не будет.
Обходной путь примерно тот же, что в предыдущей проблеме — заводим приватное поле-флаг, и переключаем его, когда запрос нужно остановить. В запросе же постоянно проверяем состояние флага. Не подходит это решение по всё тем же причинам.
Заключение
Вывод из вышесказанного я могу сделать такой. ContentProvider'ы имеет смысл использовать, только если внутри них будут выполняться атомарные операции, не занимающие много времени и возвращающие строго однородные данные, которые могут быть использованы другими приложениями. Вот всех других случаях использование ContetProvider'ов мне представляется неоправданным.

