您的位置:首页 > 移动开发 > Android开发

[Android Pro] Android异步任务处理之AsyncTaskLoader的使用

2016-12-20 21:22 507 查看
reference to : http://blog.csdn.net/happy_horse/article/details/51518280

最近项目中涉及到加载本地的地名.db文件,数据量大,自然不能直接放在UI线程中操作,好在Google在Android3.0以后,提供了AsyncTaskLoader来做一些耗时的异步任务。

一 官方对AsyncTaskLoader的定义及特点介绍如下:

Abstract Loader that provides an AsyncTask to do the work


[/code]
Introduced in Android 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;//<application>节点信息,只有一个
//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<List<AppEntry>> {
private static final String TAG = "ADP_AppListLoader";
private static final boolean DEBUG = true;
final PackageManager mPm;//包管理器
private List<AppEntry> 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<AppEntry> loadInBackground() {
Log.i("TAG","loadInBackground()");
List<ApplicationInfo> 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<AppEntry> entries=new ArrayList<>(apps.size());
//开始加载数据
for(int i=0;i<apps.size();i++){
AppEntry appEntry=new AppEntry(this,apps.get(i));
appEntry.loadLabel(getContext());
entries.add(appEntry);
}
//Sort the list
Collections.sort(entries,ALPHA_COMPARATOR);//对应用程序进行排序
return entries;
}

@Override
public void deliverResult(List<AppEntry> datas) {//分发loadInBackground()方法返回的结果
Log.i("TAG","deliverResult()");
if(isReset()){
if(datas!=null){
releaseResources(datas);//可以释放相关资源
return;
}
}
List<AppEntry> 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<AppEntry> 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<AppEntry> 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<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
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<List<AppEntry>>{
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<List<AppEntry>> onCreateLoader(int id, Bundle args) {
Log.i("TAG", "onCreateLoader()");
return new AppListLoader(getActivity());
}

@Override
public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {
Log.i("TAG", "onLoadFinished()");
mAdapter.setData(data);
if(isResumed()){
setListShown(true);
}else {
setListShownNoAnimation(true);
}
}

@Override
public void onLoaderReset(Loader<List<AppEntry>> 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,获取相关的程序信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: