官方的例子
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.actionbar;
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.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.IntentCompat;
import android.support.v4.content.Loader;
import android.support.v4.content.pm.ActivityInfoCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SearchViewCompat;
import android.support.v4.widget.SearchViewCompat.OnCloseListenerCompat;
import android.support.v4.widget.SearchViewCompat.OnQueryTextListenerCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.io.File;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Demonstration of the implementation of a custom Loader.
*/
public class LoaderCustomSupport extends FragmentActivity {
/**
* This class holds the per-item data in our Loader.
*/
public static class AppEntry {
private final File mApkFile;
private Drawable mIcon;
private final ApplicationInfo mInfo;
private String mLabel;
private final AppListLoader mLoader;
private boolean mMounted;
public AppEntry(AppListLoader loader, ApplicationInfo info) {
mLoader = loader;
mInfo = info;
mApkFile = new File(info.sourceDir);
}
public ApplicationInfo getApplicationInfo() {
return mInfo;
}
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);
}
public String getLabel() {
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;
}
}
}
@Override
public String toString() {
return mLabel;
}
}
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);
}
/**
* Populate new items in the list.
*/
@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;
}
public void setData(List<AppEntry> data) {
clear();
if (data != null) {
for (AppEntry appEntry : data) {
add(appEntry);
}
}
}
}
public static class AppListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<List<AppEntry>> {
// This is the Adapter being used to display the list's data.
AppListAdapter mAdapter;
@Override
public void onStart() {
printLog("onStart");
super.onStart();
}
public void onResume() {
super.onResume();
printLog("onResume");
};
public void onPause() {
printLog("onPause");
super.onPause();
};
public void onStop() {
printLog("onStop");
super.onStop();
};
// If non-null, this is the current filter the user has provided.
String mCurFilter;
OnQueryTextListenerCompat mOnQueryTextListenerCompat;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
printLog("onCreateView");
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
printLog("onActivityCreated");
// Give some text to display if there is no data. In a real
// application this would come from a resource.
setEmptyText("No applications");
// We have a menu item to show in action bar.
setHasOptionsMenu(true);
// Create an empty adapter we will use to display the loaded data.
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().initLoader(0, null, this);
}
@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.
printLog("onCreateLoader");
return new AppListLoader(getActivity());
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Place an action bar item for searching.
MenuItem item = menu.add("Search");
item.setIcon(android.R.drawable.ic_menu_search);
MenuItemCompat
.setShowAsAction(
item,
MenuItemCompat.SHOW_AS_ACTION_IF_ROOM
| MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
final View searchView = SearchViewCompat
.newSearchView(getActivity());
if (searchView != null) {
SearchViewCompat.setOnQueryTextListener(searchView,
new OnQueryTextListenerCompat() {
@Override
public boolean onQueryTextChange(String newText) {
// Called when the action bar search text has
// changed. Since this
// is a simple array adapter, we can just have
// it do the filtering.
mCurFilter = !TextUtils.isEmpty(newText) ? newText
: null;
mAdapter.getFilter().filter(mCurFilter);
return true;
}
});
SearchViewCompat.setOnCloseListener(searchView,
new OnCloseListenerCompat() {
@Override
public boolean onClose() {
if (!TextUtils.isEmpty(SearchViewCompat
.getQuery(searchView))) {
SearchViewCompat.setQuery(searchView, null,
true);
}
return true;
}
});
MenuItemCompat.setActionView(item, searchView);
}
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Insert desired behavior here.
Log.i("LoaderCustom", "Item clicked: " + id);
}
@Override
public void onLoaderReset(Loader<List<AppEntry>> loader) {
printLog("onLoaderReset");
mAdapter.setData(null);
}
@Override
public void onLoadFinished(Loader<List<AppEntry>> loader,
List<AppEntry> data) {
// Set the new data in the adapter.
mAdapter.setData(data);
printLog("onLoadFinished");
// The list should now be shown.
if (isResumed()) {
setListShown(true);
} else {
setListShownNoAnimation(true);
}
}
}
/**
* A custom Loader that loads all of the installed applications.
*/
public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {
List<AppEntry> mApps;
final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
PackageIntentReceiver mPackageObserver;
final PackageManager mPm;
public AppListLoader(Context context) {
super(context);
printLog("AppListLoader");
// 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();
}
/**
* 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) {
printLog("deliverResult");
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);
}
}
/**
* This is where the bulk of our work is done. This function is called
* in a background thread and should generate a new set of data to be
* published by the loader.
*/
@Override
public List<AppEntry> loadInBackground() {
printLog("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;
}
/**
* Handles a request to cancel a load.
*/
@Override
public void onCanceled(List<AppEntry> apps) {
super.onCanceled(apps);
printLog("onCanceled");
// At this point we can release the resources associated with 'apps'
// if needed.
onReleaseResources(apps);
}
/**
* 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.
printLog("onReleaseResources");
}
/**
* Handles a request to completely reset the Loader.
*/
@Override
protected void onReset() {
super.onReset();
printLog("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;
}
}
/**
* 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);
}
printLog("onStartLoading");
// 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();
printLog("onStopLoading");
}
}
/**
* 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
| ActivityInfoCompat.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(IntentCompat.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(IntentCompat.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();
}
}
/**
* 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());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
// 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();
}
}
public static void printLog(String string) {
Log.d("Loader", string);
}
}
輸出結果
02-20 17:13:03.718: D/Loader(10625): onCreateView
02-20 17:13:03.843: D/Loader(10625): onActivityCreated
02-20 17:13:03.867: D/Loader(10625): onCreateLoader
02-20 17:13:03.867: D/Loader(10625): AppListLoader
02-20 17:13:03.875: D/Loader(10625): onStart
02-20 17:13:03.875: D/Loader(10625): onStartLoading
02-20 17:13:03.882: D/Loader(10625): onResume
02-20 17:13:03.890: D/Loader(10625): loadInBackground
02-20 17:13:05.148: D/Loader(10625): deliverResult
02-20 17:13:05.148: D/Loader(10625): onLoadFinished
02-20 17:13:05.156: D/Loader(10625): onReleaseResources
//切換離去
02-20 17:13:10.867: D/Loader(10625): onPause
02-20 17:13:10.867: D/Loader(10625): onStop
02-20 17:13:10.867: D/Loader(10625): onStopLoading
//切換回來
02-20 17:13:14.531: D/Loader(10625): onCreateView
02-20 17:13:14.539: D/Loader(10625): onActivityCreated
02-20 17:13:14.546: D/Loader(10625): onStart
02-20 17:13:14.546: D/Loader(10625): deliverResult
02-20 17:13:14.546: D/Loader(10625): onReleaseResources
02-20 17:13:14.546: D/Loader(10625): onStartLoading
02-20 17:13:14.570: D/Loader(10625): onLoadFinished
02-20 17:13:14.570: D/Loader(10625): onResume
//切換離去
02-20 17:13:49.445: D/Loader(10625): onPause
02-20 17:13:49.445: D/Loader(10625): onStop
02-20 17:13:49.445: D/Loader(10625): onStopLoading
//切換回來
02-20 17:13:50.695: D/Loader(10625): onCreateView
02-20 17:13:50.703: D/Loader(10625): onActivityCreated
02-20 17:13:50.718: D/Loader(10625): onStart
02-20 17:13:50.718: D/Loader(10625): deliverResult
02-20 17:13:50.718: D/Loader(10625): onReleaseResources
02-20 17:13:50.718: D/Loader(10625): onStartLoading
02-20 17:13:50.750: D/Loader(10625): onLoadFinished
02-20 17:13:50.757: D/Loader(10625): onResume
總結:
每次切換回來都 用呼叫 onStartLoading 切換去都會呼叫onSopLoading