標籤:android cursor loader loadermanager content observer
之前寫過一篇文章《Android學習小Demo(13)Android中關於ContentObserver的使用》,在裡面利用ContentOberver去監測簡訊URI內容的變化。我們先來回顧一下,是如何利用ContentOberver來監測簡訊內容的變化的。
1)要自訂一個類,比如SmsContentObserver,繼承ContentObserver,並且實現其onChange方法。
2)在onChange方法中去查詢對應Uri,比如簡訊收件匣的內容,並將對應的記錄利用Handler發送到主介面。
3)在主介面Activity中,要建立一個SmsContentObserver,並且將其註冊到ContentReslover中去。
4)在主介面Activity的Handler中獲得在SmsContentObserver中變化的訊息,更新主介面。
總的來說,過程就大概是這樣,大家有興趣可以看一下這一篇文章。
而本文介紹的是另外一種方法,利用Loader來實現差不多的效果,既然能夠說效果差不多,那麼就說明了Loader有一個跟ContentObserver的特性,沒錯,它能夠檢測到對應內容的變化。
先簡單說一下什麼是Loader。
Loader是Android在3.0之後才引進的一個類,其主要目的在於讓Android跟Data之間的互動變得更加簡單和高效,概括起來,我覺得其功能有以下兩點:
1)動態監測所處理對象狀態的變化,大部分情況下是處理資料,但我覺得只是一方面。
2)當介面變化,需要被重新建立的時候,它們能夠重新load到上一次的資料,而不需要再重新進行查詢。
當然,它還是非同步,也就意味著不會阻塞到主介面的顯示,不過這個功能很多其他的輔助類都有,也就不算啥特點了。
而正是到其第一點的特性,才讓我們有機會可以不用那麼麻煩去實現一個ContentObserver,而轉而來利用Loader來實現相同的功能。
這一次我們做一個展示簡訊的Demo。當我們開啟手機中的簡訊應用的時候,如果這個時候有新簡訊進來,我們會看到新簡訊馬上就顯示在介面上的,而我們這個Demo也正是如此效果,具體請看(大了點,莫怪)。
從中可以看到,當我們點擊Send按鈕的時候,簡訊發過去,ListView中馬上就顯示出來剛剛發送的簡訊。
那麼應該怎麼使用Loader呢,我們下面來看代碼吧。
1)由於Loader是3.0之後才引進來的,所以在3.0之前,如果我們想要使用Loader的時候,主Activity必須要繼承FragmentActivity,才能夠拿到LoaderManager。
2)要實現LoaderManager的內部介面LoaderCallbacks<D>,這是一個泛型介面,其定義如下:
public interface LoaderCallbacks<D> { /** * Instantiate and return a new Loader for the given ID. * * @param id The ID whose loader is to be created. * @param args Any arguments supplied by the caller. * @return Return a new Loader instance that is ready to start loading. */ public Loader<D> onCreateLoader(int id, Bundle args); /** * ... * @param loader The Loader that has finished. * @param data The data generated by the Loader. */ public void onLoadFinished(Loader<D> loader, D data); /** * Called when a previously created loader is being reset, and thus * making its data unavailable. The application should at this point * remove any references it has to the Loader‘s data. * * @param loader The Loader that is being reset. */ public void onLoaderReset(Loader<D> loader); }
所以,我們代碼中的第一步就是要實現這個介面,如下:
public class MainActivity extends FragmentActivity implements LoaderCallbacks<Cursor>{ ... private Uri uri = Uri.parse("content://sms/inbox"); ... @Overridepublic Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {String[] projection = new String[] {"_id","address","body","type"};return new CursorLoader(this, uri, projection, null, null, "date desc");}@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {mAdapter.swapCursor(cursor);}@Overridepublic void onLoaderReset(Loader<Cursor> arg0) {// TODO Auto-generated method stub}3)要定義一個Uri,因為Loader它必須要從某個地方load資料,而這個Demo中,我們要擷取的是收件匣的簡訊,所以在這裡就是拿sms/inbox了。
4)在OnCreateLoader方法中,要建立一個CursorLoader。CursorLoader是AsyncTaskLoader的一個子類,所以它是一個非同步Loader,不會影響到主介面的展示。
5)在OnLoadFinished方法中,對Load完回來存放在cursor的資料進行處理。
上面的介面,只是實現的方式而已,而當調用下面這個方法的時候,Loader才開始真正地發揮作用。在onCreate方法中,
@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);lvListView = (ListView) findViewById(R.id.lvListView);mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, null, new String[] {"address","body"}, new int[] {android.R.id.text1, android.R.id.text2});lvListView.setAdapter(mAdapter);getSupportLoaderManager().initLoader(LOADER_ID, null, this);}
由於我們是用的support包,所以需要用getSupportLoaderManager類來調用initLoader方法,此方法有三個參數:
a)id,由於一個Activity或者Fragment只有一個LoaderManager,但是一個LoaderManager可以有多個Loader,用來處理不同的資料,所以id在這裡能唯一地確定是哪個Loader。
b)bundle,這是傳給LoaderManager的參數集合。
c)這是一個LoaderCallbacks的實作類別,在這個Demo中,就是此Activity,所以就是this。
6)當調用getSupportLoaderManager().initLoader()方法的時候,Android首先會根據 id 去判斷是否已經存在這樣的一個loader了,如果存在的話,它就會直接使用已有的loader,而不會去建立一個新的,也就是說,它不會去調用介面方法中的onCreateLoader方法了。而如果不存在對應 id 的Loader,則會去調用onCreateLoader方法,並執行個體化一個新的Loader出來。
而當對應 id 的loader已經存在的時候,Android會直接load資料,而介面方法中的onLoadFinished也會在資料load完之後馬上被調用,這樣就會存在一種情況,如果在onLoadFinished方法中使用的變數是在onCreateLoader中才初始化的,那麼這個變數根本都沒被初始化,就被使用了,程式就會報錯了,所以在實際開發中,要考慮到這樣一種情況的存在,在 onLoadFinished方法中,要做好一些判斷。
最後還有一個onLoaderReset方法,沒有被用到,這個方法主要是在Loader不再被使用的時候,被關閉了等情況下,用來釋放對Loader的使用的,比如在這個Demo中,如果loader不再用了,那麼我們的Adpater就不應該再關聯對應的cursor了,那麼就可以在這裡進行判斷。
結束!原始碼下載。