Вкратце, проблема здесь в том, что после ротации вам вручают те же Cursor
, которые вы ранее зациклили перед вращением, но вы не учитываете его текущую позицию.
A Cursor
отслеживает и сохраняет свою позицию в своем наборе записей, так как я уверен, что вы собрали из различных move*()
методов, которые он содержит. При первом создании позиция Cursor
будет установлена прямо перед первой записью; то есть его позиция будет установлена на -1
.
Когда вы впервые запускаете приложение, LoaderManager
вызывает onCreateLoader()
, где создается экземпляр CursorLoader
, а затем заставляет его загрузить и доставить Cursor
с позицией Cursor
в -1
. В этот момент цикл while (cursor.moveToNext())
работает так же, как и ожидалось, поскольку первый вызов moveToNext()
переместит его в первую позицию (индекс 0
), а затем в каждую доступную позицию после этого, до конца.
Однако при повороте LoaderManager
определяет, что у него уже есть запрошенный Loader
(определяется по ID), который сам видит, что в него уже загружен соответствующий Cursor
, поэтому он просто сразу же доставляет тот же Cursor
объект снова. (Это главная особенность Loader
фреймворка - он не будет перезагружать ресурсы, которые у него уже есть, независимо от изменений конфигурации.) В этом суть проблемы. Это Cursor
было оставлено в последней позиции, в которую оно было перемещено перед вращением; то есть в его конце. Следовательно, Cursor
не может moveToNext()
, так что цикл while
просто никогда не запускается вообще, после начальной
onLoadFinished()
, до вращения.
Самым простым исправлением при заданной настройке было бы вручную изменить положение Cursor
самостоятельно. Например, в getItems()
измените if
на moveToFirst()
, если Cursor
не равно нулю, и измените while
на do-while
, чтобы мы случайно не пропустили первую запись. То есть:
if (cursor != null && cursor.moveToFirst()) {
do {
int columnIndexId = cursor.getColumnIndex(ItemEntry._ID);
...
} while (cursor.moveToNext());
}
При этом, когда тот же самый Cursor
объект повторно доставляется, его позиция как бы «сбрасывается» в положение 0
. Так как эта позиция находится непосредственно в первой записи, а не прямо перед ней (помните, изначально -1
), мы изменяем на do-while
, так что первый вызов moveToNext()
не пропускает первую запись в Cursor
.
Примечания:
Я бы упомянул, что можно реализовать RecyclerView.Adapter
для непосредственного получения Cursor
, аналогично старому CursorAdapter
. В этом случае Cursor
обязательно будет перемещен в методе onBindViewHolder()
в правильную позицию для каждого элемента, а отдельный ArrayList
будет ненужным. Это займет немного усилий, но перевод CursorAdapter
в RecyclerView.Adapter
не очень сложен. В качестве альтернативы, конечно, уже есть решения. (Например, возможно, этот , хотя я не могу за это ручаться, я часто вижу доверенного друга, который часто его рекомендует).
Я бы также упомянул, что нативный Loader
фреймворк устарел, в пользу более нового ViewModel
/ LiveData
фреймворка архитектуры в библиотеках поддержки . Однако, похоже, что новейшая библиотека androidx имеет свою собственную улучшенную структуру Loader
, которая представляет собой простую оболочку для указанной установки ViewModel
/ LiveData
. Это, кажется, хороший и простой способ использовать известные конструкции Loader
, но при этом использовать преимущества последних усовершенствований архитектуры.