Android的加載器(loader)是從Android 3.0開始出來的東西。要理解這里需要先理解為什么會出現加載器(也有地方把它說成是裝載器)呢?
如果沒有加載器...
首先Activity是我們的前端頁面展現,數據庫是我們的數據持久化地址,那么正常的邏輯就是在展示頁面的渲染頁面的階段進行數據庫查詢。拿到數據以后才展示頁面。
但是這個邏輯有一些缺點:
首先是查詢數據的邏輯放在了UI生成的同個線程中,這個就意味着在查詢數據的時候,UI頁面生成的工作被阻塞住了。UI一旦被阻塞用戶就會被感知出來了,因此就會出現各種無相應頁面(Application Not Response),或者activity頁面延遲的現象,這對用戶體驗來說是不可接受的。
其次是在渲染頁面的時候需要固定需要進行一次數據查詢,但是這個是很不節省資源的。假如一個Activity從一個停止狀態回到前台,那么這個時候盡管數據並沒有變化,但是也需要進行一次query操作。在浪費資源的同時也再次增加了頁面渲染失敗的風險。
還有就是當數據變化的時候如何通知頁面進行修改呢?這個時候往往就又要創建一個monitor的角色,來當數據源變化的時候來讓頁面重新調用requery。
因此在Android的越來越提倡用戶體驗的今天,加載器和加載管理器(Loader,LoaderManager)就出現了。
Loader有什么作用?
簡單來說,Loader做了下面兩個事情:
1 在單獨的線程中讀取數據
2 監視數據的更新
而LoaderManager就是加載器的管理器,一個LoaderManager可以管理一個或多個Loader,一個Activity或者Fragment只能有一個LoadManager。LoaderManager管理Loader的初始化,重啟和銷毀操作。
從官網http://developer.android.com/reference/android/app/LoaderManager.html就可以看出它包含的方法有:
對應的就是這幾個操作。
initLoader是初始化一個加載器,它的第三個參數是一個LoaderCallbacks<D>接口,LoaderManager的initLoader是不做任何事情的,它只綁定了一個LoaderCallbacks<D>,具體的創建Loader的事情是由這個callback來做的。
LoaderCallbacks<D>接口需要實現的三個方法:
在loader創建loader的時候會調用onCreateLoader,然后當load數據結束的時候(第一次讀取數據或者數據有改變的時候load數據)會調用onLoadFinished,而onLoaderReset只有在destory一個loader的時候才有可能調用。
所以一般創建數據Cursor(CursorLoader)的工作是在onCreateLoader中做,將CursorLoader返回,這樣就創建了對這個數據源的監控,當數據源有數據變化的時候,就會自動調用了onLoadFinished函數了。
比如下面一個例子:
public class ToDoListActivity extends Activity implements NewItemFragment.OnNewItemAddedListener, LoaderManager.LoaderCallbacks<Cursor>{ //獲得對UI小組件的引用 private ArrayList<ToDoItem> todoItems; private ToDoItemAdapter aa; @Override // onCreate是創建這個activity的時候會調用的 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 獲取該Fragment的引用 FragmentManager fm = getFragmentManager(); ToDoListFragment todoListFragment = (ToDoListFragment)fm.findFragmentById(R.id.TodoListFragment); todoItems = new ArrayList<ToDoItem>(); // 這里需要將數據庫中存儲的東西都讀取出來 int resID = R.layout.todolist_item; aa = new ToDoItemAdapter(this, resID, todoItems); todoListFragment.setListAdapter(aa); getLoaderManager().initLoader(0, null, this); getLoaderManager().enableDebugLogging(true); } @Override // onResume是暫停以后重新啟動這個Activity時候調用 protected void onResume() { super.onResume(); getLoaderManager().restartLoader(0, null, this); } @Override public void onNewItemAdded(String newItem) { ContentResolver cr = getContentResolver(); ContentValues values = new ContentValues(); values.put(ToDoContentProvider.KEY_TASK, newItem); cr.insert(ToDoContentProvider.CONTENT_URI, values); getLoaderManager().restartLoader(0, null, this); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { CursorLoader loader = new CursorLoader(this, ToDoContentProvider.CONTENT_URI, null, null, null, null); return loader; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { // 當loader查詢完成的時候,Cursor會返回到onLoadFinished處理程序。 int keyTaskIndex = cursor.getColumnIndexOrThrow(ToDoContentProvider.KEY_TASK); todoItems.clear(); while (cursor.moveToNext()) { ToDoItem newItem = new ToDoItem(cursor.getString(keyTaskIndex)); todoItems.add(newItem); } aa.notifyDataSetChanged(); } @Override public void onLoaderReset(Loader<Cursor> arg0) { // TODO Auto-generated method stub } }
在這個例子中,在創建activity的時候(onCreate)調用了
getLoaderManager().initLoader(0,null,this);
這里的第一個參數0是指loader的id,我們並不關注它,所以設置了一個0。第二個參數是給Loader初始化的時候傳遞的參數(也就是onCreateLoader中的第二個參數)。
這里的第三個參數LoaderCallbacks<D>使用的直接是Activity類,所以這個類需要實現LoaderCallbacks<D>的三個方法:
onCreateLoader
onLoadFinished
onLoaderReset
在onCreateLoader中創建CursorLoader
在onLoadFinished中重新渲染ViewList。
總結:
在3.0之后Android的官方文檔強烈推薦使用Loader來做數據的加載。因此在能使用這個的情況下就盡量使用Loader吧。
使用需要先確定一個類來實現LoaderCallbacks<D>接口,然后實現接口的三個方法。
之后使用getLoaderManager來獲取LoadManager,再調用initLoader來創建loader,把實際的修改頁面的邏輯放在onLoadFinished中。
當然Loader並不只有CursorLoader,你也可以自己定義loader。
參考文章
http://www.kaixinwenda.com/article-wre_most2-8781946.html
http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html
http://www.androiddesignpatterns.com/2012/07/understanding-loadermanager.html