Android - 基于 SQL Cursor 创建一个非常大的 ListView

发布于 2024-11-07 22:11:15 字数 2478 浏览 4 评论 0原文

我有一个返回数千行的 SQLite 查询,我想使用 ListView 对其进行可视化。

为了保持 UI 线程响应,我在后台线程上创建了 ListAdapter

然而,花费最多时间(并且可能导致 ANR)的语句是 ListActivity.setListAdapter ,我必须在 UI 线程上执行它......有什么建议吗?

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);
    }

}

I have a SQLite query returning thousands of rows, which I want to visualize using a ListView.

In order to keep my UI thread responsive, I create the ListAdapter on a background thread.

However the statement that takes most time (and can cause ANR) is ListActivity.setListAdapter which I have to execute on the UI thread... Any advice?

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);
    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浅黛梨妆こ 2024-11-14 22:11:15

游标是延迟加载的,因此在第一次数据访问时会加载游标 - getCount 就是这样的访问。 setAdapter 方法正在对游标调用 getCount - 存在性能问题。

  • 您可以在适配器中设置空光标,并在加载光标时显示空列表。然后使用适配器中的changeCursor方法将光标更改为新光标。
  • 例如,您可以首先在第一个查询中获取 100 行,然后在后台加载更多行并将Cursor 更改为新行。
  • 或者您可以创建自己的 Cursor 实现,它具有自己的 getCount 实现并根据请求获取所需的行。

Cursors are lazy loaded therefore at first data access the cursor is loaded - getCount is such access. The setAdapter method is invoking getCount on cursor - there is the performance issue.

  • You can set empty cursor in adapter and display empty list during loading cursor. Then use changeCursor method in adapter to change cursor to the new one.
  • You can fetch at first for example 100 rows in the first query, then load more rows in the background and changeCursor to the new one.
  • Or you can create own implementation of Cursor that has own implementation of getCount and fetches demanded rows on request.
阳光的暖冬 2024-11-14 22:11:15
  1. 考虑使用 PagedListAdapter。它与 @pawelziemba 提出的类似,但由 Android 框架提供 https://developer .android.com/reference/android/arch/paging/PagedListAdapter -- 如果您可以使用 Room 框架,您可以在 ViewHolder 中免费
  2. 加载数据异步 - 请记住,如果 viewHolder 重新绑定并缓存加载的数据,请取消工作如果需要的话
  1. consider using PagedListAdapter. it's something similar as proposed by @pawelziemba but provided by Android Framework https://developer.android.com/reference/android/arch/paging/PagedListAdapter -- if you can use Room framework you can get that for free
  2. load data async in ViewHolder - remember to cancel work if viewHolder gets rebinded and cache loaded data if needed
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文