轉載 http://blog.sina.com.cn/s/blog_62c5894901014g5x.html
在Android3.0中,Google引入了一種資料異步加載機制,該機制的核心,便是LoaderManager、Loader,顧名思義,LoaderManager是Loader的管理者,而Loader便是資料加載器,你可以根據自己的需要實作形形色色的資料加載器。
Google強烈建議在加載資料時,使用LoaderManager及其相關的機制。
每個Activity和Fragment中,都會有且隻有一個LoaderManager,而LoaderManager中可以有多個Loader,也就是說,在一個Activity或者Fragment中,你可以同時異步加載N則不同的資料,具體加多少則,要看你那一畝三分地(Activity和Fragment就是你的地)有多大産。
Google倒是提供了一個标準的Loader,即CursorLoader,它是Loader的标準實作,如果你的資料能夠用Cursor表示,比如來自SQLiteDatabase的資料就是标準的Cursor,那麼這個類對你而言就夠用了,具體如何使用CursorLoader,請參看如下兩個例子:
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html
這個例子中牽涉的東西較多,除了我們關注的CursorLoader外,還包括ContentProvider、SQLiteOperHelper、SQLiteDatabase、Fragment、Uri等等常用概念,是以,在仔細閱讀了本例後,你将學會如何讓這這些類在你的應用中分工協作。
http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html
這個例子要簡單些,它教你如何擷取聯系人,雖然簡單,卻切中要害。
本文若是隻關注這些,就沒有意思了,畢竟在很多情況下,我們會感覺CursorLoader不順眼,于是想寫一個屬于自己的、更帥的Loader,此時抽象類AsyncTaskLoader就會叫嚣着粉墨登場了,該抽象類定義了你的加載器異步加載資料需要實作的接口,那麼如何實作呢?你可以去看下面的例子:
http://developer.android.com/reference/android/content/AsyncTaskLoader.html#loadInBackground()
為了讓你以及我自己更好的學習自定義Loader這一伎倆,我把裡頭的關鍵代碼摘了出來,咱們共同分析一番:
public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> { final InterestingConfigChanges mLastConfig = new InterestingConfigChanges(); final PackageManager mPm; List<AppEntry> mApps; PackageIntentReceiver mPackageObserver; public AppListLoader(Context context) { super(context); // Retrieve the package manager for later use; note we don't // use 'context' directly but instead the save global application // context returned by getContext(). mPm = getContext().getPackageManager(); } @Override public List<AppEntry> loadInBackground() { // Retrieve all known applications. List<ApplicationInfo> apps = mPm.getInstalledApplications( PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS); if (apps == null) { apps = new ArrayList<ApplicationInfo>(); } final Context context = getContext(); // Create corresponding array of entries and load their labels. List<AppEntry> entries = new ArrayList<AppEntry>(apps.size()); for (int i=0; i<apps.size(); i++) { AppEntry entry = new AppEntry(this, apps.get(i)); entry.loadLabel(context); entries.add(entry); } // Sort the list. Collections.sort(entries, ALPHA_COMPARATOR); // Done! return entries; } @Override public void deliverResult(List<AppEntry> apps) { if (isReset()) { // An async query came in while the loader is stopped. We // don't need the result. if (apps != null) { onReleaseResources(apps); } } List<AppEntry> oldApps = apps; mApps = apps; if (isStarted()) { // If the Loader is currently started, we can immediately // deliver its results. super.deliverResult(apps); } // At this point we can release the resources associated with // 'oldApps' if needed; now that the new result is delivered we // know that it is no longer in use. if (oldApps != null) { onReleaseResources(oldApps); } } @Override protected void onStartLoading() { if (mApps != null) { // If we currently have a result available, deliver it // immediately. deliverResult(mApps); } // Start watching for changes in the app data. if (mPackageObserver == null) { mPackageObserver = new PackageIntentReceiver(this); } // Has something interesting in the configuration changed since we // last built the app list? boolean configChange = mLastConfig.applyNewConfig(getContext().getResources()); if (takeContentChanged() || mApps == null || configChange) { // If the data has changed since the last time it was loaded // or is not currently available, start a load. forceLoad(); } } @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } @Override public void onCanceled(List<AppEntry> apps) { super.onCanceled(apps); // At this point we can release the resources associated with 'apps' // if needed. onReleaseResources(apps); } @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); // At this point we can release the resources associated with 'apps' // if needed. if (mApps != null) { onReleaseResources(mApps); mApps = null; } // Stop monitoring for changes. if (mPackageObserver != null) { getContext().unregisterReceiver(mPackageObserver); mPackageObserver = null; } } protected void onReleaseResources(List<AppEntry> apps) { // For a simple List<> there is nothing to do. For something // like a Cursor, we would close it here. } } |
諸位請看loadInBackground方法,這是Loader的核心方法,必須得重載,你要在這裡頭實作加載資料的功能,看看名字就知道,該方法将在背景運作,這方法沒有什麼特别說的,自己想加什麼樣的資料,自己清楚。
再看deliverResult方法,當資料到達用戶端後,這個方法将被調用,該方法可以不重載,你可以在其中根據需要實作傳遞資料的邏輯,請注意例子中對兩個狀态的判斷(注:程式設計,就得養成if的好習慣),一個是isReset()這個方法用來判斷Loader是否已經被重置,如果重置了,那麼留着資源也沒有啥用了,得把它釋放掉;一個是isStarted(),如果Loader被啟動那麼就把資料傳遞出去:直接調super的傳遞方法就OK。
onStartLoading方法,必須得重載,且别忘記在裡頭調用forceLoad方法,你也看到,在例子中人家調用了deliverResult方法,并且建立了一個觀察者來接收資料,在調用forceLoad時,請注意相應的條件判斷,關于為什麼要調用forceLoad方法,網上有下面一段話:
onStartLoading() is called in two places: 1. LoaderManagerImpl.doStart()->LoaderInfo.start(), which will happen when the hosting fragment isstarted. 2. LoaderManagerImpl.installLoader(LoaderInfo)->if the hosting fragment is started, callLoaderInfo.start(). So basically, your custom class extending AsyncTaskLoader will have to override onStartLoading(),and call forceLoad() within when necessary. Loader::startLoading() will be called when the hosting fragment is started, and youronStartLoading() will be called as consequence. |
以我目前的水準,并不能确切明白這段話的含義是什麼,但是,我翻了一下Android SDK的源代碼,發現Loader及AsyncTaskLoader中,startLoading方法其實屁都沒做,是以,萬事求己不如求人,還是老老實實去實作onStartLoading方法吧,不過Google這麼做有些坑爹的嫌疑,是以很多人在那裡問,哎呀,我的loadInBackground怎麼老不執行啊?
onStopLoading、onCancel方法都沒有什麼好說的,倒是onReset方法的實作值得關注,諸位看看便知,至于onReleaseResouces方法,就是給你一次釋放資源的機會,如果你用的是Cursor之類的東西,請在這裡頭close吧。
我剛剛開始研究Android SDK,也許是從.NET猛地轉到JAVA的緣故,很多架構 方面的東西都不是很習慣。Google Android SDK的架構簡單、靈活而又強大,這是值得稱道的,但是也有其不甚完善的地方。就拿LoaderManager來說,知道Android 3.0才引入,Google開發文檔說,使用這東西,能夠避免在加載資料時界面死在那裡。于是,我就相當然地認為,Google不是神,它的産品設計中,也會存在哪些低級的毗漏與缺陷。
但是,無論如何Google Android SDK以及iOS SDK,都非常值得我們去研究一番,即便你不程式設計,不寫代碼,但是裡頭的思想,确是一筆财富。