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

package com.example.administrator.modifytestview;import android.app.Activity;
import android.app.FragmentManager;
import android.app.ListFragment;
import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);FragmentManager fm = getFragmentManager();CursorLoaderListFragment list = new CursorLoaderListFragment();fm.beginTransaction().replace(R.id.root, list).commit();}public static class CursorLoaderListFragment extends ListFragmentimplements LoaderManager.LoaderCallbacks<Cursor> {// This is the Adapter being used to display the list's data.
        SimpleCursorAdapter mAdapter;// If non-null, this is the current filter the user has provided.
        String mCurFilter;@Overridepublic void onActivityCreated(Bundle savedInstanceState) {mAdapter = new SimpleCursorAdapter(getActivity(),android.R.layout.simple_list_item_2, null,new String[]{Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS},new int[]{android.R.id.text1, android.R.id.text2}, 0);setListAdapter(mAdapter);//这个地方初始化了我们的loadergetLoaderManager().initLoader(0, null, this);super.onActivityCreated(savedInstanceState);}@Overridepublic void onListItemClick(ListView l, View v, int position, long id) {// Insert desired behavior here.Log.i("FragmentComplexList", "Item clicked: " + id);}// These are the Contacts rows that we will retrieve.static final String[] CONTACTS_SUMMARY_PROJECTION = new String[]{Contacts._ID,Contacts.DISPLAY_NAME,Contacts.CONTACT_STATUS,Contacts.CONTACT_PRESENCE,Contacts.PHOTO_ID,Contacts.LOOKUP_KEY,};//只会调用一次public Loader<Cursor> onCreateLoader(int id, Bundle args) {// This is called when a new Loader needs to be created.  This// sample only has one Loader, so we don't care about the ID.// First, pick the base URI to use depending on whether we are// currently filtering.
            Uri baseUri;if (mCurFilter != null) {baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI,Uri.encode(mCurFilter));} else {baseUri = Contacts.CONTENT_URI;}// Now create and return a CursorLoader that will take care of// creating a Cursor for the data being displayed.String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("+ Contacts.HAS_PHONE_NUMBER + "=1) AND ("+ Contacts.DISPLAY_NAME + " != '' ))";//返回的是对这个数据源的监控return new CursorLoader(getActivity(), baseUri,CONTACTS_SUMMARY_PROJECTION, select, null,Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");}//每次数据源都有更新的时候,就会回调这个方法,然后update 我们的ui了。public void onLoadFinished(Loader<Cursor> loader, Cursor data) {// Swap the new cursor in.  (The framework will take care of closing the// old cursor once we return.)
            mAdapter.swapCursor(data);// The list should now be shown.if (isResumed()) {setListShown(true);} else {setListShownNoAnimation(true);}}public void onLoaderReset(Loader<Cursor> loader) {// This is called when the last Cursor provided to onLoadFinished()// above is about to be closed.  We need to make sure we are no// longer using it.mAdapter.swapCursor(null);}}}

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

public class CursorLoader extends AsyncTaskLoader<Cursor> {

我们现在要实现一个不用contentProvider的Loader 也是基于AsyncTaskLoader来的。

先给出一个抽象类:

package com.example.administrator.activeandroidtest3;import android.content.AsyncTaskLoader;
import android.content.Context;
import android.database.Cursor;public abstract class SimpleCursorLoader extends AsyncTaskLoader<Cursor> {private Cursor mCursor;public SimpleCursorLoader(Context context) {super(context);}/* 在子线程里运作 */@Overridepublic abstract Cursor loadInBackground();/* 在ui 线程里运作 */@Overridepublic void deliverResult(Cursor cursor) {if (isReset()) {// An async query came in while the loader is stoppedif (cursor != null) {cursor.close();}return;}Cursor oldCursor = mCursor;mCursor = cursor;if (isStarted()) {super.deliverResult(cursor);}if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {oldCursor.close();}}@Overrideprotected void onStartLoading() {if (mCursor != null) {deliverResult(mCursor);}if (takeContentChanged() || mCursor == null) {forceLoad();}}@Overrideprotected void onStopLoading() {cancelLoad();}@Overridepublic void onCanceled(Cursor cursor) {if (cursor != null && !cursor.isClosed()) {cursor.close();}}@Overrideprotected void onReset() {super.onReset();onStopLoading();if (mCursor != null && !mCursor.isClosed()) {mCursor.close();}mCursor = null;}
}

然后我们再接着定义我们最终的 不需要provider的loader实现类(注意你如果想写的比较完美的话 cursor记得用抽象类的,抽象类的那个就不要写成private的了,我这里为了图简单 直接用自己构造的)。

package com.example.administrator.activeandroidtest3;import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;/*** Created by Administrator on 2015/10/7.*/
public class SpecialLoader extends SimpleCursorLoader {ForceLoadContentObserver mObserver = new ForceLoadContentObserver();private Context context;public SpecialLoader(Context context) {super(context);this.context = context;}@Overridepublic Cursor loadInBackground() {DatabaseHelper dh = new DatabaseHelper(context, "Test.db");SQLiteDatabase database = dh.getReadableDatabase();String table = "Student";String[] columns = new String[]{"Name", "No"};//这个地方因为我用的是activeandroid 的orm 框架,所以默认的自增长主键是Id,但是SimpleCursorAdapter//需要的是_id 否则会报错,所以这里要重命名一下Cursor cursor = database.rawQuery("SELECT Id AS _id,Name,No FROM Student", null);if (database != null) {if (cursor != null) {//注册一下这个观察者
                cursor.registerContentObserver(mObserver);//这边也要注意 一定要监听这个uri的变化。但是如果你这个uri没有对应的provider的话//记得在你操作数据库的时候 通知一下这个uri
                cursor.setNotificationUri(context.getContentResolver(), MainActivity.uri);}}return cursor;}
}

然后我们在简单看下activity 主类里的代码:

package com.example.administrator.activeandroidtest3;import android.app.Activity;
import android.app.LoaderManager;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;import com.activeandroid.query.Select;import java.util.List;
import java.util.Random;public class MainActivity extends Activity implements LoaderManager.LoaderCallbacks {public static final Uri uri = Uri.parse("content://com.example.student");private TextView addTv;private ListView lv;private SimpleCursorAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);addTv = (TextView) this.findViewById(R.id.add);addTv.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Student student = new Student();student.name = getRandomString(5);student.no = (int) (Math.random() * 1000) + "";student.sex = (int) (Math.random() * 1);student.save();//操作完数据库要notify 不然loader那边收不到哦getContentResolver().notifyChange(uri, null);}});lv = (ListView) this.findViewById(R.id.lv);adapter = new SimpleCursorAdapter(MainActivity.this,android.R.layout.simple_list_item_2, null,new String[]{"Name", "No"},new int[]{android.R.id.text1, android.R.id.text2}, 0);lv.setAdapter(adapter);getLoaderManager().initLoader(0, null, this);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// Handle action bar item clicks here. The action bar will// automatically handle clicks on the Home/Up button, so long// as you specify a parent activity in AndroidManifest.xml.int id = item.getItemId();//noinspection SimplifiableIfStatementif (id == R.id.action_settings) {return true;}return super.onOptionsItemSelected(item);}public static String getRandomString(int length) { //length表示生成字符串的长度String base = "abcdefghijklmnopqrstuvwxyz0123456789";Random random = new Random();StringBuffer sb = new StringBuffer();for (int i = 0; i < length; i++) {int number = random.nextInt(base.length());sb.append(base.charAt(number));}return sb.toString();}@Overridepublic Loader onCreateLoader(int id, Bundle args) {SpecialLoader loader = new SpecialLoader(MainActivity.this);return loader;}@Overridepublic void onLoadFinished(Loader loader, Object data) {adapter.swapCursor((Cursor) data);}@Overridepublic void onLoaderReset(Loader loader) {}
}

最后我们看下运行的效果:

好,那到这里 又有人要说了,你这个说来说去 还不是只能支持provider或者db类型的数据源吗?好 接着往下,

我们给出另外一个例子,不过这个例子是谷歌官方的例子,我就取其中重要的部分给予注释讲解。

http://developer.android.com/intl/zh-cn/reference/android/content/AsyncTaskLoader.html

首先说一下 这个例子是干嘛的,他主要是监听手机里app list的变化,比如你删除了一个应用

安装了一个应用,马上就能捕捉到你的手机里app list的变化 并显示在界面,大家都知道 监听app list

是通过监听系统广播来完成的。 我就主要讲一下 这个官方demo里 是如何在监听到系统广播以后和loader结合起来

然后自动回调方法的。

/*** Helper class to look for interesting changes to the installed apps* so that the loader can be updated.*/
public static class PackageIntentReceiver extends BroadcastReceiver {final AppListLoader mLoader;//这个构造函数是很重要的 他接收的 就是自定义的loaderpublic PackageIntentReceiver(AppListLoader loader) {mLoader = loader;IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);filter.addAction(Intent.ACTION_PACKAGE_REMOVED);filter.addAction(Intent.ACTION_PACKAGE_CHANGED);filter.addDataScheme("package");mLoader.getContext().registerReceiver(this, filter);// Register for events related to sdcard installation.IntentFilter sdFilter = new IntentFilter();sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);//在这个地方 直接用loader来注册这个广播接收器mLoader.getContext().registerReceiver(this, sdFilter);}//在收到广播以后 什么事情都没有做,而是调用了loader的onContentChanged方法@Override public void onReceive(Context context, Intent intent) {// Tell the loader about the change.
        mLoader.onContentChanged();}
}

