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 { 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 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 loader, Cursor data) {103 104 // Swap the new cursor in. (The framework will take care of closing the105 // 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 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 no119 // longer using it.120 mAdapter.swapCursor(null);121 }122 }123 124 }
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 }
1 /** 2 * A custom Loader that loads all of the installed applications. 3 */ 4 public static class AppListLoader extends AsyncTaskLoader > { 5 final InterestingConfigChanges mLastConfig = new InterestingConfigChanges(); 6 final PackageManager mPm; 7 8 List 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 loadInBackground() { 23 // Retrieve all known applications. 24 List apps = mPm.getInstalledApplications( 25 PackageManager.GET_UNINSTALLED_PACKAGES | 26 PackageManager.GET_DISABLED_COMPONENTS); 27 if (apps == null) { 28 apps = new ArrayList (); 29 } 30 31 final Context context = getContext(); 32 33 // Create corresponding array of entries and load their labels. 34 List entries = new ArrayList (apps.size()); 35 for (int i=0; i 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 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 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 stopped130 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 associated148 * with an actively loaded data set.149 */150 protected void onReleaseResources(List apps) {151 // For a simple List<> there is nothing to do. For something152 // like a Cursor, we would close it here.153 }154 }
1 public abstract class LoaderManager { 2 /** 3 * Callback interface for a client to interact with the manager. 4 */ 5 public interface LoaderCallbacks { 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 onCreateLoader(int id, Bundle args);14 15 /**16 * Called when a previously created loader has finished its load. Note17 * that normally an application is not allowed to commit fragment18 * transactions while in this call, since it can happen after an19 * activity's state is saved. See { @link FragmentManager#beginTransaction()20 * FragmentManager.openTransaction()} for further discussion on this.21 * 22 *
This function is guaranteed to be called prior to the release of23 * the last data that was supplied for this Loader. At this point24 * you should remove all use of the old data (since it will be released25 * soon), but should not do your own release of the data since its Loader26 * owns it and will take care of that. The Loader will take care of27 * management of its data so you don't have to. In particular:28 *29 *
30 *
The Loader will monitor for changes to the data, and report31 * them to you through new calls here. You should not monitor the32 * data yourself. For example, if the data is a {
@link android.database.Cursor}33 * and you place it in a { @link android.widget.CursorAdapter}, use34 * the { @link android.widget.CursorAdapter#CursorAdapter(android.content.Context,35 * android.database.Cursor, int)} constructor without passing36 * 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 CursorAdapter39 * from doing its own observing of the Cursor, which is not needed since40 * when a change happens you will get a new Cursor throw another call41 * here.42 *
The Loader will release the data once it knows the application43 * is no longer using it. For example, if the data is44 * 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 a46 * { @link android.widget.CursorAdapter}, you should use the47 * { @link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}48 * method so that the old Cursor is not closed.49 *
50 *51 * @param loader The Loader that has finished.52 * @param data The data generated by the Loader.53 */54 public void onLoadFinished(Loader loader, D data);55 56 /**57 * Called when a previously created loader is being reset, and thus58 * making its data unavailable. The application should at this point59 * 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 loader);64 }
1 2 3 //这个是一个观察者 当发生变化的时候 他调用了onContentChanged方法 4 public final class ForceLoadContentObserver extends ContentObserver { 5 public ForceLoadContentObserver() { 6 super(new Handler()); 7 } 8 9 @Override10 public boolean deliverSelfNotifications() {11 return true;12 }13 14 @Override15 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 load27 // new data right now... but keep track of it changing to28 // 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 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 @Override13 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 not24 // cancel the Loader itself and still expects to receive a result.25 // Additionally, the Loader's own state will not have been updated to26 // 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 @Override48 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 }
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 mListener; 2 3 public interface OnLoadCompleteListener { 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 loader, D data);11 }12 13 //并且通过这个注册14 public void registerListener(int id, OnLoadCompleteListener 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 Loader initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks 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 )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)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 )info.mLoader; 27 } 28 29 30 private LoaderInfo createAndInstallLoader(int id, Bundle args, 31 LoaderManager.LoaderCallbacks 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 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 just110 // waiting for the old one to complete before starting111 // 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 before121 // 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 its134 // loaded data, so it should have stopped using the previous135 // 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 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 }