在之前呢,我們經常會有這種需求,比如在某個activity,或者某個fragment裡面,我們需要查詢某個資料來源,並且顯示出來,當資料來源自己更新的時候,介面也要及時響應。
當然咯,查詢資料這個過程可能很短,但是也可能很漫長,為了避免anr,我們都是開啟一個子執行緒去查詢,然後通過handler來更新我們的ui介面。但是,考慮到activity和
fragment 複雜的生命週期,上述的方法 使用起來會很不方便,畢竟你要考慮到儲存現場 還原現場 等等複雜的工作來保證你的app無懈可擊。所以後來呢谷歌就幫我們推出了一個
新的東西---Loader。他可以幫我們完成上述所有功能!實在是很強大。
如果你有閱讀英文技術文件的習慣 那麼谷歌官方的文件 也許比我所說的更加完美。具體可以參考如下:
http://developer.android.com/intl/zh-cn/reference/android/app/LoaderManager.html
http://developer.android.com/intl/zh-cn/reference/android/content/AsyncTaskLoader.html
http://developer.android.com/intl/zh-cn/guide/components/loaders.html
我所述的內容也是主要基於上述三篇文件。
首先呢,我們來看第一個例子,這個例子也是官方的推薦了,我給簡化了一下,主要是監聽手機裡 聯絡人這個資料來源。當資料來源改變的時候 自動update 我們的ui。
1 package com.example.administrator.modifytestview; 2 3 import android.app.Activity; 4 import android.app.FragmentManager; 5 import android.app.ListFragment; 6 import android.app.LoaderManager; 7 import android.content.CursorLoader; 8 import android.content.Loader; 9 import android.database.Cursor; 10 import android.net.Uri; 11 import android.os.Bundle; 12 import android.provider.ContactsContract.Contacts; 13 import android.util.Log; 14 import android.view.View; 15 import android.widget.ListView; 16 import android.widget.SimpleCursorAdapter; 17 18 public class MainActivity extends Activity { 19 20 21 @Override 22 protected void onCreate(Bundle savedInstanceState) { 23 super.onCreate(savedInstanceState); 24 setContentView(R.layout.activity_main); 25 FragmentManager fm = getFragmentManager(); 26 CursorLoaderListFragment list = new CursorLoaderListFragment(); 27 fm.beginTransaction().replace(R.id.root, list).commit(); 28 29 } 30 31 32 33 public static class CursorLoaderListFragment extends ListFragment 34 implements LoaderManager.LoaderCallbacks<Cursor> { 35 36 // This is the Adapter being used to display the list's data. 37 SimpleCursorAdapter mAdapter; 38 39 // If non-null, this is the current filter the user has provided. 40 String mCurFilter; 41 42 @Override 43 public void onActivityCreated(Bundle savedInstanceState) { 44 45 46 mAdapter = new SimpleCursorAdapter(getActivity(), 47 android.R.layout.simple_list_item_2, null, 48 new String[]{Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS}, 49 new int[]{android.R.id.text1, android.R.id.text2}, 0); 50 setListAdapter(mAdapter); 51 52 //這個地方初始化了我們的loader 53 getLoaderManager().initLoader(0, null, this); 54 55 super.onActivityCreated(savedInstanceState); 56 } 57 58 59 @Override 60 public void onListItemClick(ListView l, View v, int position, long id) { 61 // Insert desired behavior here. 62 Log.i("FragmentComplexList", "Item clicked: " + id); 63 } 64 65 // These are the Contacts rows that we will retrieve. 66 static final String[] CONTACTS_SUMMARY_PROJECTION = new String[]{ 67 Contacts._ID, 68 Contacts.DISPLAY_NAME, 69 Contacts.CONTACT_STATUS, 70 Contacts.CONTACT_PRESENCE, 71 Contacts.PHOTO_ID, 72 Contacts.LOOKUP_KEY, 73 }; 74 75 //只會呼叫一次 76 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 77 // This is called when a new Loader needs to be created. This 78 // sample only has one Loader, so we don't care about the ID. 79 // First, pick the base URI to use depending on whether we are 80 // currently filtering. 81 Uri baseUri; 82 if (mCurFilter != null) { 83 baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, 84 Uri.encode(mCurFilter)); 85 } else { 86 baseUri = Contacts.CONTENT_URI; 87 } 88 89 // Now create and return a CursorLoader that will take care of 90 // creating a Cursor for the data being displayed. 91 String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" 92 + Contacts.HAS_PHONE_NUMBER + "=1) AND (" 93 + Contacts.DISPLAY_NAME + " != '' ))"; 94 //返回的是對這個資料來源的監控 95 return new CursorLoader(getActivity(), baseUri, 96 CONTACTS_SUMMARY_PROJECTION, select, null, 97 Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); 98 } 99 100 //每次資料來源都有更新的時候,就會回撥這個方法,然後update 我們的ui了。 101 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 103 104 // Swap the new cursor in. (The framework will take care of closing the 105 // old cursor once we return.) 106 mAdapter.swapCursor(data); 107 108 // The list should now be shown. 109 if (isResumed()) { 110 setListShown(true); 111 } else { 112 setListShownNoAnimation(true); 113 } 114 } 115 116 public void onLoaderReset(Loader<Cursor> loader) { 117 // This is called when the last Cursor provided to onLoadFinished() 118 // above is about to be closed. We need to make sure we are no 119 // longer using it. 120 mAdapter.swapCursor(null); 121 } 122 } 123 124 }
可以仔細的觀察一下這個程式碼,我們能發現 使用loader所需要的一些步驟:
1.需要一個activity或者是fragment,當然在上述的例子裡 我們使用的是fragment。
2.一個LoaderManger的例項,注意看53行,我們get了一個loadermanager。這個地方就是獲取例項了。
3.需要一個CursorLoader,並且從contentProvider獲取資料來源,90-97行 就是這麼做的。
4.需要實現一個LoaderCallBack的這個介面,然後在幾個回撥方法裡 寫上我們自己業務的邏輯 即可。你看34行就是繼承的介面。
還有3個回撥方法在那,我們都在裡面實現了自己的邏輯。
到這,其實一看,思路還是很清晰的。那到這裡 有人肯定要說了。你這個沒用啊,要實現contentprovider,我們的app不需要做
資料共享的,能否直接運算元據庫呢?答案是可以的。在這裡我們也可以構造出一個場景。假設有一張學生表。我們點選add
按鈕,就自動往這個表裡面增加一個資料,然後下面有個listview 會自動捕捉到 這個資料來源的變化,然後自動更新列表。
我們可以知道 上面那個demo裡面 CursorLoader的定義是這樣的
1 public class CursorLoader extends AsyncTaskLoader<Cursor> {
我們現在要實現一個不用contentProvider的Loader 也是基於AsyncTaskLoader來的。
先給出一個抽象類:
1 package com.example.administrator.activeandroidtest3; 2 3 4 import android.content.AsyncTaskLoader; 5 import android.content.Context; 6 import android.database.Cursor; 7 8 9 public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> { 10 private Cursor mCursor; 11 12 public SimpleCursorLoader(Context context) { 13 super(context); 14 } 15 16 /* 在子執行緒裡運作 */ 17 @Override 18 public abstract Cursor loadInBackground(); 19 20 /* 在ui 執行緒裡運作 */ 21 @Override 22 public void deliverResult(Cursor cursor) { 23 if (isReset()) { 24 // An async query came in while the loader is stopped 25 if (cursor != null) { 26 cursor.close(); 27 } 28 return; 29 } 30 Cursor oldCursor = mCursor; 31 mCursor = cursor; 32 33 if (isStarted()) { 34 super.deliverResult(cursor); 35 } 36 37 if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) { 38 oldCursor.close(); 39 } 40 } 41 42 @Override 43 protected void onStartLoading() { 44 if (mCursor != null) { 45 deliverResult(mCursor); 46 } 47 if (takeContentChanged() || mCursor == null) { 48 forceLoad(); 49 } 50 } 51 52 @Override 53 protected void onStopLoading() { 54 cancelLoad(); 55 } 56 57 @Override 58 public void onCanceled(Cursor cursor) { 59 if (cursor != null && !cursor.isClosed()) { 60 cursor.close(); 61 } 62 } 63 64 @Override 65 protected void onReset() { 66 super.onReset(); 67 68 onStopLoading(); 69 70 if (mCursor != null && !mCursor.isClosed()) { 71 mCursor.close(); 72 } 73 mCursor = null; 74 } 75 }
然後我們再接著定義我們最終的 不需要provider的loader實現類(注意你如果想寫的比較完美的話 cursor記得用抽象類的,抽象類的那個就不要寫成private的了,我這裡為了圖簡單 直接用自己構造的)。
1 package com.example.administrator.activeandroidtest3; 2 3 import android.content.Context; 4 import android.database.Cursor; 5 import android.database.sqlite.SQLiteDatabase; 6 7 /** 8 * Created by Administrator on 2015/10/7. 9 */ 10 public class SpecialLoader extends SimpleCursorLoader { 11 12 ForceLoadContentObserver mObserver = new ForceLoadContentObserver(); 13 private Context context; 14 15 public SpecialLoader(Context context) { 16 super(context); 17 this.context = context; 18 19 } 20 21 @Override 22 public Cursor loadInBackground() { 23 DatabaseHelper dh = new DatabaseHelper(context, "Test.db"); 24 SQLiteDatabase database = dh.getReadableDatabase(); 25 String table = "Student"; 26 String[] columns = new String[]{"Name", "No"}; 27 //這個地方因為我用的是activeandroid 的orm 框架,所以預設的自增長主鍵是Id,但是SimpleCursorAdapter 28 //需要的是_id 否則會報錯,所以這裡要重新命名一下 29 Cursor cursor = database.rawQuery("SELECT Id AS _id,Name,No FROM Student", null); 30 if (database != null) { 31 if (cursor != null) { 32 //註冊一下這個觀察者 33 cursor.registerContentObserver(mObserver); 34 //這邊也要注意 一定要監聽這個uri的變化。但是如果你這個uri沒有對應的provider的話 35 //記得在你運算元據庫的時候 通知一下這個uri 36 cursor.setNotificationUri(context.getContentResolver(), MainActivity.uri); 37 } 38 39 } 40 return cursor; 41 } 42 }
然後我們在簡單看下activity 主類裡的程式碼:
1 package com.example.administrator.activeandroidtest3; 2 3 import android.app.Activity; 4 import android.app.LoaderManager; 5 import android.content.Loader; 6 import android.database.Cursor; 7 import android.net.Uri; 8 import android.os.Bundle; 9 import android.util.Log; 10 import android.view.Menu; 11 import android.view.MenuItem; 12 import android.view.View; 13 import android.widget.ListView; 14 import android.widget.SimpleCursorAdapter; 15 import android.widget.TextView; 16 17 import com.activeandroid.query.Select; 18 19 import java.util.List; 20 import java.util.Random; 21 22 public class MainActivity extends Activity implements LoaderManager.LoaderCallbacks { 23 24 public static final Uri uri = Uri.parse("content://com.example.student"); 25 private TextView addTv; 26 private ListView lv; 27 private SimpleCursorAdapter adapter; 28 29 @Override 30 protected void onCreate(Bundle savedInstanceState) { 31 super.onCreate(savedInstanceState); 32 setContentView(R.layout.activity_main); 33 addTv = (TextView) this.findViewById(R.id.add); 34 addTv.setOnClickListener(new View.OnClickListener() { 35 @Override 36 public void onClick(View v) { 37 Student student = new Student(); 38 student.name = getRandomString(5); 39 student.no = (int) (Math.random() * 1000) + ""; 40 student.sex = (int) (Math.random() * 1); 41 student.save(); 42 //操作完資料庫要notify 不然loader那邊收不到哦 43 getContentResolver().notifyChange(uri, null); 44 45 } 46 }); 47 lv = (ListView) this.findViewById(R.id.lv); 48 adapter = new SimpleCursorAdapter(MainActivity.this, 49 android.R.layout.simple_list_item_2, null, 50 new String[]{"Name", "No"}, 51 new int[]{android.R.id.text1, android.R.id.text2}, 0); 52 lv.setAdapter(adapter); 53 getLoaderManager().initLoader(0, null, this); 54 } 55 56 @Override 57 public boolean onCreateOptionsMenu(Menu menu) { 58 // Inflate the menu; this adds items to the action bar if it is present. 59 getMenuInflater().inflate(R.menu.menu_main, menu); 60 return true; 61 } 62 63 @Override 64 public boolean onOptionsItemSelected(MenuItem item) { 65 // Handle action bar item clicks here. The action bar will 66 // automatically handle clicks on the Home/Up button, so long 67 // as you specify a parent activity in AndroidManifest.xml. 68 int id = item.getItemId(); 69 70 //noinspection SimplifiableIfStatement 71 if (id == R.id.action_settings) { 72 return true; 73 } 74 75 return super.onOptionsItemSelected(item); 76 } 77 78 79 public static String getRandomString(int length) { //length表示生成字串的長度 80 String base = "abcdefghijklmnopqrstuvwxyz0123456789"; 81 Random random = new Random(); 82 StringBuffer sb = new StringBuffer(); 83 for (int i = 0; i < length; i++) { 84 int number = random.nextInt(base.length()); 85 sb.append(base.charAt(number)); 86 } 87 return sb.toString(); 88 } 89 90 91 @Override 92 public Loader onCreateLoader(int id, Bundle args) { 93 SpecialLoader loader = new SpecialLoader(MainActivity.this); 94 return loader; 95 } 96 97 @Override 98 public void onLoadFinished(Loader loader, Object data) { 99 adapter.swapCursor((Cursor) data); 100 } 101 102 @Override 103 public void onLoaderReset(Loader loader) { 104 105 } 106 }
最後我們看下執行的效果:
好,那到這裡 又有人要說了,你這個說來說去 還不是隻能支援provider或者db型別的資料來源嗎?好 接著往下,
我們給出另外一個例子,不過這個例子是谷歌官方的例子,我就取其中重要的部分給予註釋講解。
http://developer.android.com/intl/zh-cn/reference/android/content/AsyncTaskLoader.html
首先說一下 這個例子是幹嘛的,他主要是監聽手機裡app list的變化,比如你刪除了一個應用
安裝了一個應用,馬上就能捕捉到你的手機裡app list的變化 並顯示在介面,大家都知道 監聽app list
是通過監聽系統廣播來完成的。 我就主要講一下 這個官方demo裡 是如何在監聽到系統廣播以後和loader結合起來
然後自動回撥方法的。
1 /** 2 * Helper class to look for interesting changes to the installed apps 3 * so that the loader can be updated. 4 */ 5 public static class PackageIntentReceiver extends BroadcastReceiver { 6 final AppListLoader mLoader; 7 8 //這個建構函式是很重要的 他接收的 就是自定義的loader 9 public PackageIntentReceiver(AppListLoader loader) { 10 mLoader = loader; 11 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 12 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 13 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 14 filter.addDataScheme("package"); 15 mLoader.getContext().registerReceiver(this, filter); 16 // Register for events related to sdcard installation. 17 IntentFilter sdFilter = new IntentFilter(); 18 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); 19 sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); 20 //在這個地方 直接用loader來註冊這個廣播接收器 21 mLoader.getContext().registerReceiver(this, sdFilter); 22 } 23 24 //在收到廣播以後 什麼事情都沒有做,而是呼叫了loader的onContentChanged方法 25 @Override public void onReceive(Context context, Intent intent) { 26 // Tell the loader about the change. 27 mLoader.onContentChanged(); 28 } 29 }
你看這裡的25-26行 呼叫了 loader的onContentChanged方法。繼續看下面的loader
1 /** 2 * A custom Loader that loads all of the installed applications. 3 */ 4 public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> { 5 final InterestingConfigChanges mLastConfig = new InterestingConfigChanges(); 6 final PackageManager mPm; 7 8 List<AppEntry> mApps; 9 PackageIntentReceiver mPackageObserver; 10 11 public AppListLoader(Context context) { 12 super(context); 13 14 // Retrieve the package manager for later use; note we don't 15 // use 'context' directly but instead the save global application 16 // context returned by getContext(). 17 mPm = getContext().getPackageManager(); 18 } 19 20 //實際上最重要的就是這個方法了,每當這個回撥方法被呼叫的時候 就去取applist 然後將結果返回到 21 //onLoadFinished 這個回撥方法裡面! 22 @Override public List<AppEntry> loadInBackground() { 23 // Retrieve all known applications. 24 List<ApplicationInfo> apps = mPm.getInstalledApplications( 25 PackageManager.GET_UNINSTALLED_PACKAGES | 26 PackageManager.GET_DISABLED_COMPONENTS); 27 if (apps == null) { 28 apps = new ArrayList<ApplicationInfo>(); 29 } 30 31 final Context context = getContext(); 32 33 // Create corresponding array of entries and load their labels. 34 List<AppEntry> entries = new ArrayList<AppEntry>(apps.size()); 35 for (int i=0; i<apps.size(); i++) { 36 AppEntry entry = new AppEntry(this, apps.get(i)); 37 entry.loadLabel(context); 38 entries.add(entry); 39 } 40 41 // Sort the list. 42 Collections.sort(entries, ALPHA_COMPARATOR); 43 44 // Done! 45 return entries; 46 } 47 48 /** 49 * Called when there is new data to deliver to the client. The 50 * super class will take care of delivering it; the implementation 51 * here just adds a little more logic. 52 */ 53 @Override public void deliverResult(List<AppEntry> apps) { 54 if (isReset()) { 55 // An async query came in while the loader is stopped. We 56 // don't need the result. 57 if (apps != null) { 58 onReleaseResources(apps); 59 } 60 } 61 List<AppEntry> oldApps = mApps; 62 mApps = apps; 63 64 if (isStarted()) { 65 // If the Loader is currently started, we can immediately 66 // deliver its results. 67 super.deliverResult(apps); 68 } 69 70 // At this point we can release the resources associated with 71 // 'oldApps' if needed; now that the new result is delivered we 72 // know that it is no longer in use. 73 if (oldApps != null) { 74 onReleaseResources(oldApps); 75 } 76 } 77 78 /** 79 * Handles a request to start the Loader. 80 */ 81 @Override protected void onStartLoading() { 82 if (mApps != null) { 83 // If we currently have a result available, deliver it 84 // immediately. 85 deliverResult(mApps); 86 } 87 88 // Start watching for changes in the app data. 89 if (mPackageObserver == null) { 90 mPackageObserver = new PackageIntentReceiver(this); 91 } 92 93 // Has something interesting in the configuration changed since we 94 // last built the app list? 95 boolean configChange = mLastConfig.applyNewConfig(getContext().getResources()); 96 97 if (takeContentChanged() || mApps == null || configChange) { 98 // If the data has changed since the last time it was loaded 99 // or is not currently available, start a load. 100 forceLoad(); 101 } 102 } 103 104 /** 105 * Handles a request to stop the Loader. 106 */ 107 @Override protected void onStopLoading() { 108 // Attempt to cancel the current load task if possible. 109 cancelLoad(); 110 } 111 112 /** 113 * Handles a request to cancel a load. 114 */ 115 @Override public void onCanceled(List<AppEntry> apps) { 116 super.onCanceled(apps); 117 118 // At this point we can release the resources associated with 'apps' 119 // if needed. 120 onReleaseResources(apps); 121 } 122 123 /** 124 * Handles a request to completely reset the Loader. 125 */ 126 @Override protected void onReset() { 127 super.onReset(); 128 129 // Ensure the loader is stopped 130 onStopLoading(); 131 132 // At this point we can release the resources associated with 'apps' 133 // if needed. 134 if (mApps != null) { 135 onReleaseResources(mApps); 136 mApps = null; 137 } 138 139 // Stop monitoring for changes. 140 if (mPackageObserver != null) { 141 getContext().unregisterReceiver(mPackageObserver); 142 mPackageObserver = null; 143 } 144 } 145 146 /** 147 * Helper function to take care of releasing resources associated 148 * with an actively loaded data set. 149 */ 150 protected void onReleaseResources(List<AppEntry> apps) { 151 // For a simple List<> there is nothing to do. For something 152 // like a Cursor, we would close it here. 153 } 154 }
好,到這裡流程就很明顯了,在loader裡 註冊廣播接收器,當廣播接收器 收到廣播以後 就呼叫loader的onContentChanged方法,
這個方法一呼叫 AppListLoader裡的loadInBackGround就會被呼叫,然後當loadInBackGround執行完畢以後 就會把結果
傳遞給onLoadFinished方法了。 搞清楚這個流程 你就真正學會了使用loader這個大殺器了。當然了,我們並不滿足於此,loader
還有一個特性就是可以自動管理他自己的生命週期 等等。我們現在就去看看他的原始碼,是如何完成這一點的。 並且上述幾個方法
之間是如何相互呼叫的,順序如何。
首先 我們要搞清楚幾個類之間的關係:
1 public class CursorLoader extends AsyncTaskLoader<Cursor> { 2 3 4 public abstract class AsyncTaskLoader<D> extends Loader<D> { 5 6 public class Loader<D> {
這樣就很清晰。首先由一個實體類作為最基礎的基類,Loader 注意他可以接受一個泛型為引數,然後有一個抽象類:AsyncTaskLoader 也是泛型作為引數。
最後實際呼叫運作的類就是CursorLoader類了,這裡就可以看出來 傳進去的泛型是一個Cursor。你在自定義Loader的時候,這個泛型引數 當然是可以自己決定的,
比如官方demo裡 傳的就是一個List。
搞清楚 他們三者之間的關係,剩下的就簡單多了。可以逐步分析了。
在前面的3個demo裡,我們分別演示了在fragment和activity裡 呼叫loader的方法。 那我們就看看 這兩者之間有什麼異同點。先來看fragment。
fragment裡 我們是這樣呼叫的:
1 //這個地方初始化了我們的loader 2 getLoaderManager().initLoader(0, null, this);
直接get了一個manager 然後init他。我們進去看fragment的原始碼:
1 //這邊就能看出來一個fragment只能有一個loadermanager了。 2 public LoaderManager getLoaderManager() { 3 4 if (mLoaderManager != null) { 5 return mLoaderManager; 6 } 7 //mHost很好理解 就是fragment的宿主,也就是跟fragment 相關聯的activity。 8 if (mHost == null) { 9 throw new IllegalStateException("Fragment " + this + " not attached to Activity"); 10 } 11 mCheckedForLoaderManager = true; 12 mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, true); 13 return mLoaderManager; 14 }
既然 我們知道 fragment的getLoaderManager也是通過activity的getLoader去呼叫的,那我們就去activity裡的原始碼看看 :
1 //在activty中最終實際上呼叫的就是他了 是這個方法 2 LoaderManagerImpl getLoaderManagerImpl() { 3 if (mLoaderManager != null) { 4 return mLoaderManager; 5 } 6 mCheckedForLoaderManager = true; 7 mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/); 8 return mLoaderManager; 9 } 10 11 //這個地方就能看到 主要的第一個引數 who,你到這就能發現 如果是activity自己呼叫的話,傳進去的who的值就是root 12 //也就是說一個actvity 只能有一個loadermanger 但是我們可以發現在fragment裡 傳進去的值是下面這個: 13 // Internal unique name for this fragment; 14 //String mWho; 15 //也就是說每一個fragment的mWho的值都是唯一的,而在activty中,是維護了一個map,一個key 對應一個loadermanager 16 //key就是fragment的那個唯一的標示,或者是activity自己,activity自己的標示就是(root)了 17 LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) { 18 if (mAllLoaderManagers == null) { 19 mAllLoaderManagers = new ArrayMap<String, LoaderManager>(); 20 } 21 LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who); 22 if (lm == null) { 23 if (create) { 24 lm = new LoaderManagerImpl(who, this, started); 25 mAllLoaderManagers.put(who, lm); 26 } 27 } else { 28 lm.updateHostController(this); 29 } 30 return lm; 31 }
好 一直到這裡 ,我們就可以下一個結論了,真正的loadermanager都是儲存在activity中的,包括fragment的loadermanager也是,通過一個map來保證 get的時候
取的manager是自己對應的,並且全域性唯一。繼續往下看:
1 public abstract class LoaderManager { 2 /** 3 * Callback interface for a client to interact with the manager. 4 */ 5 public interface LoaderCallbacks<D> { 6 /** 7 * Instantiate and return a new Loader for the given ID. 8 * 9 * @param id The ID whose loader is to be created. 10 * @param args Any arguments supplied by the caller. 11 * @return Return a new Loader instance that is ready to start loading. 12 */ 13 public Loader<D> onCreateLoader(int id, Bundle args); 14 15 /** 16 * Called when a previously created loader has finished its load. Note 17 * that normally an application is <em>not</em> allowed to commit fragment 18 * transactions while in this call, since it can happen after an 19 * activity's state is saved. See {@link FragmentManager#beginTransaction() 20 * FragmentManager.openTransaction()} for further discussion on this. 21 * 22 * <p>This function is guaranteed to be called prior to the release of 23 * the last data that was supplied for this Loader. At this point 24 * you should remove all use of the old data (since it will be released 25 * soon), but should not do your own release of the data since its Loader 26 * owns it and will take care of that. The Loader will take care of 27 * management of its data so you don't have to. In particular: 28 * 29 * <ul> 30 * <li> <p>The Loader will monitor for changes to the data, and report 31 * them to you through new calls here. You should not monitor the 32 * data yourself. For example, if the data is a {@link android.database.Cursor} 33 * and you place it in a {@link android.widget.CursorAdapter}, use 34 * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context, 35 * android.database.Cursor, int)} constructor <em>without</em> passing 36 * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY} 37 * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER} 38 * (that is, use 0 for the flags argument). This prevents the CursorAdapter 39 * from doing its own observing of the Cursor, which is not needed since 40 * when a change happens you will get a new Cursor throw another call 41 * here. 42 * <li> The Loader will release the data once it knows the application 43 * is no longer using it. For example, if the data is 44 * a {@link android.database.Cursor} from a {@link android.content.CursorLoader}, 45 * you should not call close() on it yourself. If the Cursor is being placed in a 46 * {@link android.widget.CursorAdapter}, you should use the 47 * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)} 48 * method so that the old Cursor is not closed. 49 * </ul> 50 * 51 * @param loader The Loader that has finished. 52 * @param data The data generated by the Loader. 53 */ 54 public void onLoadFinished(Loader<D> loader, D data); 55 56 /** 57 * Called when a previously created loader is being reset, and thus 58 * making its data unavailable. The application should at this point 59 * remove any references it has to the Loader's data. 60 * 61 * @param loader The Loader that is being reset. 62 */ 63 public void onLoaderReset(Loader<D> loader); 64 }
一看就知道 loadermanger 其實是一個抽象類。就是定義了一些 我們需要的介面而已,這些介面方法的含義和用法 在那3個demo裡 相信大家都有了解,不多說。
我們去看看這個抽象類的實現類,為什麼要看他,因為你在get到這個maganger以後 馬上就去呼叫了他的init方法 我們就看看這部分的邏輯是怎麼樣的:
1 public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) { 2 if (mCreatingLoader) { 3 throw new IllegalStateException("Called while creating a loader"); 4 } 5 6 //這個就是先看看是否有活動的loader 有的話就取出來 沒有的話 就建立一個 7 LoaderInfo info = mLoaders.get(id); 8 9 if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args); 10 11 if (info == null) { 12 // Loader doesn't already exist; create. 13 info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback); 14 if (DEBUG) Log.v(TAG, " Created new loader " + info); 15 } else { 16 if (DEBUG) Log.v(TAG, " Re-using existing loader " + info); 17 info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback; 18 } 19 20 if (info.mHaveData && mStarted) { 21 // If the loader has already generated its data, report it now. 22 info.callOnLoadFinished(info.mLoader, info.mData); 23 } 24 25 return (Loader<D>)info.mLoader; 26 } 27 28 //這個就是現在存活的loader 29 final SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(0); 30 31 //這個是已經執行結束的loader 32 final SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(0); 33 34 //其實這個建立loader的過程特別簡單,我們主要看第三個引數,callback 這個引數 35 //一想就明白,在前面3個demo裡我們是直接在fragemet和activity裡實現的callback 36 //所以傳進去的就是this,也就是說 回撥就是在這個函式裡 真正的和loader 發生了關聯了 37 private LoaderInfo createAndInstallLoader(int id, Bundle args, 38 LoaderManager.LoaderCallbacks<Object> callback) { 39 try { 40 mCreatingLoader = true; 41 LoaderInfo info = createLoader(id, args, callback); 42 installLoader(info); 43 return info; 44 } finally { 45 mCreatingLoader = false; 46 } 47 }
你看 一直到這裡,我們就明白了 callback是怎麼和loadermageer本身發生關聯的。 我們繼續往下看。這次我們要搞明白
當資料來源發生變化的時候 是怎麼一步步回撥我們子類loader的方法的。
我們先看Loader這個基類的主要方法:
1 2 3 //這個是一個觀察者 當發生變化的時候 他呼叫了onContentChanged方法 4 public final class ForceLoadContentObserver extends ContentObserver { 5 public ForceLoadContentObserver() { 6 super(new Handler()); 7 } 8 9 @Override 10 public boolean deliverSelfNotifications() { 11 return true; 12 } 13 14 @Override 15 public void onChange(boolean selfChange) { 16 onContentChanged(); 17 } 18 } 19 20 //下面這2個方法一看就明白 最終當資料來源發生變化的時候 會通知這個觀察者,然後這個觀察者會最終呼叫 21 //onForceLoad這個方法 而onForceLoad是交給子類去實現的 也就是AsyncTaskLoader的onForceLoad方法了 22 public void onContentChanged() { 23 if (mStarted) { 24 forceLoad(); 25 } else { 26 // This loader has been stopped, so we don't want to load 27 // new data right now... but keep track of it changing to 28 // refresh later if we start again. 29 mContentChanged = true; 30 } 31 } 32 33 public void forceLoad() { 34 onForceLoad(); 35 } 36 37 /** 38 * Subclasses must implement this to take care of requests to {@link #forceLoad()}. 39 * This will always be called from the process's main thread. 40 */ 41 protected void onForceLoad() { 42 }
然後看看AsyncTaskLoader的幾個主要方法:
1 //這邊一目瞭然 asynacTaskLoader 裡面 正好是有一個AsyncTask物件的!實現了runnabele介面 2 //注意著引數d 這個d是幹嘛的,這個d就是用來傳遞引數的一個泛型,可以是系統實現的loader裡的cursor 3 //也可以是我們自己實現的loader裡的list型別 4 final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable { 5 private final CountDownLatch mDone = new CountDownLatch(1); 6 7 // Set to true to indicate that the task has been posted to a handler for 8 // execution at a later time. Used to throttle updates. 9 boolean waiting; 10 11 /* Runs on a worker thread */ 12 @Override 13 protected D doInBackground(Void... params) { 14 if (DEBUG) Log.v(TAG, this + " >>> doInBackground"); 15 try { 16 //這個地方就很明顯了,他呼叫了自己的onLoadInBackGround方法 17 D data = AsyncTaskLoader.this.onLoadInBackground(); 18 if (DEBUG) Log.v(TAG, this + " <<< doInBackground"); 19 return data; 20 } catch (OperationCanceledException ex) { 21 if (!isCancelled()) { 22 // onLoadInBackground threw a canceled exception spuriously. 23 // This is problematic because it means that the LoaderManager did not 24 // cancel the Loader itself and still expects to receive a result. 25 // Additionally, the Loader's own state will not have been updated to 26 // reflect the fact that the task was being canceled. 27 // So we treat this case as an unhandled exception. 28 throw ex; 29 } 30 if (DEBUG) Log.v(TAG, this + " <<< doInBackground (was canceled)", ex); 31 return null; 32 } 33 } 34 //後面還有很多程式碼 略過 35 } 36 37 //你看這裡下面的2個函式 一看就明白了 最終task裡呼叫的是這個抽象方法,那這個抽象方法 38 //就是留給我們子類自己去實現的,我們在自定義loader的時候最重要的就是重寫這個方法。 39 protected D onLoadInBackground() { 40 return loadInBackground(); 41 } 42 43 public abstract D loadInBackground(); 44 45 //你看這個地方 就是當資料來源發生變化的時候 就會呼叫這個方法了,啟動了我們的laodtask 46 //也是最終呼叫子類 也就是CursorLoader這樣的子類的loadInBackground方法了 47 @Override 48 protected void onForceLoad() { 49 super.onForceLoad(); 50 cancelLoad(); 51 mTask = new LoadTask(); 52 if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask); 53 executePendingTask(); 54 }
相信到這裡 大家一定能搞明白資料來源變化的時候 是怎麼一步步呼叫我們的loader裡的回撥方法的,那有人肯定要繼續問
當你這個方法呼叫完畢的時候 是怎麼通知最後updateUI呢,也就是當你background方法結束以後是怎麼呼叫的
onLoadFinished方法的呢?
我們繼續看AsyncTaskLoader這個類
1 2 //在那個asynctask裡面 走完是肯定要走這個方法的 相信大家都能理解。 3 @Override 4 protected void onPostExecute(D data) { 5 if (DEBUG) Log.v(TAG, this + " onPostExecute"); 6 try { 7 AsyncTaskLoader.this.dispatchOnLoadComplete(this, data); 8 } finally { 9 mDone.countDown(); 10 } 11 } 12 //實際上走的就是這個方法。看26行- 13 void dispatchOnLoadComplete(LoadTask task, D data) { 14 if (mTask != task) { 15 if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel"); 16 dispatchOnCancelled(task, data); 17 } else { 18 if (isAbandoned()) { 19 // This cursor has been abandoned; just cancel the new data. 20 onCanceled(data); 21 } else { 22 commitContentChanged(); 23 mLastLoadCompleteTime = SystemClock.uptimeMillis(); 24 mTask = null; 25 if (DEBUG) Log.v(TAG, "Delivering result"); 26 deliverResult(data); 27 } 28 } 29 } 30 31 //這邊一下就看出來是呼叫的mListtenr的回撥方法 32 public void deliverResult(D data) { 33 if (mListener != null) { 34 mListener.onLoadComplete(this, data); 35 } 36 }
實際上這個Listener就是在Loader這個基類裡:
1 OnLoadCompleteListener<D> mListener; 2 3 public interface OnLoadCompleteListener<D> { 4 /** 5 * Called on the thread that created the Loader when the load is complete. 6 * 7 * @param loader the loader that completed the load 8 * @param data the result of the load 9 */ 10 public void onLoadComplete(Loader<D> loader, D data); 11 } 12 13 //並且通過這個註冊 14 public void registerListener(int id, OnLoadCompleteListener<D> listener) { 15 if (mListener != null) { 16 throw new IllegalStateException("There is already a listener registered"); 17 } 18 mListener = listener; 19 mId = id; 20 }
那就好了 我們就是要看一下 是在哪個地方呼叫的registerlistener這個方法 註冊他的
1 2 //回到initLoader的這個方法 注意這個方法是在LoaderManger裡面 3 public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) { 4 if (mCreatingLoader) { 5 throw new IllegalStateException("Called while creating a loader"); 6 } 7 8 LoaderInfo info = mLoaders.get(id); 9 10 if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args); 11 12 if (info == null) { 13 //下面的程式碼跳轉到30行 14 info = createAndInstallLoader(id, args, (LoaderManager.LoaderCallbacks<Object>)callback); 15 if (DEBUG) Log.v(TAG, " Created new loader " + info); 16 } else { 17 if (DEBUG) Log.v(TAG, " Re-using existing loader " + info); 18 info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback; 19 } 20 21 if (info.mHaveData && mStarted) { 22 // If the loader has already generated its data, report it now. 23 info.callOnLoadFinished(info.mLoader, info.mData); 24 } 25 26 return (Loader<D>)info.mLoader; 27 } 28 29 30 private LoaderInfo createAndInstallLoader(int id, Bundle args, 31 LoaderManager.LoaderCallbacks<Object> callback) { 32 try { 33 mCreatingLoader = true; 34 LoaderInfo info = createLoader(id, args, callback); 35 //這裡跳轉到43行 36 installLoader(info); 37 return info; 38 } finally { 39 mCreatingLoader = false; 40 } 41 } 42 43 void installLoader(LoaderInfo info) { 44 mLoaders.put(info.mId, info); 45 if (mStarted) { 46 //跳轉到51行 47 info.start(); 48 } 49 } 50 51 void start() { 52 if (mRetaining && mRetainingStarted) { 53 // Our owner is started, but we were being retained from a 54 // previous instance in the started state... so there is really 55 // nothing to do here, since the loaders are still started. 56 mStarted = true; 57 return; 58 } 59 60 if (mStarted) { 61 // If loader already started, don't restart. 62 return; 63 } 64 65 mStarted = true; 66 67 if (DEBUG) Log.v(TAG, " Starting: " + this); 68 if (mLoader == null && mCallbacks != null) { 69 mLoader = mCallbacks.onCreateLoader(mId, mArgs); 70 } 71 if (mLoader != null) { 72 if (mLoader.getClass().isMemberClass() 73 && !Modifier.isStatic(mLoader.getClass().getModifiers())) { 74 throw new IllegalArgumentException( 75 "Object returned from onCreateLoader must not be a non-static inner member class: " 76 + mLoader); 77 } 78 if (!mListenerRegistered) { 79 //就是在這裡註冊的mloader裡的回撥了,注意這裡的引數是this 也就是loaderInfo這個類 注意這個類就是loadermanger裡的內部類了 再繼續往下看 80 //我們前面說到 在asynctask裡面最終呼叫的是mLoader裡的onLoadComplete方法 所以我們就看看loaderInfo這個類裡的這個方法做了什麼看91行 81 mLoader.registerListener(mId, this); 82 mLoader.registerOnLoadCanceledListener(this); 83 mListenerRegistered = true; 84 } 85 mLoader.startLoading(); 86 } 87 } 88 89 90 91 @Override 92 public void onLoadComplete(Loader<Object> loader, Object data) { 93 if (DEBUG) Log.v(TAG, "onLoadComplete: " + this); 94 95 if (mDestroyed) { 96 if (DEBUG) Log.v(TAG, " Ignoring load complete -- destroyed"); 97 return; 98 } 99 100 if (mLoaders.get(mId) != this) { 101 // This data is not coming from the current active loader. 102 // We don't care about it. 103 if (DEBUG) Log.v(TAG, " Ignoring load complete -- not active"); 104 return; 105 } 106 107 LoaderInfo pending = mPendingLoader; 108 if (pending != null) { 109 // There is a new request pending and we were just 110 // waiting for the old one to complete before starting 111 // it. So now it is time, switch over to the new loader. 112 if (DEBUG) Log.v(TAG, " Switching to pending loader: " + pending); 113 mPendingLoader = null; 114 mLoaders.put(mId, null); 115 destroy(); 116 installLoader(pending); 117 return; 118 } 119 120 // Notify of the new data so the app can switch out the old data before 121 // we try to destroy it. 122 if (mData != data || !mHaveData) { 123 mData = data; 124 mHaveData = true; 125 if (mStarted) { 126 //繼續往下 看第149行 127 callOnLoadFinished(loader, data); 128 } 129 } 130 131 //if (DEBUG) Log.v(TAG, " onLoadFinished returned: " + this); 132 133 // We have now given the application the new loader with its 134 // loaded data, so it should have stopped using the previous 135 // loader. If there is a previous loader on the inactive list, 136 // clean it up. 137 LoaderInfo info = mInactiveLoaders.get(mId); 138 if (info != null && info != this) { 139 info.mDeliveredData = false; 140 info.destroy(); 141 mInactiveLoaders.remove(mId); 142 } 143 144 if (mHost != null && !hasRunningLoaders()) { 145 mHost.mFragmentManager.startPendingDeferredFragments(); 146 } 147 } 148 149 void callOnLoadFinished(Loader<Object> loader, Object data) { 150 if (mCallbacks != null) { 151 String lastBecause = null; 152 if (mHost != null) { 153 lastBecause = mHost.mFragmentManager.mNoTransactionsBecause; 154 mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished"; 155 } 156 try { 157 if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": " 158 + loader.dataToString(data)); 159 //到這裡就真相大白了,最終callback是在這裡呼叫的onLoadFinished方法也就是我們經常重寫的方法 160 mCallbacks.onLoadFinished(loader, data); 161 } finally { 162 if (mHost != null) { 163 mHost.mFragmentManager.mNoTransactionsBecause = lastBecause; 164 } 165 } 166 mDeliveredData = true; 167 } 168 }
好,到這裡 我們就把Loader框架中的 資料傳遞 整個流程給摸清楚了。最後我們再來看看 他的生命週期是如何管理的吧。
我們可以先看看activity的:
1 //看activity的onStart方法 2 protected void onStart() { 3 if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this); 4 mCalled = true; 5 //繼續看12行 這個地方mFragements 你就理解成activity本身即可,不多做解釋 這個地方要搞清楚 又是另外一塊了 有興趣的可以自行谷歌activity和fragment如何建立關係 6 mFragments.doLoaderStart(); 7 8 getApplication().dispatchActivityStarted(this); 9 } 10 11 //這個函式就很明顯了 呼叫了manager的dostart函式 12 void doLoaderStart() { 13 if (mLoadersStarted) { 14 return; 15 } 16 mLoadersStarted = true; 17 18 if (mLoaderManager != null) { 19 //跳轉到30行 20 mLoaderManager.doStart(); 21 } else if (!mCheckedForLoaderManager) { 22 mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false); 23 } 24 mCheckedForLoaderManager = true; 25 } 26 27 //------------------注意上面的程式碼都在activity裡,下面的開始 都在LoaderManger類裡了 28 29 void doStart() { 30 if (DEBUG) Log.v(TAG, "Starting in " + this); 31 if (mStarted) { 32 RuntimeException e = new RuntimeException("here"); 33 e.fillInStackTrace(); 34 Log.w(TAG, "Called doStart when already started: " + this, e); 35 return; 36 } 37 38 mStarted = true; 39 40 // Call out to sub classes so they can start their loaders 41 // Let the existing loaders know that we want to be notified when a load is complete 42 for (int i = mLoaders.size()-1; i >= 0; i--) { 43 //跳轉到50行 44 mLoaders.valueAt(i).start(); 45 } 46 } 47 48 49 void start() { 50 if (mRetaining && mRetainingStarted) { 51 // Our owner is started, but we were being retained from a 52 // previous instance in the started state... so there is really 53 // nothing to do here, since the loaders are still started. 54 mStarted = true; 55 return; 56 } 57 58 if (mStarted) { 59 // If loader already started, don't restart. 60 return; 61 } 62 63 mStarted = true; 64 65 if (DEBUG) Log.v(TAG, " Starting: " + this); 66 if (mLoader == null && mCallbacks != null) { 67 //原來onCreateLoader這個回撥方法 是在這裡呼叫的 怪不得谷歌說這個方法是必定會被執行並且只會被執行一次的方法! 68 mLoader = mCallbacks.onCreateLoader(mId, mArgs); 69 } 70 if (mLoader != null) { 71 if (mLoader.getClass().isMemberClass() 72 && !Modifier.isStatic(mLoader.getClass().getModifiers())) { 73 throw new IllegalArgumentException( 74 "Object returned from onCreateLoader must not be a non-static inner member class: " 75 + mLoader); 76 } 77 if (!mListenerRegistered) { 78 mLoader.registerListener(mId, this); 79 mLoader.registerOnLoadCanceledListener(this); 80 mListenerRegistered = true; 81 } 82 //你看這裡呼叫了startLoading方法 這個方法是屬於mLoader的 跳轉到88行 83 mLoader.startLoading(); 84 } 85 } 86 87 //88- 98行是loader這個類裡的 88 public final void startLoading() { 89 mStarted = true; 90 mReset = false; 91 mAbandoned = false; 92 onStartLoading(); 93 } 94 95 //你看最終是呼叫的這個方法,注意他是空方法 是交給子類去實現的,我們去看看cursorloader這個子類是怎麼實現的吧。 96 protected void onStartLoading() { 97 } 98 //99- 112行 是cursorLoader這個類的程式碼 99 100 //你看這個地方 直接呼叫了forceload方法 這個方法大家前面肯定有印象 他最終會啟動那個asynctask 去執行background方法 101 //這也就解釋了 第一次我們的資料是怎麼來的,比如說 假設我們的資料來源還沒有被更新的時候,為什麼會自動去查詢資料來源 並返回資料 102 //到這裡就明白了,原來是activity的onStart函式為開端 一步步走到Loader的子類的onStartLoading方法裡的,當然你如果覺得 103 //Loader不需要初始載入 只要在有變化的時候再載入 那這個方法你就可以保持為空了。 104 protected void onStartLoading() { 105 if (mCursor != null) { 106 deliverResult(mCursor); 107 } 108 if (takeContentChanged() || mCursor == null) { 109 forceLoad(); 110 } 111 } 112 113 //114-139行 為 http://developer.android.com/intl/zh-cn/reference/android/content/AsyncTaskLoader.html 這個裡面 AppListLoader 的一段原始碼 114 //你看138行 也是直接呼叫的forceLoad 這樣當我們的applist沒有變化的時候 第一次也能顯示出列表 115 /** 116 * Handles a request to start the Loader. 117 */ 118 @Override protected void onStartLoading() { 119 if (mApps != null) { 120 // If we currently have a result available, deliver it 121 // immediately. 122 deliverResult(mApps); 123 } 124 125 // Start watching for changes in the app data. 126 if (mPackageObserver == null) { 127 mPackageObserver = new PackageIntentReceiver(this); 128 } 129 130 // Has something interesting in the configuration changed since we 131 // last built the app list? 132 boolean configChange = mLastConfig.applyNewConfig(getContext().getResources()); 133 134 if (takeContentChanged() || mApps == null || configChange) { 135 // If the data has changed since the last time it was loaded 136 // or is not currently available, start a load. 137 forceLoad(); 138 } 139 }
start流程 我們分析完畢了 最後我們再看看stop流程吧 看完這個 其他生命週期 我們就不分析了留給讀者自己感興趣的話自己分析試試看。
1 //我們來看看fragment的onDestroy方法 都做了什麼 2 public void onDestroy() { 3 mCalled = true; 4 //Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager 5 // + " mLoaderManager=" + mLoaderManager); 6 if (!mCheckedForLoaderManager) { 7 mCheckedForLoaderManager = true; 8 mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false); 9 } 10 if (mLoaderManager != null) { 11 //跳轉到16行 12 mLoaderManager.doDestroy(); 13 } 14 } 15 //上面的程式碼 是在fragment裡 下面的程式碼在loadermanger裡 16 void doDestroy() { 17 if (!mRetaining) { 18 if (DEBUG) Log.v(TAG, "Destroying Active in " + this); 19 for (int i = mLoaders.size()-1; i >= 0; i--) { 20 mLoaders.valueAt(i).destroy(); 21 } 22 mLoaders.clear(); 23 } 24 25 if (DEBUG) Log.v(TAG, "Destroying Inactive in " + this); 26 for (int i = mInactiveLoaders.size()-1; i >= 0; i--) { 27 mInactiveLoaders.valueAt(i).destroy(); 28 } 29 mInactiveLoaders.clear(); 30 } 31 //下面這個destroy流程 可以清晰的看到很多東西 包括clear所有回撥等 32 void destroy() { 33 if (DEBUG) Log.v(TAG, " Destroying: " + this); 34 mDestroyed = true; 35 boolean needReset = mDeliveredData; 36 mDeliveredData = false; 37 if (mCallbacks != null && mLoader != null && mHaveData && needReset) { 38 if (DEBUG) Log.v(TAG, " Reseting: " + this); 39 String lastBecause = null; 40 if (mHost != null) { 41 lastBecause = mHost.mFragmentManager.mNoTransactionsBecause; 42 mHost.mFragmentManager.mNoTransactionsBecause = "onLoaderReset"; 43 } 44 try { 45 mCallbacks.onLoaderReset(mLoader); 46 } finally { 47 if (mHost != null) { 48 mHost.mFragmentManager.mNoTransactionsBecause = lastBecause; 49 } 50 } 51 } 52 mCallbacks = null; 53 mData = null; 54 mHaveData = false; 55 if (mLoader != null) { 56 if (mListenerRegistered) { 57 mListenerRegistered = false; 58 mLoader.unregisterListener(this); 59 mLoader.unregisterOnLoadCanceledListener(this); 60 } 61 //在這呼叫了rest 62 mLoader.reset(); 63 } 64 if (mPendingLoader != null) { 65 mPendingLoader.destroy(); 66 } 67 } 68 //最後我們來看看loader裡的程式碼 就能明白了 當fragement destroy的時候最終的呼叫來到了子類的onReset方法 69 public void reset() { 70 onReset(); 71 mReset = true; 72 mStarted = false; 73 mAbandoned = false; 74 mContentChanged = false; 75 mProcessingChange = false; 76 } 77 78 /** 79 * Subclasses must implement this to take care of resetting their loader, 80 * as per {@link #reset()}. This is not called by clients directly, 81 * but as a result of a call to {@link #reset()}. 82 * This will always be called from the process's main thread. 83 */ 84 protected void onReset() { 85 } 86 87 //這裡是cURSORLOADER的程式碼了 你看這裡關閉了cursor 88 @Override 89 protected void onReset() { 90 super.onReset(); 91 92 // Ensure the loader is stopped 93 onStopLoading(); 94 95 if (mCursor != null && !mCursor.isClosed()) { 96 mCursor.close(); 97 } 98 mCursor = null; 99 } 100 101 //同樣的 我們也能看到applistloader原始碼裡面 也是在這個函式裡清除了廣播接收器。 102 //所以讀到這裡 我們就知道 loader的強大了。你只需要搞清楚這些生命週期的函式的意義 103 //就可以重寫他們,至於什麼時候呼叫 loader都幫你做好了 你只需要在裡面實現你自己的邏輯即可!非常強大 非常好用 104 @Override protected void onReset() { 105 super.onReset(); 106 107 // Ensure the loader is stopped 108 onStopLoading(); 109 110 // At this point we can release the resources associated with 'apps' 111 // if needed. 112 if (mApps != null) { 113 onReleaseResources(mApps); 114 mApps = null; 115 } 116 117 // Stop monitoring for changes. 118 if (mPackageObserver != null) { 119 getContext().unregisterReceiver(mPackageObserver); 120 mPackageObserver = null; 121 } 122 }
---恢復內容結束---