活動——裝載程序

1. 裝載器API概要


2. 在應用中使用裝載器

    2.1 啓動裝載器

    2.2 重啓裝載器

    2.3 使用LoaderManager回調


3. 實例

    3.1 更多實例



在android 3.0中引入了裝載器,它使得在活動或碎片內異步加載數據變得容易。裝載程序有如下特性:

  • 它們對每個活動和碎片都是可用的。
  • 它們提供異步的數據加載。
  • 它們監控着活動和碎片的數據源,並在數據改變時遞送新結果。
  • 配置改變後,當活動和碎片被重建時,它們自動重新連接到最後裝載器的遊標。因此,活動和碎片不必再次查詢它們的數據。

1. 裝載器API概要



在應用裏使用裝載器的過程中可能有多個類和接口被調用。它們被總結在下面的這個表中:

 類/接口

 描述

 



LoaderManager

ActivityFragment有關以管理一個或多個Loader實例的抽象基類。它幫助應用管理連同ActivityFragment生命週期長時間運行的操作;和CursorLoader一起是最常見的使用,然而應用可以自由地寫它們自己的裝載器來加載別的數據類型。

每個活動或碎片僅有一個LoaderManager。但是一個LoaderManager可以擁有多個裝載器。

LoaderManager.LoaderCallbacks

LoaderManager交互的客戶端回調接口。例如,可以使用onCreateLoader()回調方法創建一個新的裝載器。

 Loader

執行異步數據加載的抽象類。它是裝載器的基類。通常,你將使用CursorLoader,但是你也可以實現你自己的子類。當裝載器活動時,它們應該監控它們的數據源並且當內容改變時遞送新的結果

 AsyncTaskLoader

提供一個AsyncTask來執行工作的抽象裝載器。

 CursorLoader

AsyncTaskLoader的子類,它查詢ContentResolver並返回一個Cursor。此類實現了Loader協議,並以標準的方式查詢遊標,創建AsyncTaskLoader在後臺線程中執行遊標查詢,這樣做是爲了不阻塞應用的UI。使用此裝載器是從ContentProvider異步地加載數據的最好方式,而不是通過碎片或活動的API執行託管查詢。


上表中的類和接口是你在應用中用來實現裝載器的基本組件。你不需要所有這些爲你創建每個裝載機,但是你通常需要一個LoaderManager引用以便初始化裝載器以及Loader類的實現,譬如CursorLoader。下面的章節展示在應用中如何使用這些類和接口。


2. 在應用中實例裝載器



本章節描述在Android應用中如何使用裝載器。使用裝載器的應用基本上包括以下部分:



2.1 啓動裝載器


ActivityFragment裏,LoaderManager管理一個或多個Loader實例。 每個活動或碎片僅有一個LoaderManager


通常,在活動的onCreate()方法內初始化Loader或者是在碎片的onActivityCreated()方法內。像下面這樣做:


// 準備裝載器。重連一個現有的裝載器,或者啓動一個新的。
getLoaderManager().initLoader(0, null, this);

initLoader()方法攜帶以下參數:

initLoader()調用確保了裝載器被初始化且是激活的。 此調用可能有兩種結果:call ensures that a loader is initialized and active. It has two possible out comes:

在任何一種情況下,給定的LoaderManager.LoaderCallbacks實現與裝載器是相關聯的,並且當裝載器狀態改變時被調用。如果在此調用點裝載器處於它的啓動狀態,且請求的裝載器已經存在,那麼系統則立即調用onLoadFinished()(在initLoader()期間),所以你必須爲這種情的出現做好準備。更多次回調的討論,請查閱onLoadFinished


注意, initLoader()方法返回被創建的Loader,但是你不需要獲得它的引用。LoaderManager自動管理着裝載器的生命。LoaderManager啓動並在必要時停止加載,而且維持着裝載器的狀態和它有關聯的內容。這意味着,很少與裝載器直接交互(不過對於使用裝載器方法來微調裝載器行爲的例子,請查閱LoaderThrottle實例)。當特定事件發生時,通常你必須使用LoaderManager.LoaderCallbacks方法來介入加載過程。對於更多該主題的討論,請查閱使用LoaderManager回調章節。


