標籤雲

搜尋此網誌

2015/12/04

Best Practices for Background Jobs - CursorLoader

查詢 ContentProvider 是可能造成 UI 停頓的工作
官方建議我們可以使用 CursorLoader 在背景進行非同步的查詢
然後再將結果送回 activity
而當資料改變時 CursorLoader 也會自動重新進行查詢
(以下的範例是使用 support library 的 CursorLoader 版本)

要使用 CursorLoader 必須實作 LoaderManager.LoaderCallbacks interface
為了獲得像 Fragment 一樣的 CursorLoader 支援,範例這裡我們繼承 FragmentActivity

然後在得到查詢條件後使用 LoaderManager.initLoader() 進行初始化
(LoaderManager 透過 Fragment 的 getLoaderManager() 或 support 版的 getSupportLoaderManager())
如果不須查詢條件也可以直接在 onCreate 或 onCreateView 裡直接初始化
public class PhotoThumbnailFragment extends FragmentActivity implements
        LoaderManager.LoaderCallbacks<cursor> {
    ...
    private static final int URL_LOADER = 0; //loader 的給定 ID

    public String[] mFromColumns = { DataProviderContract.IMAGE_PICTURENAME_COLUMN };
    public int[] mToFields = { R.id.PictureName };
     ...
    public View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
        ...
        // 初始化 CursorLoader. The URL_LOADER 最後會傳入 onCreateLoader()
        getSupportLoaderManager().initLoader(URL_LOADER, null, this);
        ...
        ListView mListView = (ListView) findViewById(R.id.dataList);

        SimpleCursorAdapter mAdapter =
            new SimpleCursorAdapter(
                    this,                // Current context
                    R.layout.list_item,  // Layout for a single row
                    null,                // No Cursor yet
                    mFromColumns,        // Cursor columns to use
                    mToFields,           // Layout fields to use
                    0                    // No flags
            );
        mListView.setAdapter(mAdapter);
    }
    ...
    // 當系統把Loader 初始化並準備好進行查詢時會呼叫 onCreateLoader 這個 Callback
    @Override
    public Loader<cursor> onCreateLoader(int loaderID, Bundle bundle){
        //依照 loaderID 進行處理
        switch (loaderID) {
            case URL_LOADER:
                // 回傳 CursorLoader,當查詢完成時會呼叫 onLoadFinished()
                return new CursorLoader(
                        this,           // Parent activity context
                        mDataUrl,       // Table to query
                        mProjection,    // Projection to return
                        null,           // No selection clause
                        null,           // No selection arguments
                        null            // Default sort order
                );
            default:
                // 不合理的 id 就回傳 null
                return null;
        }
    }
    ...
    // 完成查詢時會呼叫 onLoadFinished
    @Override
    public void onLoadFinished(Loader<cursor> loader, Cursor cursor) {
        ...
        //更新 cursor
        mAdapter.changeCursor(cursor);
    }
    ...
    // 如果資料改變則 onLoaderReset 會被呼叫
    @Override
    public void onLoaderReset(Loader<cursor> loader) {
        // 把 adapter 中 Cursor 的 reference 清掉,防止 memory leaks.
        mAdapter.changeCursor(null);
    }
}

相關資料:
Loading Data in the Background
Running a Query with a CursorLoader

沒有留言: