reference to :
最近项目中涉及到加载本地的地名.db文件,数据量大,自然不能直接放在UI线程中操作,好在Google在Android3.0以后,提供了AsyncTaskLoader来做一些耗时的异步任务。
一 官方对AsyncTaskLoader的定义及特点介绍如下:
Abstract Loader that provides an AsyncTask to do the work
Introduced in 3.0, loaders make it easy to asynchronously load data in an activity or fragment. Loaders have these characteristics:
1、They are available to every Activity and Fragment.
//支持Activity和Fragment 2、They provide asynchronous loading of data. //异步下载 (就是不影响UI线程) 3、They monitor the source of their data and deliver new results when the content changes. //当数据源改变时能及时通知客户端 4、They automatically reconnect to the last loader’s cursor when being recreated after a configuration change. Thus, they don’t need to re-query their data. //发生configuration change时自动重连接二 实际项目介绍
下面引用官方的一个展示当前设备所有已安装应用程序的DEMO,来对AsyncTaskLoader的用法做一个详细的介绍:
项目结构如图:第一步:我们需要写一个对应于每一个应用程序的实体类,该实体类包含应用程序图标和标签两个属性。
AppEntry.java:/** * Created by Administrator on 2016/5/25. */public class AppEntry { private String mLabel;//应用文字标签 private Drawable mIcon;//应用图标 private final AppListLoader mLoader; private final ApplicationInfo mInfo;//节点信息,只有一个 //PackageInfo、ApplicationInfo、ActivityInfo、ResolveInfo四种信息类的一种 private final File mApkFile; private boolean mMounted; public AppEntry(AppListLoader mLoader,ApplicationInfo mInfo ) { this.mInfo = mInfo; this.mLoader = mLoader; mApkFile=new File(mInfo.sourceDir);//sourceDir=Full path to the location of this package } public ApplicationInfo getApplicationInfo() { return mInfo; } public String getLabel() { return mLabel; } public Drawable getIcon() { if (mIcon == null) { if (mApkFile.exists()) { mIcon = mInfo.loadIcon(mLoader.mPm); //public Drawable loadIcon (PackageManager pm){}获取应用图标 return mIcon; } else { mMounted = false; } } else if (!mMounted) { // If the app wasn't mounted but is now mounted, reload its icon. if (mApkFile.exists()) { mMounted = true; mIcon = mInfo.loadIcon(mLoader.mPm); return mIcon; } } else { return mIcon; } return mLoader.getContext().getResources() .getDrawable(android.R.drawable.sym_def_app_icon);//否则返回默认的小机器人 } @Override public String toString() { return mLabel; } void loadLabel(Context context) { if (mLabel == null || !mMounted) { if (!mApkFile.exists()) { mMounted = false; mLabel = mInfo.packageName;//获取程序名称 } else { mMounted = true; CharSequence label = mInfo.loadLabel(context.getPackageManager()); mLabel = label != null ? label.toString() : mInfo.packageName; } } }}
第二步:需要写一个自己的AppListLoader ,继承自AsyncTaskLoader,并实现其相关抽象方法。
(1)onStartLoading:注册一些监听器到loader上,并且执行一次forceLoad(); 否则loader不会开始工作
(2)loadInBackground:不用说,在这里就是加载数据并且返回,其实这个数据就返回到了LoaderManager的onLoadFinished方法第二个参数 (3)onStopLoading:停止加载数据,但不要停止监听也不要释放数据,就可以随时重启loader (4)onReset:先确保已经停止加载数据了,然后释放掉监听器并设为null (5)onCanceled: 在这里可以释放资源,如果是list就不需要做什么了,但是象cursor或者打开了什么文件就应该关闭一下;AppListLoader .java:
public class AppListLoader extends AsyncTaskLoader
> { private static final String TAG = "ADP_AppListLoader"; private static final boolean DEBUG = true; final PackageManager mPm;//包管理器 private List mApps;//装在应用程序实体的容器 // An observer to notify the Loader when new apps are installed/updated. private InstalledAppsObserver mAppsObserver;//非系统应用程序安装或者卸载的广播接收器 // The observer to notify the Loader when the system Locale has been changed. private SystemLocaleObserver mLocaleObserver;//系统应用程序安装或者卸载的广播接收器 public AppListLoader(Context context) { super(context); mPm = getContext().getPackageManager(); Log.i("TAG","AppListLoader(Context)"); } @Override protected void onStartLoading() { Log.i("TAG","onStartLoading()"); if(mApps!=null){ deliverResult(mApps); } // Register the observers that will notify the Loader when changes are made. if (mAppsObserver == null) { mAppsObserver = new InstalledAppsObserver(this);//注册一个非系统应用程序的接收器 } if (mLocaleObserver == null) { mLocaleObserver = new SystemLocaleObserver(this);//注册一个系统应用程序的接收器 } if (takeContentChanged()) { forceLoad(); } else if (mApps == null) { forceLoad();//强制加载数据 } } @Override public void forceLoad() { Log.i("TAG","forceLoad()"); super.forceLoad(); } @Override public List loadInBackground() { Log.i("TAG","loadInBackground()"); List apps=mPm.getInstalledApplications(0);// public static final int FILTER_ALL_APP = 0; // 所有应用程序 // public static final int FILTER_SYSTEM_APP = 1; // 系统程序 // public static final int FILTER_THIRD_APP = 2; // 第三方应用程序 // public static final int FILTER_SDCARD_APP = 3; // 安装在SDCard的应用程序 if(apps==null){ apps=new ArrayList<>(); } List entries=new ArrayList<>(apps.size()); //开始加载数据 for(int i=0;i datas) { //分发loadInBackground()方法返回的结果 Log.i("TAG","deliverResult()"); if(isReset()){ if(datas!=null){ releaseResources(datas);//可以释放相关资源 return; } } List oldApps=mApps; mApps=datas; if(isStarted()){ super.deliverResult(datas); } if(oldApps!=null&&oldApps!=datas){ releaseResources(oldApps); } } @Override protected void onStopLoading() { //停止加载数据 Log.i("TAG","onStopLoading()"); cancelLoad(); } @Override protected void onReset() { Log.i("TAG","onReset()"); onStopLoading(); // At this point we can release the resources associated with 'apps'. if (mApps != null) { releaseResources(mApps); mApps = null; } // The Loader is being reset, so we should stop monitoring for changes. if (mAppsObserver != null) { getContext().unregisterReceiver(mAppsObserver);//注销广播接收器 mAppsObserver = null; } if (mLocaleObserver != null) { getContext().unregisterReceiver(mLocaleObserver);//注销广播接收器 mLocaleObserver = null; } } @Override public void onCanceled(List apps) { // Attempt to cancel the current asynchronous load. super.onCanceled(apps); Log.i("TAG","onCanceled()"); releaseResources(apps); } /** * Helper method to take care of releasing resources associated with an * actively loaded data set. */ private void releaseResources(List apps) { // For a simple List, there is nothing to do. For something like a Cursor, // we would close it in this method. All resources associated with the // Loader should be released here. } /** * Performs alphabetical comparison of { @link AppEntry} objects. This is * used to sort queried data in { @link }. */ private static final Comparator ALPHA_COMPARATOR = new Comparator () { Collator sCollator = Collator.getInstance(); @Override public int compare(AppEntry object1, AppEntry object2) { return sCollator.compare(object1.getLabel(), object2.getLabel()); } };}
第三步:在MainActivity中调用AsyncTaskLoader,并继承LoaderManager.LoaderCallbacks的接口,重写接口方法:
(1)onCreateLoader: 这个是创建一个AsyncTaskLoader并返回,我们在里面new一个自己写的AppListLoader并返回就OK了;
(2)onLoadFinished: 这个是加载完成后可以更新UI,在这里就是setAdapter了 而这个加载过程其实就是在CursorLoader里面完成的, 只不过系统帮我们完成了,而如果自定义loader的话就要自己完成,这就是区别; (3)onLoaderReset: loader的重置,在这里一般让UI不显示数据就行;MainActivity .java:
public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);//将AppListFragment添加到当前的activity里 FragmentManager fm=getSupportFragmentManager(); if(fm.findFragmentById(android.R.id.content)==null){ AppListFragment list=new AppListFragment(); fm.beginTransaction().add(android.R.id.content,list).commit(); } }//实现LoaderManager.LoaderCallbacks的接口 public static class AppListFragment extends ListFragment implements LoaderManager.LoaderCallbacks
>{ private static final String TAG = "ADP_AppListFragment"; private static final boolean DEBUG = true; private AppListAdapter mAdapter; private static final int LOADER_ID = 1; @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); mAdapter=new AppListAdapter(getActivity()); setEmptyText("No Applications"); setListAdapter(mAdapter); setListShown(false); if (getLoaderManager().getLoader(LOADER_ID) == null) { Log.i("TAG", "Initializing the new Loader..."); } else { Log.i("TAG", "Reconnecting with existing Loader (id '1')..."); } getLoaderManager().initLoader(LOADER_ID, null, this); } @Override public Loader
> onCreateLoader(int id, Bundle args) { Log.i("TAG", "onCreateLoader()"); return new AppListLoader(getActivity()); } @Override public void onLoadFinished(Loader
> loader, List data) { Log.i("TAG", "onLoadFinished()"); mAdapter.setData(data); if(isResumed()){ setListShown(true); }else { setListShownNoAnimation(true); } } @Override public void onLoaderReset(Loader
> loader) { Log.i("TAG", "onLoaderReset()"); mAdapter.setData(null); }}
然后运行程序如下:
打开应用,AppListLoader中核心方法执行的先后顺序:
05-25 12:57:46.050 11184-11184/com.troy.applistloader I/TAG: +++ Calling initLoader()! +++05-25 12:57:46.050 11184-11184/com.troy.applistloader I/TAG: +++ Initializing the new Loader... +++05-25 12:57:46.050 11184-11184/com.troy.applistloader I/TAG: onCreateLoader()05-25 12:57:46.050 11184-11184/com.troy.applistloader I/TAG: onStartLoading()05-25 12:57:46.060 11184-11184/com.troy.applistloader I/TAG: forceLoad()05-25 12:57:46.060 11184-13196/com.troy.applistloader I/TAG: loadInBackground()05-25 12:57:47.530 11184-11184/com.troy.applistloader I/TAG: deliverResult()05-25 12:57:47.530 11184-11184/com.troy.applistloader I/TAG: onLoadFinished()
返回键,会执行的方法及执行顺序:
05-25 13:00:08.790 11184-11184/com.troy.applistloader I/TAG: onStopLoading()05-25 13:00:08.790 11184-11184/com.troy.applistloader I/TAG: onLoaderReset()05-25 13:00:08.790 11184-11184/com.troy.applistloader I/TAG: onReset()05-25 13:00:08.790 11184-11184/com.troy.applistloader I/TAG: onStopLoading()
三 总结
本项目的学习之后,我们应该掌握以下几点:
(1)理解AsyncTaskLoader的每一个核心方法的作用及调用时机,以及如何自定义一个AsyncTaskLoader。 (2)如何在Fragement中启动AsyncTaskLoader,继承LoaderManager.LoaderCallbacks,实现接口的三个方法。 (3)应该了解AsyncTaskLoader的底层实际上是执行的AsyncTask,这个可以看看源码。 (4)如何应用ApplicationInfo,获取相关的程序信息。