2.2 重啓裝載器


如上所示,當使用initLoader()時,它使用一個具有指定ID的現有裝載器,如果存在一個的話。如果沒有,則創建一個。不過有時你打算丟棄舊的數據然後重新開始。


爲了丟棄舊數據,你需要使用restartLoader()。例如,下面這個SearchView.OnQueryTextListener實現在用戶的查詢數據發生變化時重啓了裝載器:

public boolean onQueryTextChanged(String newText) {
    // 在當動作欄搜索文本發生變化時被調用。
    // 更新搜索過濾,然後重啓裝載器用這個過濾器來執行一條新的查詢。
    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getLoaderManager().restartLoader(0, null, this);
    return true;
}



2.3 使用LoaderManager回調


LoaderManager.LoaderCallbacks是一個允許客戶端與LoaderManager交互的回調接口。

在特殊的CursorLoader內,裝載器被希望用來在客戶端被停止後獲取他們的數據。這允許應用跨越活動或碎片的onStop()onStart()方法來保持它們的數據,所以當用戶返還到應用時,他們不必等待數據重新加載。當知道何時創建一個新的裝載器時,使用LoaderManager.LoaderCallbacks接口方法,然後告訴應用何時停止使用裝載器的數據。

LoaderManager.LoaderCallbacks接口包括這些方法:

  • onLoaderReset()  — 當先前的裝載器正在重置時調用,因此使得它的數據不可用。

這些方法在接下來的章節裏有更爲詳細地描述。


onCreateLoader

當試圖訪問裝載器時(例如,通過initLoader()),它來檢查通過制定ID的裝載器是否存在。如果不存在,它觸發LoaderManager.LoaderCallbacksonCreateLoader()方法。這就是創建新的裝載器的地方。通常,這可能是CursorLoader,但是你可以實現你自己的Loader子類。


在此例中Loader回調方法創建一個CursorLoader。創建它必須使用CursorLoader的構造函數,它要求一套完整的需要來執行ContentProvider查詢的信息。它尤其需要:

  • uri — 檢索內容的URI。
  • projection — 返回哪些列的清單。傳遞null將返回所以列,但效率不高。
  • selection — 一個聲明返回哪些行的過濾器,其格式如同SQL WHERE子句(不包括WHERE本身)。傳遞null將返回給定URI的全部行。
  • selectionArgs — 在selection中可以包含 ?符號,這將被來自selectionArgs的值所替代,它們按順序出現在selection中。這些值限制爲String。
  • sortOrder — 如何讀行排序,格式如同SQL ORDER BY字句(不包括ORDER BY本身)。傳遞null將使用默認的存儲順序,它可能是無序的。

例如:

 // 如果非空,它是用戶提供的當前過濾器。
String mCurFilter;
...
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
    // 在需要創建一個新的裝載器時調用它。
    // 該例子中只有一個裝載器,所以不用在乎ID。
    // 首先,依當前我們是否正在過濾來選擇基準URI來用。
    Uri baseUri;
    if (mCurFilter != null) {
        baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                  Uri.encode(mCurFilter));
    } else {
        baseUri = Contacts.CONTENT_URI;
    }

    // 現在創建並返回一個CursorLoader,它將負責創建正被顯示的數據遊標。
    String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
            + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
            + Contacts.DISPLAY_NAME + " != '' ))";
    return new CursorLoader(getActivity(), baseUri,
            CONTACTS_SUMMARY_PROJECTION, select, null,
            Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}



onLoaderReset


當先前創建的裝載器正在被重置時調用這個方法,因此致使它的數據不可用。通過此函數可以知道數據何時將要被釋放,這樣你就可以移除你對它的引用。


下面的實現以一個null值調用swapCursor()

// 這是用來顯示列表數據的適配器。
SimpleCursorAdapter mAdapter;
...

public void onLoaderReset(Loader<Cursor> loader) {
    //當最後提供給onLoadFinished()的Cursor將要被關閉時被調用。我們需要確保不再使用它。
    mAdapter.swapCursor(null);
}


