裝載器從android3.0開始引進。它使得在activity或fragment中異步加載數據變得簡單。
當成批顯示數據的時候,為了使用戶體驗更好,需要進行異步裝載。也就是說,讓未顯示數據的ListView等UI組件或控件先顯示,避免出現白屏的尷尬現象,同時在后台下載數據,等下載完成后再更新ListView組件。這樣盡管用戶不會立刻看到數據,但是也不至於網絡速度緩慢或服務器響應不及時而造成假死現象。
裝載器具有如下特性:
-
它們對每個Activity和Fragment都有效;
-
他們提供了異步加載數據的能力;
-
它擁有一個數據改變通知機制,當數據源做出改變時會及時通知。 也就是可以監聽數據源,一旦數據源發生變化,Loader會感知這些變化;
-
當Cursor 發生變化時,會自動加載數據,因此並不需要再重新進行數據查詢。
android設計Loader的初衷是想讓大家像CursorLoader的做法一樣,通過loader去維護數據,每次啟動loader時先檢查有沒有舊的數據並把舊的數據先deliver給用戶,然后再考慮要不要重新加載新的數據。
(二)、裝載器API概述:
在使用裝載器時,會涉及很多類和接口們,在下表中對它們總結一下:
Class/Interface | 說明 |
LoaderManager | 一個抽像類,關聯到一個Activity或Fragment,管理一個或多個裝載器的實例。這幫助一個應用管理那些與Activity或Fragment的生命周期相關的長時間運行的的操作。最常見的方式是與一個CursorLoader一起使用,然而應用是可以隨便寫它們自己的裝載器以加載其它類型的數據。 每個activity或fragment只有一個LoaderManager。但是一個LoaderManager可以擁有多個裝載器。 |
LoaderManager.LoaderCallbacks | 一個用於客戶端與LoaderManager交互的回調接口。例如,你使用回調方法onCreateLoader()來創建一個新的裝載器。 |
Loader(裝載器) | 一個執行異步數據加載的抽象類。它是加載器的基類。你可以使用典型的CursorLoader,但是你也可以實現你自己的子類。一旦裝載器被激活,它們將監視它們的數據源並且在數據改變時發送新的結果。 |
AsyncTaskLoader | 提供一個AsyncTask來執行異步加載工作的抽象類。 |
CursorLoader | AsyncTaskLoader的子類,它查詢ContentResolver然后返回一個Cursor。這個類為查詢cursor以標准的方式實現了裝載器的協議,它的游標查詢是通過AsyncTaskLoader在后台線程中執行,從而不會阻塞界面。使用這個裝載器是從一個ContentProvider異步加載數據的最好方式。相比之下,通過fragment或activity的API來執行一個被管理的查詢就不行了。 |
二AsyncTaskLoader示例:
(一)、AsyncTaskLoader 實現數據加載的步驟:
1、窗體Activity要實現LoaderManager.LoaderCallbacks<Cursor>接口。至於繼承於Activity還是FragmentActivity要看是否需要支持3.0以下的版本。如果需要兼容則繼承與FragmentActivity。
2.創建LoaderManager對象,通過getLoaderManager()或getSupportLoaderManager()方法實現。如果是繼承FragmentActivity,則使用getSupportLoaderManager()方法創建,否則使用前者即可。
3、初始化LoaderManager對象,調用initLoader()方法來初始化;
- initLoader()方法有以下參數:
- 一個唯一ID來標識裝載器
- 可選的參數,用於裝載器初始化
- 一個LoaderManager.LoaderCallbacks的實現。被LoaderManager調用以報告裝載器的事件。一般窗體都實現了這個接口,所以傳的是它自己:this;
- initLoader()保證一個裝載器被初始化並激活.它具有兩種可能的結果:
- 如果Id所指的裝載器已經存在,那么這個裝載器將被重用
- 如果裝載器不存在,initLoader()就出發LoaderManager.LoaderCallbacks中的毀掉方法onCreateLoader()。這是實例化並返回一個新的Loader的地方
4、操作ListView空間對象:先findViewByID(),然后setAdapter();
【備注】此時必須使用SimpleCursorAdapter適配器,而構建適配器的時候,第三個參數Cursor設置為null,最后一個參數:flags必須是:CursAdapter.FLAG_REGISTER_CONTENT_OBSERVER。
5、自定義Loader,作為onCreateLoader()的返回值
- 自定義Loader要繼承於AsyncTaskLoader<Cursor>;
- 必須有構造方法
- 必須重寫onStartLoading()、loadInBackground()、deliverResult()。而且要在onStartLoading中調用forceLoad()才能依次調用下一個即將執行的方法
- 在loadInBackground()方法中執行數據庫查詢,返回Cursor;
- 在deliverResult()方法中執行跟適配器交換數據的操作。adapter.swapCursor(data)。
- Flags used to determine the behavior of the adapter; may be any combination of
FLAG_AUTO_REQUERY
andFLAG_REGISTER_CONTENT_OBSERVER。
- FLAG_AUTO_REQUERY(常量值:1 )從 API11 開始已經廢棄。因為他會在應用程序的 UI 線程中執行游標查詢操作, 導致響應緩慢甚至應用程序無響應(ANR)的錯誤。作為替代方案,請使用 LoaderManager 和 AsyncTaskLoader、CursorLoader。
- 如果設置FLAG_REGISTER_CONTENT_OBSERVER(常量值:2),適配器會在Cursor上注冊一個內容觀測器,當通知到達時會調用 onContentChanged() 方法。
當一個已創建的裝載器被重置從而使其數據無效時,此方法被調用.此回調使你能發現什么時候數據將被釋放。你可以釋放對它的引用。
(四)、AsyncTaskLoader中各個方法的執行順序:
04-01 04:00:25.477: MainActivity: ==onCreate 04-01 04:00:25.701: LoaderCallbacks: ==onCreateLoader 04-01 04:00:25.705: AsyncTaskLoader: ==onStartLoading 04-01 04:00:25.709: AsyncTaskLoader: ==loadInBackground 04-01 04:00:25.721: AsyncTaskLoader: ==deliverResult 04-01 04:00:25.721: LoaderCallbacks: ==onLoadFinished 04-01 04:00:25.749: MainActivity: ==onStart 04-01 04:00:25.749: MainActivity: ==onResume 04-01 04:00:25.973: MainActivity: ==onCreateOptionsMenu
(五)、當另一個app修改了同一個數據源(如:共同使用的SDCard上的數據庫)后AsyncTaskLoader中各個方法的執行順序:
04-01 04:01:06.693: MainActivity: ==onPause 04-01 04:01:08.413: MainActivity: ==onStop 04-01 04:01:15.721: AsyncTaskLoader: ==onStartLoading 04-01 04:01:15.721: AsyncTaskLoader: ==loadInBackground 04-01 04:01:15.721: AsyncTaskLoader: ==deliverResult 04-01 04:01:15.721: LoaderCallbacks: ==onLoadFinished 04-01 04:01:15.757: MainActivity: ==onStart 04-01 04:01:15.757: MainActivity: ==onResume
(三)、示例代碼:
publicclass MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> { private ListView listView_main; private LoaderManager manager; privatestatic SimpleCursorAdapter adapter; @Override protectedvoid onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.activity_main); listView_main = (ListView) findViewById(R.id.listView_main); manager = getSupportLoaderManager(); manager.initLoader(0, null, this); adapter = new SimpleCursorAdapter(this, R.layout.item_listview_main, null, new String[] { "_id", "title" }, newint[] { R.id.text_item_title, R.id.text_item_content }, 2); listView_main.setAdapter(adapter); } @Override public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { Log.i("Activity", "===onCreateLoader"); returnnew MyLoader(this); } @Override publicvoid onLoadFinished(Loader<Cursor> arg0, Cursor arg1) { Log.i("Activity", "===onLoaderFinished"); adapter.swapCursor(arg1); } @Override publicvoid onLoaderReset(Loader<Cursor> arg0) { Log.i("Activity", "===onLoaderReset"); } staticclass MyLoader extends AsyncTaskLoader<Cursor> { private Context context; public MyLoader(Context context) { super(context); this.context = context; } @Override protectedvoid onStartLoading() { super.onStartLoading(); Log.i("MyLoader", "===onStartLoading"); forceLoad(); } @Override public Cursor loadInBackground() { Log.i("MyLoader", "===loadInBackground"); MySQLiteDatabaseHelper dbHelper = new MySQLiteDatabaseHelper(); Cursor cursor = dbHelper.selectCursor( "select id as _id , title from android_info limit 0,20", null); deliverResult(cursor); return cursor; } @Override publicvoid deliverResult(Cursor data) { Log.i("MyLoader", "===deliverResult"); super.deliverResult(data); adapter.swapCursor(data); } } }