你看这里的25-26行 调用了 loader的onContentChanged方法。继续看下面的loader、

/*** A custom Loader that loads all of the installed applications.*/
public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();final PackageManager mPm;List<AppEntry> mApps;PackageIntentReceiver mPackageObserver;public AppListLoader(Context context) {super(context);// Retrieve the package manager for later use; note we don't// use 'context' directly but instead the save global application// context returned by getContext().mPm = getContext().getPackageManager();}//实际上最重要的就是这个方法了,每当这个回调方法被调用的时候 就去取applist 然后将结果返回到//onLoadFinished 这个回调方法里面!@Override public List<AppEntry> loadInBackground() {// Retrieve all known applications.List<ApplicationInfo> apps = mPm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES |PackageManager.GET_DISABLED_COMPONENTS);if (apps == null) {apps = new ArrayList<ApplicationInfo>();}final Context context = getContext();// Create corresponding array of entries and load their labels.List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());for (int i=0; i<apps.size(); i++) {AppEntry entry = new AppEntry(this, apps.get(i));entry.loadLabel(context);entries.add(entry);}// Sort the list.
        Collections.sort(entries, ALPHA_COMPARATOR);// Done!return entries;}/*** Called when there is new data to deliver to the client.  The* super class will take care of delivering it; the implementation* here just adds a little more logic.*/@Override public void deliverResult(List<AppEntry> apps) {if (isReset()) {// An async query came in while the loader is stopped.  We// don't need the result.if (apps != null) {onReleaseResources(apps);}}List<AppEntry> oldApps = mApps;mApps = apps;if (isStarted()) {// If the Loader is currently started, we can immediately// deliver its results.
            super.deliverResult(apps);}// At this point we can release the resources associated with// 'oldApps' if needed; now that the new result is delivered we// know that it is no longer in use.if (oldApps != null) {onReleaseResources(oldApps);}}/*** Handles a request to start the Loader.*/@Override protected void onStartLoading() {if (mApps != null) {// If we currently have a result available, deliver it// immediately.
            deliverResult(mApps);}// Start watching for changes in the app data.if (mPackageObserver == null) {mPackageObserver = new PackageIntentReceiver(this);}// Has something interesting in the configuration changed since we// last built the app list?boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());if (takeContentChanged() || mApps == null || configChange) {// If the data has changed since the last time it was loaded// or is not currently available, start a load.
            forceLoad();}}/*** Handles a request to stop the Loader.*/@Override protected void onStopLoading() {// Attempt to cancel the current load task if possible.
        cancelLoad();}/*** Handles a request to cancel a load.*/@Override public void onCanceled(List<AppEntry> apps) {super.onCanceled(apps);// At this point we can release the resources associated with 'apps'// if needed.
        onReleaseResources(apps);}/*** Handles a request to completely reset the Loader.*/@Override protected void onReset() {super.onReset();// Ensure the loader is stopped
        onStopLoading();// At this point we can release the resources associated with 'apps'// if needed.if (mApps != null) {onReleaseResources(mApps);mApps = null;}// Stop monitoring for changes.if (mPackageObserver != null) {getContext().unregisterReceiver(mPackageObserver);mPackageObserver = null;}}/*** Helper function to take care of releasing resources associated* with an actively loaded data set.*/protected void onReleaseResources(List<AppEntry> apps) {// For a simple List<> there is nothing to do.  For something// like a Cursor, we would close it here.
    }
}

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

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

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

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

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

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

