Loader的特點和使用場所
- Loader 可以在Activity 和Fragments 的界面下運行(本篇將結合上篇文章的Fragments的DEMO進行講述如何在Fragments 下使用Loaders)。
- 它可以提供類似於AysncTask一樣的異步請求數據加載的功能,實際上它就是來源於AysncTask 的擴展並增加了很多人性化的功能,例如加載進度框、更好的控制API等。
- 它擁有一個類似於Windows Phone 開發的MVVM一樣的數據改變通知機制,當數據源做出改變時會及時通知。
- 當Cursor 發生變化時,會自動加載數據,因此並不需要再重新進行數據查詢。
Loader API 使用參考
- LoaderManager
每一個Activity 和 Fragments 只能存在一個LoaderManager,負責管理界面中的Loader。 - LoaderManager.LoaderCallbacks
位於LoaderManager的子接口,提供眾多的回調接口操作Loader ,如初始化、創建、重新啟動、銷毀、結束等一系列實用接口。 - Loader
與Fragments相似,是一個基礎類,可以由用戶自行擴展適合自己的類 - AsyncTaskLoader
提供了一個基於AsyncTask 工作機制的Loader,查看源碼可以看到其繼承於Loader,並且子類LoadTask 繼承於AsyncTask<Void, Void, D>,並且實現了Runable 接口,功能十分強大。本DEMO就是基於AsyncTaskLoader篇寫。 - CursorLoader
提供了一個基於AsyncTaskLoader工作機制的Loader,查看源碼可以看到其繼承於AsyncTaskLoader,一般繼承OnQueryTextListener,可用於查詢、更改數據源作用。
Loader 詳細使用DEMO
基於上篇的Fragments 代碼,添加Loaders功能,實現加載應用程序數據到界面上,DEMO的運行效果如下:
核心代碼為
應用程序的數據對象類:
public
static
class AppEntry {
private final AppListLoader mLoader;
private final ApplicationInfo mInfo;
private final File mApkFile;
private String mLable;
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 getLable() {
return mLable;
}
public Drawable getIcon() {
if (mIcon == null) {
if (mApkFile.exists()) {
mIcon = mInfo.loadIcon(mLoader.mPm);
return mIcon;
} else {
mMounted = false;
}
} else if (!mMounted) {
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() {
// TODO Auto-generated method stub
return mLable.toString();
}
void loadLable(Context mContext) {
if (mLable == null || !mMounted) {
if (!mApkFile.exists()) {
mMounted = false;
mLable = mInfo.packageName;
} else {
mMounted = true;
CharSequence lable = mInfo.loadLabel(mContext
.getPackageManager());
mLable = lable != null ? lable.toString()
: mInfo.packageName;
}
}
}
private final AppListLoader mLoader;
private final ApplicationInfo mInfo;
private final File mApkFile;
private String mLable;
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 getLable() {
return mLable;
}
public Drawable getIcon() {
if (mIcon == null) {
if (mApkFile.exists()) {
mIcon = mInfo.loadIcon(mLoader.mPm);
return mIcon;
} else {
mMounted = false;
}
} else if (!mMounted) {
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() {
// TODO Auto-generated method stub
return mLable.toString();
}
void loadLable(Context mContext) {
if (mLable == null || !mMounted) {
if (!mApkFile.exists()) {
mMounted = false;
mLable = mInfo.packageName;
} else {
mMounted = true;
CharSequence lable = mInfo.loadLabel(mContext
.getPackageManager());
mLable = lable != null ? lable.toString()
: mInfo.packageName;
}
}
}
}
實現AsyncTaskLoader 加載數據
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);
// TODO Auto-generated constructor stub
mPm = getContext().getPackageManager();
}
@Override
public List<AppEntry> loadInBackground() {
// TODO Auto-generated method stub
List<ApplicationInfo> apps = mPm
.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES
| PackageManager.GET_DISABLED_COMPONENTS);
if (apps == null)
apps = new ArrayList<ApplicationInfo>();
final Context mContext = getContext();
List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
for (ApplicationInfo info : apps) {
AppEntry entry = new AppEntry( this, info);
entry.loadLable(mContext);
entries.add(entry);
}
Collections.sort(entries, ALPHA_COMPARATOR);
return entries;
}
@Override
public void deliverResult(
List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry> data) {
// TODO Auto-generated method stub
if (isReset()) {
if (data != null) {
// 釋放資源處理
}
}
List<AppEntry> oladApps=data;
mApps=data;
if(isStarted()){
super.deliverResult(data);
}
if(oladApps!= null){
// 釋放資源
}
}
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 void onCanceled(
List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry> data) {
// TODO Auto-generated method stub
super.onCanceled(data);
cancelLoad();
}
@Override
protected void onReset() {
// TODO Auto-generated method stub
super.onReset();
onStopLoading();
if(mApps!= null){
// 釋放資源
mApps= null;
}
if(mPackageObserver!= null){
getContext().unregisterReceiver(mPackageObserver);
mPackageObserver= null;
}
}
final InterestingConfigChanges mLastConfig = new InterestingConfigChanges();
final PackageManager mPm;
List<AppEntry> mApps;
packageIntentReceiver mPackageObserver;
public AppListLoader(Context context) {
super(context);
// TODO Auto-generated constructor stub
mPm = getContext().getPackageManager();
}
@Override
public List<AppEntry> loadInBackground() {
// TODO Auto-generated method stub
List<ApplicationInfo> apps = mPm
.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES
| PackageManager.GET_DISABLED_COMPONENTS);
if (apps == null)
apps = new ArrayList<ApplicationInfo>();
final Context mContext = getContext();
List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
for (ApplicationInfo info : apps) {
AppEntry entry = new AppEntry( this, info);
entry.loadLable(mContext);
entries.add(entry);
}
Collections.sort(entries, ALPHA_COMPARATOR);
return entries;
}
@Override
public void deliverResult(
List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry> data) {
// TODO Auto-generated method stub
if (isReset()) {
if (data != null) {
// 釋放資源處理
}
}
List<AppEntry> oladApps=data;
mApps=data;
if(isStarted()){
super.deliverResult(data);
}
if(oladApps!= null){
// 釋放資源
}
}
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 void onCanceled(
List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry> data) {
// TODO Auto-generated method stub
super.onCanceled(data);
cancelLoad();
}
@Override
protected void onReset() {
// TODO Auto-generated method stub
super.onReset();
onStopLoading();
if(mApps!= null){
// 釋放資源
mApps= null;
}
if(mPackageObserver!= null){
getContext().unregisterReceiver(mPackageObserver);
mPackageObserver= null;
}
}
}
實現數據源:
public
static
class AppListAdapter
extends ArrayAdapter<AppEntry>{
private LayoutInflater mInflater;
public AppListAdapter(Context context) {
// TODO Auto-generated constructor stub
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) {
// TODO Auto-generated method stub
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.getLable());
return view;
}
private LayoutInflater mInflater;
public AppListAdapter(Context context) {
// TODO Auto-generated constructor stub
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) {
// TODO Auto-generated method stub
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.getLable());
return view;
}
}
在Activity 或者Fragments 里面實現接口:
public static class DetailsFragment extends ListFragment implements OnQueryTextListener,LoaderCallbacks<List<AppEntry>>
實現接口里面的函數:
public
boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
return false;
}
public boolean onQueryTextSubmit(String query) {
// TODO Auto-generated method stub
return false;
}
public Loader<List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry>> onCreateLoader(
int id, Bundle args) {
// TODO Auto-generated method stub
return new AppListLoader(getActivity());
}
/**
* Load 完成后
*/
public void onLoadFinished(
Loader<List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry>> arg0,
List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry> arg1) {
// TODO Auto-generated method stub
mAdapter.setData(arg1);
if (isResumed()) {
setListShown( true);
} else {
setListShownNoAnimation( true);
}
}
/**
* Loader 重置時
*/
public void onLoaderReset(
Loader<List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry>> arg0) {
// TODO Auto-generated method stub
mAdapter.setData( null);
// TODO Auto-generated method stub
return false;
}
public boolean onQueryTextSubmit(String query) {
// TODO Auto-generated method stub
return false;
}
public Loader<List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry>> onCreateLoader(
int id, Bundle args) {
// TODO Auto-generated method stub
return new AppListLoader(getActivity());
}
/**
* Load 完成后
*/
public void onLoadFinished(
Loader<List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry>> arg0,
List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry> arg1) {
// TODO Auto-generated method stub
mAdapter.setData(arg1);
if (isResumed()) {
setListShown( true);
} else {
setListShownNoAnimation( true);
}
}
/**
* Loader 重置時
*/
public void onLoaderReset(
Loader<List<com.xuzhi.fragment.FragmentDemoActivity.AppEntry>> arg0) {
// TODO Auto-generated method stub
mAdapter.setData( null);
}
得到LoaderManager初始化Loader,啟動加載在Fragment 的onActivityCreated回調方法上添加,本DEMO的DetailsFragment類添加:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
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);
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
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);
}
總結
Loader的使用分為以下幾個步驟:
- 實現自己的異步Loader ,如本篇的AsyncTaskLoader或者CursorLoader或者自己繼承Loader,第二步需要用到這個Loader
- 創建數據源->public static class AppListAdapter extends ArrayAdapter<AppEntry>
- 在Activity 或者Fragments 上實現OnQueryTextListener,LoaderManager.LoaderCallbacks接口
- 在onCreateLoader里面返回Loader,在onLoadfinished里面做清除動作,在onQueryTextChange或者onQueryTextSubmit改變數據源,在onCreate里面讓Loader去執行請求數據getLoaderManager().initLoader(0, null, this);
源碼下載:/Files/TerryBlog/FragmentLoader.zip