Android - создайте очень большой ListView на основе курсора SQL.

У меня есть запрос SQLite, возвращающий тысячи строк, которые я хочу визуализировать с помощью ListView.

Чтобы мой поток пользовательского интерфейса оставался отзывчивым, я создаю ListAdapter в фоновом потоке.

Однако оператор, который занимает больше всего времени (и может вызвать ANR), - это ListActivity.setListAdapter, который я должен выполнить в потоке пользовательского интерфейса ... Есть какие-нибудь советы?

public class CursorTestActivity extends ListActivity {

    private static final String LOGTAG = "DBTEST";

    private DatabaseManager mDbManager;
    private Cursor mCursor;
    private HandlerThread mIOWorkerThread;
    private Handler mIOHandler;
    private Handler mUIHandler;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mDbManager = new DatabaseManager(this);

        mUIHandler = new Handler();
        createIOWorkerThread();

        log("creating cursor");
        mCursor = mDbManager.getCursor(); // does db.query(...)
        startManagingCursor(mCursor);

        mIOHandler.post(new Runnable() {
            @Override
            public void run() {
                setMyListAdapter();
            }
        });
        log("onCreate done");
    }

    private void setMyListAdapter() {
        log("constructing adapter");
        // CustomCursorAdapter implements bindView and newView
        final CustomCursorAdapter listAdapter = new CustomCursorAdapter(this,
                mCursor, false);
        log("finished constructing adapter");
        mUIHandler.post(new Runnable() {
            @Override
            public void run() {
                log("setting list adapter");
                setListAdapter(listAdapter); // gets slower the more rows are returned
                log("setting content view");
                setContentView(R.layout.main);
                log("done setting content view");
            }
        });
    }

    private void createIOWorkerThread() {
        mIOWorkerThread = new HandlerThread("io_thread");
        mIOWorkerThread.start();
        Looper looper = mIOWorkerThread.getLooper();
        mIOHandler = new Handler(looper);
    }

    private void destroyIOWorkerThread() {
        if (mIOWorkerThread == null)
            return;
        Looper looper = mIOWorkerThread.getLooper();
        if (looper != null) {
            looper.quit();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mDbManager != null)
            mDbManager.close();
        destroyIOWorkerThread();
    }

    private static void log(String s) {
        Log.d(LOGTAG, s);
    }

}

person Community    schedule 14.05.2011    source источник


Ответы (2)


Курсоры загружаются лениво, поэтому при первом доступе к данным загружается курсор - такой доступ является getCount. Метод setAdapter вызывает getCount для курсора - это проблема производительности.

  • Вы можете установить пустой курсор в адаптере и отображать пустой список во время загрузки курсора. Затем используйте метод changeCursor в адаптере, чтобы изменить курсор на новый.
  • Вы можете сначала получить, например, 100 строк в первом запросе, затем загрузить больше строк в фоновом режиме и изменить курсор на новый.
  • Или вы можете создать собственную реализацию Cursor, которая имеет собственную реализацию getCount и извлекает требуемые строки по запросу.
person pawelzieba    schedule 14.05.2011

  1. рассмотрите возможность использования PagedListAdapter. это что-то похожее на предложение @pawelziemba, но предоставленное Android Framework https://developer.android.com/reference/android/arch/paging/PagedListAdapter - если вы можете использовать платформу Room, вы можете получить это бесплатно
  2. загружать данные асинхронно в ViewHolder - не забудьте отменить работу, если viewHolder будет повторно привязан, и кешировать загруженные данные, если необходимо
person Filipkowicz    schedule 28.05.2020