public class CursorLoader extends AsyncTaskLoader<Cursor> {public abstract class AsyncTaskLoader<D> extends Loader<D> {public class Loader<D> {

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

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

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

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

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

fragment里 我们是这样调用的:

  //这个地方初始化了我们的loadergetLoaderManager().initLoader(0, null, this);

直接get了一个manager 然后init他。我们进去看fragment的源码:

//这边就能看出来一个fragment只能有一个loadermanager了。
public LoaderManager getLoaderManager() {if (mLoaderManager != null) {return mLoaderManager;}//mHost很好理解 就是fragment的宿主,也就是跟fragment 相关联的activity。if (mHost == null) {throw new IllegalStateException("Fragment " + this + " not attached to Activity");}mCheckedForLoaderManager = true;mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, true);return mLoaderManager;}

既然 我们知道 fragment的getLoaderManager也是通过activity的getLoader去调用的,那我们就去activity里的源码看看 :

//在activty中最终实际上调用的就是他了 是这个方法
  LoaderManagerImpl getLoaderManagerImpl() {if (mLoaderManager != null) {return mLoaderManager;}mCheckedForLoaderManager = true;mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/);return mLoaderManager;}//这个地方就能看到 主要的第一个参数 who,你到这就能发现 如果是activity自己调用的话,传进去的who的值就是root
//也就是说一个actvity 只能有一个loadermanger 但是我们可以发现在fragment里 传进去的值是下面这个:
// Internal unique name for this fragment;
//String mWho;
//也就是说每一个fragment的mWho的值都是唯一的,而在activty中,是维护了一个map,一个key 对应一个loadermanager
//key就是fragment的那个唯一的标示,或者是activity自己,activity自己的标示就是(root)了
    LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {if (mAllLoaderManagers == null) {mAllLoaderManagers = new ArrayMap<String, LoaderManager>();}LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);if (lm == null) {if (create) {lm = new LoaderManagerImpl(who, this, started);mAllLoaderManagers.put(who, lm);}} else {lm.updateHostController(this);}return lm;}

好 一直到这里 ,我们就可以下一个结论了,真正的loadermanager都是存储在activity中的,包括fragment的loadermanager也是,通过一个map来保证 get的时候

取的manager是自己对应的,并且全局唯一。继续往下看:

public abstract class LoaderManager {/*** Callback interface for a client to interact with the manager.*/public interface LoaderCallbacks<D> {/*** Instantiate and return a new Loader for the given ID.** @param id The ID whose loader is to be created.* @param args Any arguments supplied by the caller.* @return Return a new Loader instance that is ready to start loading.*/public Loader<D> onCreateLoader(int id, Bundle args);/*** Called when a previously created loader has finished its load.  Note* that normally an application is <em>not</em> allowed to commit fragment* transactions while in this call, since it can happen after an* activity's state is saved.  See {@link FragmentManager#beginTransaction()* FragmentManager.openTransaction()} for further discussion on this.* * <p>This function is guaranteed to be called prior to the release of* the last data that was supplied for this Loader.  At this point* you should remove all use of the old data (since it will be released* soon), but should not do your own release of the data since its Loader* owns it and will take care of that.  The Loader will take care of* management of its data so you don't have to.  In particular:** <ul>* <li> <p>The Loader will monitor for changes to the data, and report* them to you through new calls here.  You should not monitor the* data yourself.  For example, if the data is a {@link android.database.Cursor}* and you place it in a {@link android.widget.CursorAdapter}, use* the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context,* android.database.Cursor, int)} constructor <em>without</em> passing* in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY}* or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER}* (that is, use 0 for the flags argument).  This prevents the CursorAdapter* from doing its own observing of the Cursor, which is not needed since* when a change happens you will get a new Cursor throw another call* here.* <li> The Loader will release the data once it knows the application* is no longer using it.  For example, if the data is* a {@link android.database.Cursor} from a {@link android.content.CursorLoader},* you should not call close() on it yourself.  If the Cursor is being placed in a* {@link android.widget.CursorAdapter}, you should use the* {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}* method so that the old Cursor is not closed.* </ul>** @param loader The Loader that has finished.* @param data The data generated by the Loader.*/public void onLoadFinished(Loader<D> loader, D data);/*** Called when a previously created loader is being reset, and thus* making its data unavailable.  The application should at this point* remove any references it has to the Loader's data.** @param loader The Loader that is being reset.*/public void onLoaderReset(Loader<D> loader);}

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

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

public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {if (mCreatingLoader) {throw new IllegalStateException("Called while creating a loader");}//这个就是先看看是否有活动的loader 有的话就取出来 没有的话 就创建一个LoaderInfo info = mLoaders.get(id);if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);if (info == null) {// Loader doesn't already exist; create.info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);if (DEBUG) Log.v(TAG, "  Created new loader " + info);} else {if (DEBUG) Log.v(TAG, "  Re-using existing loader " + info);info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;}if (info.mHaveData && mStarted) {// If the loader has already generated its data, report it now.
            info.callOnLoadFinished(info.mLoader, info.mData);}return (Loader<D>)info.mLoader;}//这个就是现在存活的loaderfinal SparseArray<LoaderInfo> mLoaders = new SparseArray<LoaderInfo>(0);//这个是已经运行结束的loaderfinal SparseArray<LoaderInfo> mInactiveLoaders = new SparseArray<LoaderInfo>(0);//其实这个创建loader的过程特别简单,我们主要看第三个参数,callback 这个参数//一想就明白,在前面3个demo里我们是直接在fragemet和activity里实现的callback//所以传进去的就是this,也就是说 回调就是在这个函数里 真正的和loader 发生了关联了private LoaderInfo createAndInstallLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<Object> callback) {try {mCreatingLoader = true;LoaderInfo info = createLoader(id, args, callback);installLoader(info);return info;} finally {mCreatingLoader = false;}}

你看 一直到这里,我们就明白了 callback是怎么和loadermageer本身发生关联的。 我们继续往下看。这次我们要搞明白

当数据源发生变化的时候 是怎么一步步回调我们子类loader的方法的。

我们先看Loader这个基类的主要方法:

//这个是一个观察者 当发生变化的时候 他调用了onContentChanged方法public final class ForceLoadContentObserver extends ContentObserver {public ForceLoadContentObserver() {super(new Handler());}@Overridepublic boolean deliverSelfNotifications() {return true;}@Overridepublic void onChange(boolean selfChange) {onContentChanged();}}//下面这2个方法一看就明白 最终当数据源发生变化的时候 会通知这个观察者,然后这个观察者会最终调用
//onForceLoad这个方法 而onForceLoad是交给子类去实现的 也就是AsyncTaskLoader的onForceLoad方法了
public void onContentChanged() {if (mStarted) {forceLoad();} else {// This loader has been stopped, so we don't want to load// new data right now...  but keep track of it changing to// refresh later if we start again.mContentChanged = true;}}public void forceLoad() {onForceLoad();}/*** Subclasses must implement this to take care of requests to {@link #forceLoad()}.* This will always be called from the process's main thread.*/protected void onForceLoad() {}

然后看看AsyncTaskLoader的几个主要方法:

//这边一目了然 asynacTaskLoader 里面 正好是有一个AsyncTask对象的!实现了runnabele接口
//注意着参数d 这个d是干嘛的,这个d就是用来传递参数的一个泛型,可以是系统实现的loader里的cursor
//也可以是我们自己实现的loader里的list类型
final class LoadTask extends AsyncTask<Void, Void, D> implements Runnable {private final CountDownLatch mDone = new CountDownLatch(1);// Set to true to indicate that the task has been posted to a handler for// execution at a later time.  Used to throttle updates.
        boolean waiting;/* Runs on a worker thread */@Overrideprotected D doInBackground(Void... params) {if (DEBUG) Log.v(TAG, this + " >>> doInBackground");try {//这个地方就很明显了,他调用了自己的onLoadInBackGround方法D data = AsyncTaskLoader.this.onLoadInBackground();if (DEBUG) Log.v(TAG, this + "  <<< doInBackground");return data;} catch (OperationCanceledException ex) {if (!isCancelled()) {// onLoadInBackground threw a canceled exception spuriously.// This is problematic because it means that the LoaderManager did not// cancel the Loader itself and still expects to receive a result.// Additionally, the Loader's own state will not have been updated to// reflect the fact that the task was being canceled.// So we treat this case as an unhandled exception.throw ex;}if (DEBUG) Log.v(TAG, this + "  <<< doInBackground (was canceled)", ex);return null;}}//后面还有很多代码 略过
}//你看这里下面的2个函数 一看就明白了 最终task里调用的是这个抽象方法,那这个抽象方法
//就是留给我们子类自己去实现的,我们在自定义loader的时候最重要的就是重写这个方法。protected D onLoadInBackground() {return loadInBackground();}public abstract D loadInBackground();//你看这个地方 就是当数据源发生变化的时候 就会调用这个方法了,启动了我们的laodtask
//也是最终调用子类 也就是CursorLoader这样的子类的loadInBackground方法了
@Overrideprotected void onForceLoad() {super.onForceLoad();cancelLoad();mTask = new LoadTask();if (DEBUG) Log.v(TAG, "Preparing load: mTask=" + mTask);executePendingTask();}

相信到这里 大家一定能搞明白数据源变化的时候 是怎么一步步调用我们的loader里的回调方法的,那有人肯定要继续问

当你这个方法调用完毕的时候 是怎么通知最后updateUI呢,也就是当你background方法结束以后是怎么调用的

onLoadFinished方法的呢?

我们继续看AsyncTaskLoader这个类

//在那个asynctask里面 走完是肯定要走这个方法的 相信大家都能理解。
        @Overrideprotected void onPostExecute(D data) {if (DEBUG) Log.v(TAG, this + " onPostExecute");try {AsyncTaskLoader.this.dispatchOnLoadComplete(this, data);} finally {mDone.countDown();}}
//实际上走的就是这个方法。看26行-void dispatchOnLoadComplete(LoadTask task, D data) {if (mTask != task) {if (DEBUG) Log.v(TAG, "Load complete of old task, trying to cancel");dispatchOnCancelled(task, data);} else {if (isAbandoned()) {// This cursor has been abandoned; just cancel the new data.
                onCanceled(data);} else {commitContentChanged();mLastLoadCompleteTime = SystemClock.uptimeMillis();mTask = null;if (DEBUG) Log.v(TAG, "Delivering result");deliverResult(data);}}}//这边一下就看出来是调用的mListtenr的回调方法public void deliverResult(D data) {if (mListener != null) {mListener.onLoadComplete(this, data);}}

实际上这个Listener就是在Loader这个基类里:

OnLoadCompleteListener<D> mListener;public interface OnLoadCompleteListener<D> {/*** Called on the thread that created the Loader when the load is complete.** @param loader the loader that completed the load* @param data the result of the load*/public void onLoadComplete(Loader<D> loader, D data);}//并且通过这个注册public void registerListener(int id, OnLoadCompleteListener<D> listener) {if (mListener != null) {throw new IllegalStateException("There is already a listener registered");}mListener = listener;mId = id;}

那就好了 我们就是要看一下 是在哪个地方调用的registerlistener这个方法 注册他的

//回到initLoader的这个方法 注意这个方法是在LoaderManger里面public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {if (mCreatingLoader) {throw new IllegalStateException("Called while creating a loader");}LoaderInfo info = mLoaders.get(id);if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);if (info == null) {
//下面的代码跳转到30行info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);if (DEBUG) Log.v(TAG, "  Created new loader " + info);} else {if (DEBUG) Log.v(TAG, "  Re-using existing loader " + info);info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;}if (info.mHaveData && mStarted) {// If the loader has already generated its data, report it now.
            info.callOnLoadFinished(info.mLoader, info.mData);}return (Loader<D>)info.mLoader;}private LoaderInfo createAndInstallLoader(int id, Bundle args,LoaderManager.LoaderCallbacks<Object> callback) {try {mCreatingLoader = true;LoaderInfo info = createLoader(id, args, callback);//这里跳转到43行
            installLoader(info);return info;} finally {mCreatingLoader = false;}}void installLoader(LoaderInfo info) {mLoaders.put(info.mId, info);if (mStarted) {//跳转到51行
            info.start();}}void start() {if (mRetaining && mRetainingStarted) {// Our owner is started, but we were being retained from a// previous instance in the started state...  so there is really// nothing to do here, since the loaders are still started.mStarted = true;return;}if (mStarted) {// If loader already started, don't restart.return;}mStarted = true;if (DEBUG) Log.v(TAG, "  Starting: " + this);if (mLoader == null && mCallbacks != null) {mLoader = mCallbacks.onCreateLoader(mId, mArgs);}if (mLoader != null) {if (mLoader.getClass().isMemberClass()&& !Modifier.isStatic(mLoader.getClass().getModifiers())) {throw new IllegalArgumentException("Object returned from onCreateLoader must not be a non-static inner member class: "+ mLoader);}if (!mListenerRegistered) {//就是在这里注册的mloader里的回调了,注意这里的参数是this 也就是loaderInfo这个类 注意这个类就是loadermanger里的内部类了 再继续往下看//我们前面说到 在asynctask里面最终调用的是mLoader里的onLoadComplete方法 所以我们就看看loaderInfo这个类里的这个方法做了什么看91行mLoader.registerListener(mId, this);mLoader.registerOnLoadCanceledListener(this);mListenerRegistered = true;}mLoader.startLoading();}}@Overridepublic void onLoadComplete(Loader<Object> loader, Object data) {if (DEBUG) Log.v(TAG, "onLoadComplete: " + this);if (mDestroyed) {if (DEBUG) Log.v(TAG, "  Ignoring load complete -- destroyed");return;}if (mLoaders.get(mId) != this) {// This data is not coming from the current active loader.// We don't care about it.if (DEBUG) Log.v(TAG, "  Ignoring load complete -- not active");return;}LoaderInfo pending = mPendingLoader;if (pending != null) {// There is a new request pending and we were just// waiting for the old one to complete before starting// it.  So now it is time, switch over to the new loader.if (DEBUG) Log.v(TAG, "  Switching to pending loader: " + pending);mPendingLoader = null;mLoaders.put(mId, null);destroy();installLoader(pending);return;}// Notify of the new data so the app can switch out the old data before// we try to destroy it.if (mData != data || !mHaveData) {mData = data;mHaveData = true;if (mStarted) {//继续往下 看第149行
                    callOnLoadFinished(loader, data);}}//if (DEBUG) Log.v(TAG, "  onLoadFinished returned: " + this);// We have now given the application the new loader with its// loaded data, so it should have stopped using the previous// loader.  If there is a previous loader on the inactive list,// clean it up.LoaderInfo info = mInactiveLoaders.get(mId);if (info != null && info != this) {info.mDeliveredData = false;info.destroy();mInactiveLoaders.remove(mId);}if (mHost != null && !hasRunningLoaders()) {mHost.mFragmentManager.startPendingDeferredFragments();}}void callOnLoadFinished(Loader<Object> loader, Object data) {if (mCallbacks != null) {String lastBecause = null;if (mHost != null) {lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";}try {if (DEBUG) Log.v(TAG, "  onLoadFinished in " + loader + ": "+ loader.dataToString(data));//到这里就真相大白了,最终callback是在这里调用的onLoadFinished方法也就是我们经常重写的方法
                    mCallbacks.onLoadFinished(loader, data);} finally {if (mHost != null) {mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;}}mDeliveredData = true;}}