3. 實例



作爲實例,下面是一個Fragment的完整實現,它顯示了一個ListView,其包含有對聯繫人內容提供者進行查詢的結果。實現使用CursorLoader來管理在提供者上的查詢。


對於一個訪問用戶聯繫人的應用,如此例所示,它的清單文件中必須包括READ_CONTACTS權限。


public static class CursorLoaderListFragment extends ListFragment
        implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {

    // 用來顯示列表數據的適配器。
    SimpleCursorAdapter mAdapter;

    // 如果非空,這是用戶提供的當前過濾器。
    String mCurFilter;

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

        // 如果沒有數據,提供一些文本來顯示。
        // 在真正的應用中這可能出自於資源。
        setEmptyText("No phone numbers");

        //在動作欄顯示一個菜單項
        setHasOptionsMenu(true);

        //創建一個空的適配器,我們將使用它來顯示被加載的數據。
        mAdapter = new SimpleCursorAdapter(getActivity(),
                android.R.layout.simple_list_item_2, null,
                new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS },
                new int[] { android.R.id.text1, android.R.id.text2 }, 0);
        setListAdapter(mAdapter);

        //準備裝載器。要麼與一個現有的裝載器重新連接,要麼啓動一個新的裝載器。
        getLoaderManager().initLoader(0, null, this);
    }

    @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        //放置一個動作欄項用來搜索。
        MenuItem item = menu.add("Search");
        item.setIcon(android.R.drawable.ic_menu_search);
        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
        SearchView sv = new SearchView(getActivity());
        sv.setOnQueryTextListener(this);
        item.setActionView(sv);
    }

    public boolean onQueryTextChange(String newText) {
        //當動作欄搜索文本改變時調用。 
        // 更新搜索過濾器,並重啓裝載器用這個過濾器來執行新的查詢。
        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
        getLoaderManager().restartLoader(0, null, this);
        return true;
    }

    @Override public boolean onQueryTextSubmit(String query) {
        //不要在乎它。
        return true;
    }

    @Override public void onListItemClick(ListView l, View v, int position, long id) {
        // 這裏插入期望的行爲。
        Log.i("FragmentComplexList", "Item clicked: " + id);
    }

    //這些是將要取回的聯繫人數據行。
    static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
        Contacts._ID,
        Contacts.DISPLAY_NAME,
        Contacts.CONTACT_STATUS,
        Contacts.CONTACT_PRESENCE,
        Contacts.PHOTO_ID,
        Contacts.LOOKUP_KEY,
    };
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        // 當需要創建一個新的裝載器時調用它。本例中僅有一個裝載器,所以不必在乎ID。
        // 首先,依當前是否正在過濾,選擇基準URI來用。
        Uri baseUri;
        if (mCurFilter != null) {
            baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,
                    Uri.encode(mCurFilter));
        } else {
            baseUri = Contacts.CONTENT_URI;
        }

        // 現在創建並返回一個CursorLoader,它將負責創建正被顯示的數據的遊標.
        String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
                + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
                + Contacts.DISPLAY_NAME + " != '' ))";
        return new CursorLoader(getActivity(), baseUri,
                CONTACTS_SUMMARY_PROJECTION, select, null,
                Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        // 交換新的遊標。 (一旦返回,框架將負責關閉老的遊標)。
        mAdapter.swapCursor(data);
    }

    public void onLoaderReset(Loader<Cursor> loader) {
        // 當提供給onLoadFinished()的最後的遊標將要關閉時調用。
        // 需要確保我們不在使用它。
        mAdapter.swapCursor(null);
    }
}


3.1 更多實例


在ApiDemos中有很多不同的例子,它們說明了如何使用裝載器:

  • LoaderCursor — 如上所示代碼段的完整版。
  • LoaderThrottle — 一個如何使用節流來減少內容提供者執行查詢的數目,當它的數據發生變化時。

更多下載好安裝SDK實例的信息,請參閱 Getting the Samples




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章