博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 异步加载神器Loader全解析
阅读量:6798 次
发布时间:2019-06-26

本文共 49061 字,大约阅读时间需要 163 分钟。

在之前呢,我们经常会有这种需求,比如在某个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
{ 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 }

可以仔细的观察一下这个代码,我们能发现 使用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
{

我们现在要实现一个不用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
{10 private Cursor mCursor;11 12 public SimpleCursorLoader(Context context) {13 super(context);14 }15 16 /* 在子线程里运作 */17 @Override18 public abstract Cursor loadInBackground();19 20 /* 在ui 线程里运作 */21 @Override22 public void deliverResult(Cursor cursor) {23 if (isReset()) {24 // An async query came in while the loader is stopped25 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 @Override43 protected void onStartLoading() {44 if (mCursor != null) {45 deliverResult(mCursor);46 }47 if (takeContentChanged() || mCursor == null) {48 forceLoad();49 }50 }51 52 @Override53 protected void onStopLoading() {54 cancelLoad();55 }56 57 @Override58 public void onCanceled(Cursor cursor) {59 if (cursor != null && !cursor.isClosed()) {60 cursor.close();61 }62 }63 64 @Override65 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     @Override22     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,但是SimpleCursorAdapter28         //需要的是_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                 //记得在你操作数据库的时候 通知一下这个uri36                 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     @Override103     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
> { 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 }

好,到这里流程就很明显了,在loader里 注册广播接收器,当广播接收器 收到广播以后 就调用loader的onContentChanged方法,

这个方法一调用 AppListLoader里的loadInBackGround就会被调用,然后当loadInBackGround执行完毕以后 就会把结果

传递给onLoadFinished方法了。 搞清楚这个流程 你就真正学会了使用loader这个大杀器了。当然了,我们并不满足于此,loader

还有一个特性就是可以自动管理他自己的生命周期 等等。我们现在就去看看他的源码,是如何完成这一点的。 并且上述几个方法

之间是如何相互调用的,顺序如何。

 

首先 我们要搞清楚几个类之间的关系:

1 public class CursorLoader extends AsyncTaskLoader
{2 3 4 public abstract class AsyncTaskLoader
extends Loader
{5 6 public class Loader
{

这样就很清晰。首先由一个实体类作为最基础的基类,Loader 注意他可以接受一个泛型为参数,然后有一个抽象类:AsyncTaskLoader 也是泛型作为参数。

最后实际调用运作的类就是CursorLoader类了,这里就可以看出来 传进去的泛型是一个Cursor。你在自定义Loader的时候,这个泛型参数 当然是可以自己决定的,

比如官方demo里 传的就是一个List。

搞清楚 他们三者之间的关系,剩下的就简单多了。可以逐步分析了。

在前面的3个demo里,我们分别演示了在fragment和activity里 调用loader的方法。 那我们就看看 这两者之间有什么异同点。先来看fragment。

fragment里 我们是这样调用的:

1  //这个地方初始化了我们的loader2             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的值就是root12 //也就是说一个actvity 只能有一个loadermanger 但是我们可以发现在fragment里 传进去的值是下面这个:13 // Internal unique name for this fragment;14 //String mWho;15 //也就是说每一个fragment的mWho的值都是唯一的,而在activty中,是维护了一个map,一个key 对应一个loadermanager16 //key就是fragment的那个唯一的标示,或者是activity自己,activity自己的标示就是(root)了17     LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {18         if (mAllLoaderManagers == null) {19             mAllLoaderManagers = new ArrayMap
();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
{ 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 }

一看就知道 loadermanger 其实是一个抽象类。就是定义了一些 我们需要的接口而已,这些接口方法的含义和用法 在那3个demo里 相信大家都有了解,不多说。

我们去看看这个抽象类的实现类,为什么要看他,因为你在get到这个maganger以后 马上就去调用了他的init方法 我们就看看这部分的逻辑是怎么样的:

1 public 
Loader
initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks
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
)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)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
)info.mLoader;26 }27 28 //这个就是现在存活的loader29 final SparseArray
mLoaders = new SparseArray
(0);30 31 //这个是已经运行结束的loader32 final SparseArray
mInactiveLoaders = new SparseArray
(0);33 34 //其实这个创建loader的过程特别简单,我们主要看第三个参数,callback 这个参数35 //一想就明白,在前面3个demo里我们是直接在fragemet和activity里实现的callback36 //所以传进去的就是this,也就是说 回调就是在这个函数里 真正的和loader 发生了关联了37 private LoaderInfo createAndInstallLoader(int id, Bundle args,38 LoaderManager.LoaderCallbacks
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         @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 }

相信到这里 大家一定能搞明白数据源变化的时候 是怎么一步步调用我们的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
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 }

好,到这里 我们就把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 it121             // 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 we131         // 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 loaded136             // 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 stopped108 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 }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

---恢复内容结束---

转载于:https://www.cnblogs.com/punkisnotdead/p/4861376.html

你可能感兴趣的文章
陶哲轩实分析例17.2.3
查看>>
兩個集合之間的全體部分函數可以形成一個集合
查看>>
Elementary Methods in Number Theory Exercise 1.2.17
查看>>
委托由浅入深学习
查看>>
小程序中通过判断id来删除数据,当数据长度为0时,显示隐藏部分(交流QQ群:604788754)...
查看>>
php把数据转换为json格式
查看>>
Java线程(学习整理)--2---加入另一个线程join
查看>>
replace into 浅析之一
查看>>
软件工程15 个人阅读作业2—提出问题
查看>>
Windows Azure Traffic Manager的新的管理API
查看>>
Mybatis学习(4)输入映射、输出映射、动态sql
查看>>
java设计模式-策略模式
查看>>
iOS随笔记录
查看>>
objective-c面向对象
查看>>
Windows 7下Git SSH 创建Key【待解决?】
查看>>
阿里云服务器Linux CentOS安装配置(七)域名解析
查看>>
最长公共前缀---简单
查看>>
课程引言作业一
查看>>
like 大数据字段 查询慢
查看>>
JSON 数据格式
查看>>