好,到这里 我们就把Loader框架中的 数据传递 整个流程给摸清楚了。最后我们再来看看 他的生命周期是如何管理的吧。

我们可以先看看activity的:

//看activity的onStart方法
protected void onStart() {if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);mCalled = true;//继续看12行 这个地方mFragements 你就理解成activity本身即可,不多做解释 这个地方要搞清楚 又是另外一块了 有兴趣的可以自行谷歌activity和fragment如何建立关系
        mFragments.doLoaderStart();getApplication().dispatchActivityStarted(this);}//这个函数就很明显了 调用了manager的dostart函数void doLoaderStart() {if (mLoadersStarted) {return;}mLoadersStarted = true;if (mLoaderManager != null) {//跳转到30行
            mLoaderManager.doStart();} else if (!mCheckedForLoaderManager) {mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);}mCheckedForLoaderManager = true;}//------------------注意上面的代码都在activity里,下面的开始 都在LoaderManger类里了void doStart() {if (DEBUG) Log.v(TAG, "Starting in " + this);if (mStarted) {RuntimeException e = new RuntimeException("here");e.fillInStackTrace();Log.w(TAG, "Called doStart when already started: " + this, e);return;}mStarted = true;// Call out to sub classes so they can start their loaders// Let the existing loaders know that we want to be notified when a load is completefor (int i = mLoaders.size()-1; i >= 0; i--) {//跳转到50行
            mLoaders.valueAt(i).start();}}void start() {if (mRetaining && mRetainingStarted) {// Our owner is started, but we were being retained from a// previous instance in the started state...  so there is really// nothing to do here, since the loaders are still started.mStarted = true;return;}if (mStarted) {// If loader already started, don't restart.return;}mStarted = true;if (DEBUG) Log.v(TAG, "  Starting: " + this);if (mLoader == null && mCallbacks != null) {//原来onCreateLoader这个回调方法 是在这里调用的 怪不得谷歌说这个方法是必定会被执行并且只会被执行一次的方法!mLoader = mCallbacks.onCreateLoader(mId, mArgs);}if (mLoader != null) {if (mLoader.getClass().isMemberClass()&& !Modifier.isStatic(mLoader.getClass().getModifiers())) {throw new IllegalArgumentException("Object returned from onCreateLoader must not be a non-static inner member class: "+ mLoader);}if (!mListenerRegistered) {mLoader.registerListener(mId, this);mLoader.registerOnLoadCanceledListener(this);mListenerRegistered = true;}//你看这里调用了startLoading方法 这个方法是属于mLoader的 跳转到88行
                mLoader.startLoading();}}//88- 98行是loader这个类里的public final void startLoading() {mStarted = true;mReset = false;mAbandoned = false;onStartLoading();}//你看最终是调用的这个方法,注意他是空方法 是交给子类去实现的,我们去看看cursorloader这个子类是怎么实现的吧。protected void onStartLoading() {}
//99-  112行 是cursorLoader这个类的代码//你看这个地方 直接调用了forceload方法 这个方法大家前面肯定有印象  他最终会启动那个asynctask 去执行background方法
//这也就解释了 第一次我们的数据是怎么来的,比如说 假设我们的数据源还没有被更新的时候,为什么会自动去查找数据源 并返回数据
//到这里就明白了,原来是activity的onStart函数为开端 一步步走到Loader的子类的onStartLoading方法里的,当然你如果觉得
//Loader不需要初始加载 只要在有变化的时候再加载 那这个方法你就可以保持为空了。protected void onStartLoading() {if (mCursor != null) {deliverResult(mCursor);}if (takeContentChanged() || mCursor == null) {forceLoad();}}//114-139行 为 http://developer.android.com/intl/zh-cn/reference/android/content/AsyncTaskLoader.html 这个里面 AppListLoader  的一段源码
//你看138行 也是直接调用的forceLoad 这样当我们的applist没有变化的时候 第一次也能显示出列表 /*** Handles a request to start the Loader.*/@Override protected void onStartLoading() {if (mApps != null) {// If we currently have a result available, deliver it// immediately.
            deliverResult(mApps);}// Start watching for changes in the app data.if (mPackageObserver == null) {mPackageObserver = new PackageIntentReceiver(this);}// Has something interesting in the configuration changed since we// last built the app list?boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());if (takeContentChanged() || mApps == null || configChange) {// If the data has changed since the last time it was loaded// or is not currently available, start a load.
            forceLoad();}}

