Android源码里面一个自定义的AppListLoader,来示例如何使用AsyncTaskLoader及LoaderManager,这里做了一部分改动,方便梳理代码!
加载已经安装的应用列表
LoaderActivity.java
package com.example.loadercustom;
import java.io.File;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.example.loadercustom.AppListFragment.AppListLoader;
import android.app.Activity;
import android.app.FragmentManager;
import android.content.AsyncTaskLoader;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
/**
* Demonstration of the implementation of a custom Loader.
*/
public class LoaderActivity extends Activity {
private static final String TAG = "yxf";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
// Create the list fragment and add it as our sole content.
if (fm.findFragmentById(android.R.id.content) == null) {
AppListFragment list = new AppListFragment();
fm.beginTransaction().add(android.R.id.content, list).commit();
}
}
/**
* This class holds the per-item data in our Loader.
*/
public static class AppEntry {
private final AppListLoader mLoader;
private final ApplicationInfo mInfo;
private final File mApkFile;
private String mLabel;
private Drawable mIcon;
private boolean mMounted;
public AppEntry(AppListLoader loader, ApplicationInfo info) {
mLoader = loader;
mInfo = info;
mApkFile = new File(info.sourceDir);
}
public ApplicationInfo getApplicationInfo() {
return mInfo;
}
public String getLabel() {
return mLabel;
}
public Drawable getIcon() {
if (mIcon == null) {
if (mApkFile.exists()) {
mIcon = mInfo.loadIcon(mLoader.mPm);
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;
}
}
}
}
/**
* Helper for determining if the configuration has changed in an interesting
* way so we need to rebuild the app list.
*/
public static class InterestingConfigChanges {
final Configuration mLastConfiguration = new Configuration();
int mLastDensity;
boolean applyNewConfig(Resources res) {
int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
|ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
mLastDensity = res.getDisplayMetrics().densityDpi;
return true;
}
return false;
}
}
/**
* 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;
public 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);
mLoader.getContext().registerReceiver(this, sdFilter);
}
@Override
public void onReceive(Context context, Intent intent) {
// Tell the loader about the change.
mLoader.onContentChanged();
}
}
public static class AppListAdapter extends ArrayAdapter<AppEntry> {
private final LayoutInflater mInflater;
public AppListAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public void setData(List<AppEntry> data) {
clear();
if (data != null) {
addAll(data);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) {
view = mInflater.inflate(R.layout.list_item_icon_text, parent, false);
} else {
view = convertView;
}
AppEntry item = getItem(position);
((ImageView)view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());
((TextView)view.findViewById(R.id.text)).setText(item.getLabel());
return view;
}
}
}
AppListFragment.java
package com.example.loadercustom;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.example.loadercustom.LoaderActivity.AppEntry;
import com.example.loadercustom.LoaderActivity.AppListAdapter;
import com.example.loadercustom.LoaderActivity.InterestingConfigChanges;
import com.example.loadercustom.LoaderActivity.PackageIntentReceiver;
import android.app.ListFragment;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Loader;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
public class AppListFragment extends ListFragment{
private static final String TAG = "LoaderManager-yxf";
AppListAdapter mAdapter;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No applications");
setHasOptionsMenu(true);
mAdapter = new AppListAdapter(getActivity());
setListAdapter(mAdapter);
// Start out with a progress indicator.
setListShown(false);
// Prepare the loader. Either re-connect with an existing one, or start
// a new one.
//getLoaderManager().enableDebugLogging(true);//for debugging
getLoaderManager().initLoader(0, null, mCallbacks);
}
private LoaderCallbacks<List<AppEntry>> mCallbacks = new LoaderCallbacks<List<AppEntry>>() {
@Override
public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader with no arguments, so it is simple.
Log.d(TAG, "LoaderCallBack, onCreateLoader");
return new AppListLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<List<AppEntry>> loader,
List<AppEntry> data) {
// Set the new data in the adapter.
mAdapter.setData(data);
/**
* isResume() methos Return true if the fragment is in the resumed
* state. This is true for the duration of {@link #onResume()} and
* {@link #onPause()} as well.
*/
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<List<AppEntry>> loader) {
// Clear the data in the adapter.
mAdapter.setData(null);
}
};
/**
* Perform alphabetical comparison of application entry objects.
*/
public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<AppEntry>() {
private final Collator sCollator = Collator.getInstance();
@Override
public int compare(AppEntry object1, AppEntry object2) {
return sCollator.compare(object1.getLabel(), object2.getLabel());
}
};
/**
* 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);
mPm = getContext().getPackageManager();
}
@Override
protected void onStartLoading() {
if (mApps != null) {
deliverResult(mApps);
}
if (mPackageObserver == null) {
mPackageObserver = new PackageIntentReceiver(this);
}
boolean configChange = mLastConfig.applyNewConfig(getContext()
.getResources());
if (takeContentChanged() || mApps == null || configChange) {
forceLoad();
}
}
@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);
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()) {
super.deliverResult(apps);
}
if (oldApps != null) {
onReleaseResources(oldApps);
}
}
@Override
protected void onStopLoading() {
Log.e(TAG,"AppListFragment, onStopLoading(), " + new Exception());
cancelLoad();
}
@Override
public void onCanceled(List<AppEntry> apps) {
super.onCanceled(apps);
onReleaseResources(apps);
}
@Override
protected void onReset() {
super.onReset();
// Ensure the loader is stopped
onStopLoading();
if (mApps != null) {
onReleaseResources(mApps);
mApps = null;
}
if (mPackageObserver != null) {
getContext().unregisterReceiver(mPackageObserver);
mPackageObserver = null;
}
}
protected void onReleaseResources(List<AppEntry> apps) {
Log.d(TAG,"AppListLoader,onReleaseResources");
// For a simple List<> there is nothing to do. For something
// like a Cursor, we would close it here.
}
}
}
res/layout/list_item_icon_text.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:id="@+id/icon"
android:layout_width="48dip"
android:layout_height="48dip" />
<TextView android:id="@+id/text"
android:layout_gravity="center_vertical"
android:layout_width="0dip"
android:layout_weight="1.0"
android:layout_height="wrap_content" />
</LinearLayout>