这个demo演示了如何使用自定义加载器实现对数据的管理。
* 自定义loader的基本步骤:
* 1.继承AsyncTaskLoader
* 2.定义一个观察者来接收数据源改变的通知(可以是ContentObserver也可以是BroadcastReceiver)
* 3.实现抽象方法loadInBackground,在该方法中将开启一个工作线程实行异步加载数据的操作 我们需要在这个方法中准备需要加载的数据集
* 4.重写deliverResult方法,在该方法中将数据结果传递给客户端
* 5.重写onStartLoading(), 方法在开始加载时调用此方法,在该方法中需要将数据的结果立即发送给客户端 并完成对观察者的初始化,开始监听数据源的变化。
* 6.重写onStoppLoading()方法,当loader停止时调用此方法,当loader处于停止状态,他还会继续监听数据 源的变化,却不会向客户端发送结果
* 7.重写onReset()方法。在loader的重置状态,它即不再监听数据源的变化,也不向客户端发送结果。需要在此方法中 停止数据加载、释放数据源、清空数据集、释放观察者。
* 8.重写onCanceled方法,当试图取消异步加载loader时调用此方法,在此方法中应释放数据源。
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:padding="5dp">
<ImageView
android:id="@+id/icon"
android:layout_width="40dp"
android:layout_height="40dp"
/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
MainActivity
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
if (fm.findFragmentById(android.R.id.content) == null) {
AppListFragment listFragment = new AppListFragment();
fm.beginTransaction().add(android.R.id.content, listFragment)
.commit();
}
}
public static class AppListFragment extends ListFragment implements
OnQueryTextListener, OnCloseListener {
private String curFilter;
private AppListAdapter mAdapter;
private MySearchView serachView;
private LoaderCallbacks<List<AppEntry>> MyLoader=new LoaderCallbacks<List<AppEntry>>() {
@Override
public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
return new AppListLoader(getActivity());
}
@Override
public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {
mAdapter.setData(data);
if(isResumed()){
setListShown(true);
}else{
setListShownNoAnimation(true);
}
}
@Override
public void onLoaderReset(Loader<List<AppEntry>> loader) {
mAdapter.setData(null);
}
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setEmptyText("No applications!");
setHasOptionsMenu(true);
mAdapter = new AppListAdapter(getActivity());
setListAdapter(mAdapter);
setListShown(false);
getLoaderManager().initLoader(0, null, MyLoader);
}
public class MySearchView extends SearchView {
public MySearchView(Context context) {
super(context);
}
@Override
public void onActionViewCollapsed() {
super.onActionViewCollapsed();
setQuery("", false);
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
MenuItem item = menu.add("search");
item.setIcon(android.R.drawable.ic_menu_search);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM
| MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
serachView = new MySearchView(getActivity());
serachView.setOnQueryTextListener(this);
serachView.setOnCloseListener(this);
serachView.setIconifiedByDefault(true);
item.setActionView(serachView);
}
@Override
public boolean onQueryTextSubmit(String query) {
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
curFilter = TextUtils.isEmpty(newText) ? newText:null;
mAdapter.getFilter().filter(curFilter);
return true;
}
@Override
public boolean onClose() {
if(TextUtils.isEmpty(serachView.getQuery())){
serachView.setQuery(null, true);
}
return true;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Toast.makeText(getActivity(), "Item click:"+id, 0).show();
}
}
public static class AppListAdapter extends ArrayAdapter<AppEntry> {
private LayoutInflater mInflate;
public AppListAdapter(Context context) {
super(context, android.R.layout.simple_list_item_2);
mInflate=(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 v;
if(convertView==null){
v=mInflate.inflate(R.layout.activity_main, parent, false);
}else{
v=convertView;
}
ImageView icon=(ImageView) v.findViewById(R.id.icon);
TextView title=(TextView) v.findViewById(R.id.title);
AppEntry item=getItem(position);
icon.setImageDrawable(item.getIcon());
title.setText(item.getLabel());
return v;
}
}
public static class AppEntry {
private ApplicationInfo mInfo;
private AppListLoader mLoader;
private File mApkFile;
private boolean mMounted = false;
private Drawable mIcon;
private String mLabel;
public AppEntry(ApplicationInfo info, AppListLoader loader) {
super();
this.mInfo = info;
this.mLoader = loader;
mApkFile = new File(info.sourceDir);
}
// 获取activity的图标
public Drawable getIcon() {
if (mIcon == null || !mMounted) {
if (mApkFile.exists()) {
mMounted = true;
mIcon = mInfo.loadIcon(mLoader.pm);
return mIcon;
} else {
mMounted = false;
return mLoader.getContext().getResources()
.getDrawable(android.R.drawable.sym_def_app_icon);
}
} else {
return mIcon;
}
}
// 获取activity的label
public String getLabel() {
if (mLabel == null || !mMounted) {
if (!mApkFile.exists()) {
mMounted = false;
mLabel = mInfo.packageName;
} else {
mMounted = true;
String label = (String) mInfo.loadLabel(mLoader.pm);
mLabel = label != null ? label : mInfo.packageName;
}
}
return mLabel;
}
}
// 定义一个比较器,用于集合排序
public static final Comparator<AppEntry> ALPHA_COMPARATOR = new Comparator<MainActivity.AppEntry>() {
private final Collator collator = Collator.getInstance();
@Override
public int compare(AppEntry lhs, AppEntry rhs) {
return collator.compare(lhs.getLabel(), rhs.getLabel());
}
};
public static class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {
public final PackageManager pm;
private List<AppEntry> mApps;
private PackageIntentReceiver mReceiver;
private InterestingConfigChange lastConfig=new InterestingConfigChange();
public AppListLoader(Context context) {
super(context);
// 注意此处需要使用整个application的上下文
pm = getContext().getPackageManager();
}
@Override
public List<AppEntry> loadInBackground() {
// 获取到本机上所有应用信息的集合,包括已经删除但还存在安装目录的应用,包括已经被禁用的应用
List<ApplicationInfo> apps = pm
.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES
| PackageManager.GET_DISABLED_COMPONENTS);
if (apps == null) {
apps = new ArrayList<ApplicationInfo>();
}
List<AppEntry> mEntries = new ArrayList<MainActivity.AppEntry>(
apps.size());
for (int i = 0; i < apps.size(); i++) {
AppEntry entry = new AppEntry(apps.get(i), this);
mEntries.add(entry);
}
// 对集合进行排序
Collections.sort(mEntries, ALPHA_COMPARATOR);
return mEntries;
}
@Override
public void deliverResult(List<AppEntry> data) {
// 如果loader处于重置状态,则释放数据源
if (isReset()) {
if (data != null) {
onReleaseResources(data);
return;
}
}
// 记录原来的数据源
List<AppEntry> oldApps = mApps;
mApps = data;
// 如果loader已经启动,则将结果发送至客户端
if (isStarted()) {
super.deliverResult(data);
}
// 释放掉原来的数据源
if (oldApps != null) {
onReleaseResources(oldApps);
}
}
@Override
protected void onStartLoading() {
super.onStartLoading();
if (mApps != null) {
// 如果我们此时有一个可用的数据源,则立即将其发送至客户端
deliverResult(mApps);
}
// 开始监听数据源的变化
if (mReceiver == null) {
mReceiver = new PackageIntentReceiver(this);
}
// 判断一下从我们最后创建list后,系统配置是否有变化,如果有则开启加载器
boolean configChange=lastConfig.applyNewConfig(getContext().getResources());
if(takeContentChanged() || mApps==null || configChange){
//如果数据发生变化或者当前数据不可用,或者系统配置发生变化则开启加载器加载数据
forceLoad();
}
}
@Override
protected void onStopLoading() {
// 如果加载器已经停止则取消加载
cancelLoad();
}
@Override
protected void onReset() {
super.onReset();
//停止加载
onStopLoading();
if(mApps!=null){
onReleaseResources(mApps);
mApps=null;
}
if(mReceiver!=null){
getContext().unregisterReceiver(mReceiver);
mReceiver=null;
}
}
@Override
public void onCanceled(List<AppEntry> data) {
super.onCanceled(data);
onReleaseResources(data);
}
private void onReleaseResources(List<AppEntry> data) {
// 在本例中数据源只是一个list所以这里不需要做任何事,但如果数据源
// 是一个Cursor,则应该在这里进行释放。在这里应该释放掉一切与loader
// 相关联的资源
}
}
public static class PackageIntentReceiver extends BroadcastReceiver {
private AppListLoader mLoader;
public PackageIntentReceiver(AppListLoader loader) {
super();
this.mLoader = loader;
// 定义意图过滤器,当package添加、删除、修改时都会接收到通知
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
// 添加数据的类型为package类型
filter.addDataScheme("package");
mLoader.getContext().registerReceiver(this, filter);
// 定义与sd卡相关的意图过滤器
IntentFilter sdFileter = new IntentFilter(
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFileter
.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
mLoader.getContext().registerReceiver(this, sdFileter);
}
@Override
public void onReceive(Context context, Intent intent) {
// 通知loader数据改变
mLoader.onContentChanged();
}
}
// 定义类关注系统配置是否发生变化
public static class InterestingConfigChange {
final Configuration mLastConfiguration = new Configuration();
int mLastDensity;
public boolean applyNewConfig(Resources res) {
// 获取发生变化的系统配置
int configChanges = mLastConfiguration.updateFrom(res
.getConfiguration());
// 判断屏幕的dpi密度是否发生变化
boolean densityChange = mLastDensity != res.getDisplayMetrics().densityDpi;
if (densityChange
|| (configChanges&(ActivityInfo.CONFIG_LOCALE
| ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_UI_MODE)) != 0) {
mLastDensity=res.getDisplayMetrics().densityDpi;
return true;
}
return false;
}
}
}