start流程 我们分析完毕了 最后我们再看看stop流程吧 看完这个 其他生命周期 我们就不分析了留给读者自己感兴趣的话自己分析试试看。

//我们来看看fragment的onDestroy方法 都做了什么
public void onDestroy() {mCalled = true;//Log.v("foo", "onDestroy: mCheckedForLoaderManager=" + mCheckedForLoaderManager//        + " mLoaderManager=" + mLoaderManager);if (!mCheckedForLoaderManager) {mCheckedForLoaderManager = true;mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);}if (mLoaderManager != null) {//跳转到16行
            mLoaderManager.doDestroy();}}
//上面的代码 是在fragment里 下面的代码在loadermanger里void doDestroy() {if (!mRetaining) {if (DEBUG) Log.v(TAG, "Destroying Active in " + this);for (int i = mLoaders.size()-1; i >= 0; i--) {mLoaders.valueAt(i).destroy();}mLoaders.clear();}if (DEBUG) Log.v(TAG, "Destroying Inactive in " + this);for (int i = mInactiveLoaders.size()-1; i >= 0; i--) {mInactiveLoaders.valueAt(i).destroy();}mInactiveLoaders.clear();}
//下面这个destroy流程 可以清晰的看到很多东西 包括clear所有回调等void destroy() {if (DEBUG) Log.v(TAG, "  Destroying: " + this);mDestroyed = true;boolean needReset = mDeliveredData;mDeliveredData = false;if (mCallbacks != null && mLoader != null && mHaveData && needReset) {if (DEBUG) Log.v(TAG, "  Reseting: " + this);String lastBecause = null;if (mHost != null) {lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;mHost.mFragmentManager.mNoTransactionsBecause = "onLoaderReset";}try {mCallbacks.onLoaderReset(mLoader);} finally {if (mHost != null) {mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;}}}mCallbacks = null;mData = null;mHaveData = false;if (mLoader != null) {if (mListenerRegistered) {mListenerRegistered = false;mLoader.unregisterListener(this);mLoader.unregisterOnLoadCanceledListener(this);}//在这调用了rest
                mLoader.reset();}if (mPendingLoader != null) {mPendingLoader.destroy();}}
