LoaderManager及Loader初步探索

转载  http://blog.sina.com.cn/s/blog_62c5894901014g5x.html

Android3.0中,Google引入了一种数据异步加载机制,该机制的核心,便是LoaderManagerLoader,顾名思义,LoaderManagerLoader的管理者,而Loader便是数据加载器,你可以根据自己的需要实现形形色色的数据加载器。

Google强烈建议在加载数据时,使用LoaderManager及其相关的机制。

每个ActivityFragment中,都会有且只有一个LoaderManager,而LoaderManager中可以有多个Loader,也就是说,在一个Activity或者Fragment中,你可以同时异步加载N则不同的数据,具体加多少则,要看你那一亩三分地(ActivityFragment就是你的地)有多大产。

Google倒是提供了一个标准的Loader,即CursorLoader,它是Loader的标准实现,如果你的数据能够用Cursor表示,比如来自SQLiteDatabase的数据就是标准的Cursor,那么这个类对你而言就够用了,具体如何使用CursorLoader,请参看如下两个例子:

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderThrottle.html

这个例子中牵涉的东西较多,除了我们关注的CursorLoader外,还包括ContentProviderSQLiteOperHelperSQLiteDatabaseFragmentUri等等常用概念,因此,在仔细阅读了本例后,你将学会如何让这这些类在你的应用中分工协作。

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/LoaderCursor.html

这个例子要简单些,它教你如何获取联系人,虽然简单,却切中要害。

本文若是只关注这些,就没有意思了,毕竟在很多情况下,我们会感觉CursorLoader不顺眼,于是想写一个属于自己的、更帅的Loader,此时抽象类AsyncTaskLoader就会叫嚣着粉墨登场了,该抽象类定义了你的加载器异步加载数据需要实现的接口,那么如何实现呢?你可以去看下面的例子:

http://developer.android.com/reference/android/content/AsyncTaskLoader.html#loadInBackground()

为了让你以及我自己更好的学习自定义Loader这一伎俩,我把里头的关键代码摘了出来,咱们共同分析一番:

 

public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {

    final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();

    final PackageManager mPm;

 

    List<AppEntry> mApps;

    PackageIntentReceiver mPackageObserver;

 

    public AppListLoader(Context context) {

        super(context);

 

        // Retrieve the package manager for later use; note we don't

        // use 'context' directly but instead the save global application

        // context returned by getContext().

        mPm = getContext().getPackageManager();

    }

 

   

    @Override public List<AppEntry> loadInBackground() {

        // Retrieve all known applications.

        List<ApplicationInfo> apps = mPm.getInstalledApplications(

                PackageManager.GET_UNINSTALLED_PACKAGES |

                PackageManager.GET_DISABLED_COMPONENTS);

        if (apps == null) {

            apps = new ArrayList<ApplicationInfo>();

        }

 

        final Context context = getContext();

 

        // Create corresponding array of entries and load their labels.

        List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());

        for (int i=0; i<apps.size(); i++) {

            AppEntry entry = new AppEntry(this, apps.get(i));

            entry.loadLabel(context);

            entries.add(entry);

        }

 

        // Sort the list.

        Collections.sort(entries, ALPHA_COMPARATOR);

 

        // Done!

        return entries;

    }

 

   

    @Override public void deliverResult(List<AppEntry> apps) {

        if (isReset()) {

            // An async query came in while the loader is stopped.  We

            // don't need the result.

            if (apps != null) {

                onReleaseResources(apps);

            }

        }

        List<AppEntry> oldApps = apps;

        mApps = apps;

 

        if (isStarted()) {

            // If the Loader is currently started, we can immediately

            // deliver its results.

            super.deliverResult(apps);

        }

 

        // At this point we can release the resources associated with

        // 'oldApps' if needed; now that the new result is delivered we

        // know that it is no longer in use.

        if (oldApps != null) {

            onReleaseResources(oldApps);

        }

    }

 

   

    @Override protected void onStartLoading() {

        if (mApps != null) {

            // If we currently have a result available, deliver it

            // immediately.

            deliverResult(mApps);

        }

 

        // Start watching for changes in the app data.

        if (mPackageObserver == null) {

            mPackageObserver = new PackageIntentReceiver(this);

        }

 

        // Has something interesting in the configuration changed since we

        // last built the app list?

        boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());

 

        if (takeContentChanged() || mApps == null || configChange) {

            // If the data has changed since the last time it was loaded

            // or is not currently available, start a load.

            forceLoad();

        }

    }

 

   

    @Override protected void onStopLoading() {

        // Attempt to cancel the current load task if possible.

        cancelLoad();

    }

 

   

    @Override public void onCanceled(List<AppEntry> apps) {

        super.onCanceled(apps);

 

        // At this point we can release the resources associated with 'apps'

        // if needed.

        onReleaseResources(apps);

    }

 

   

    @Override protected void onReset() {

        super.onReset();

 

        // Ensure the loader is stopped

        onStopLoading();

 

        // At this point we can release the resources associated with 'apps'

        // if needed.

        if (mApps != null) {

            onReleaseResources(mApps);

            mApps = null;

        }

 

        // Stop monitoring for changes.

        if (mPackageObserver != null) {

            getContext().unregisterReceiver(mPackageObserver);

            mPackageObserver = null;

        }

    }

 

   

    protected void onReleaseResources(List<AppEntry> apps) {

        // For a simple List<> there is nothing to do.  For something

        // like a Cursor, we would close it here.

    }

}

 

诸位请看loadInBackground方法,这是Loader的核心方法,必须得重载,你要在这里头实现加载数据的功能,看看名字就知道,该方法将在后台运行,这方法没有什么特别说的,自己想加什么样的数据,自己清楚。  

再看deliverResult方法,当数据到达客户端后,这个方法将被调用,该方法可以不重载,你可以在其中根据需要实现传递数据的逻辑,请注意例子中对两个状态的判断(注:编程,就得养成if的好习惯),一个是isReset()这个方法用来判断Loader是否已经被重置,如果重置了,那么留着资源也没有啥用了,得把它释放掉;一个是isStarted(),如果Loader被启动那么就把数据传递出去:直接调super的传递方法就OK

onStartLoading方法,必须得重载,且别忘记在里头调用forceLoad方法,你也看到,在例子中人家调用了deliverResult方法,并且创建了一个观察者来接收数据,在调用forceLoad时,请注意相应的条件判断,关于为什么要调用forceLoad方法,网上有下面一段话:

onStartLoading() is called in two places:

1. LoaderManagerImpl.doStart()->LoaderInfo.start(), which will happen when the hosting fragment isstarted.

2. LoaderManagerImpl.installLoader(LoaderInfo)->if the hosting fragment is started, callLoaderInfo.start().

 

So basically, your custom class extending AsyncTaskLoader will have to override onStartLoading(),and call forceLoad() within when necessary.

 

Loader::startLoading() will be called when the hosting fragment is started, and youronStartLoading() will be called as consequence.

    以我目前的水平,并不能确切明白这段话的含义是什么,但是,我翻了一下Android SDK的源代码,发现LoaderAsyncTaskLoader中,startLoading方法其实屁都没做,所以,万事求己不如求人,还是老老实实去实现onStartLoading方法吧,不过Google这么做有些坑爹的嫌疑,所以很多人在那里问,哎呀,我的loadInBackground怎么老不执行啊?

    onStopLoadingonCancel方法都没有什么好说的,倒是onReset方法的实现值得关注,诸位看看便知,至于onReleaseResouces方法,就是给你一次释放资源的机会,如果你用的是Cursor之类的东西,请在这里头close吧。

    我刚刚开始研究Android SDK,也许是从.NET猛地转到JAVA的缘故,很多架构 方面的东西都不是很习惯。Google Android SDK的架构简单、灵活而又强大,这是值得称道的,但是也有其不甚完善的地方。就拿LoaderManager来说,知道Android 3.0才引入,Google开发文档说,使用这东西,能够避免在加载数据时界面死在那里。于是,我就相当然地认为,Google不是神,它的产品设计中,也会存在哪些低级的毗漏与缺陷。

但是,无论如何Google Android SDK以及iOS SDK,都非常值得我们去研究一番,即便你不编程,不写代码,但是里头的思想,确是一笔财富。

你可能感兴趣的:(LoaderManager及Loader初步探索)