//最后我们来看看loader里的代码 就能明白了 当fragement destroy的时候最终的调用来到了子类的onReset方法public void reset() {onReset();mReset = true;mStarted = false;mAbandoned = false;mContentChanged = false;mProcessingChange = false;}/*** Subclasses must implement this to take care of resetting their loader,* as per {@link #reset()}.  This is not called by clients directly,* but as a result of a call to {@link #reset()}.* This will always be called from the process's main thread.*/protected void onReset() {}//这里是cURSORLOADER的代码了 你看这里关闭了cursor
    @Overrideprotected void onReset() {super.onReset();// Ensure the loader is stopped
        onStopLoading();if (mCursor != null && !mCursor.isClosed()) {mCursor.close();}mCursor = null;}//同样的 我们也能看到applistloader源码里面 也是在这个函数里清除了广播接收器。
//所以读到这里 我们就知道 loader的强大了。你只需要搞清楚这些生命周期的函数的意义
//就可以重写他们,至于什么时候调用 loader都帮你做好了 你只需要在里面实现你自己的逻辑即可!非常强大 非常好用@Override protected void onReset() {super.onReset();// Ensure the loader is stopped
        onStopLoading();// At this point we can release the resources associated with 'apps'// if needed.if (mApps != null) {onReleaseResources(mApps);mApps = null;}// Stop monitoring for changes.if (mPackageObserver != null) {getContext().unregisterReceiver(mPackageObserver);mPackageObserver = null;}}

转载于:https://www.cnblogs.com/zhujiabin/p/8147775.html

Android 异步加载神器Loader全解析相关推荐

  1. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53939176 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  2. Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

    本文转载自郭神的Glide分析系列:http://blog.csdn.net/guolin_blog/article/details/78582548 本文同步发表于我的微信公众号,扫一扫文章底部的二 ...

  3. Android图片加载框架最全解析(五)

    由此我们可以得知,在没有明确指定的情况下,ImageView默认的scaleType是FIT_CENTER. 有了这个前提条件,我们就可以继续去分析Glide的源码了.当然,本文中的源码还是建在第二篇 ...

  4. Android图片加载框架最全解析(七),实现带进度的Glide图片加载功能

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/78357251 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  5. Android图片加载框架最全解析(三),深入探究Glide的缓存机制

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/54895665 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  6. Android图片加载框架最全解析(一),app开发入门教程

    首先,调用Glide.with()方法用于创建一个加载图片的实例.with()方法可以接收Context.Activity或者Fragment类型的参数.也就是说我们选择的范围非常广,不管是在Acti ...

  7. Android异步加载全解析之引入二级缓存

    Android异步加载全解析之引入二级缓存 为啥要二级缓存 前面我们有了一级缓存,为啥还要二级缓存呢?说白了,这就和电脑是一样的,我们电脑有内存和硬盘,内存读取速度快,所以CPU直接读取内存中的数据, ...

  8. Android:异步加载图片

    我们知道Android为了不阻塞UI线程(main线程),不允许在非UI线程中进行UI操作以及网络请求等操作,为了不阻塞UI,我们往往就要进行异步加载. 我们以异步加载图片为例子,来学习一下异步加载 ...

  9. Android异步加载

    为什么要异步加载: 为了用户体验,避免卡顿 Android系统要求使用异步加载,耗时操作会阻塞UI线程 异步加载的常用的方式: 多线程/线程池 AsyncTask 以下以加载网络图片为示例 在主类之外 ...

最新文章

  1. 西游记里河水让人怀孕的秘密:是寄生虫!我往河里放了寄生虫!
  2. AI公开课:19.02.20 雷鸣教授《人工智能革命与机遇》课堂笔记以及个人感悟
  3. .NET连接ORACLE数据库的方法
  4. 画活动图教程_二次元人物头发怎么画?画好头发有什么技巧?
  5. 一顿家庭火锅让本不富裕的家庭雪上加霜......
  6. 远程服务器 上传公钥,SecureCRT+Ubuntu SSH服务器的远程公钥登陆
  7. beego 例子_beego框架代码分析
  8. HBase环境搭建60010端口无法访问问题解决方案
  9. java输出5行星型三角_JAVA图形小动画之简单行星运动
  10. 大数据在各行业中的应用表现
  11. matlab程序中中零内插在qdpsk调制中的作用,桂林电子科技大学通信原理思考题
  12. mac安装jdk1.8
  13. 魔兽世界模型浏览器WowModelExplorer演示
  14. Pattern Recognition and Machine Learning(模式识别与机器学习)第一章导读
  15. Java Attach机制
  16. PT展揭晓“2021年中国5G实力榜”,亿美软通入围“5G消息企业十强”
  17. woocommerce 分类到菜单_WooCommerce实用代码集合
  18. BUUCTF-MISC-九连环
  19. Python实现GWO智能灰狼优化算法优化支持向量机回归模型(svr算法)项目实战
  20. python(matplotlib)绘制直方图及阶梯图

热门文章

  1. python字符串相加_谁说python字符串相加效率低
  2. Vue父子组件生命周期触发顺序
  3. java 计时器归零_java - 如何每15秒运行一次计时器并取消并重新启动
  4. c语言 北京时间转换utc时间_PHP时间戳和日期相互转换操作
  5. python查找客户总金额_该程序查找需要支付给所有表现良好的Python的最低金额
  6. 怎么解决 数据丢失的问题_硬盘数据丢失怎么恢复
  7. 商务专业考计算机二级,计算机二级ms考什么
  8. 分式化简结果要求_初二数学期末复习,分式考点归纳,考点较多,解答要谨慎...
  9. idea控制台中文乱码问题
  10. 计算机基础与应用32页,《计算机基础与应用》2次作业及答案