Android Loaders介绍(异步加载数据利器,类似AsyncTask)

(一)

Google强烈建议在加载数据时,使用Loaders及其相关的机制。

它可以提供类似于AysncTask一样的异步请求数据加载的功能,实际上它就是来源于AysncTask的扩展并增加了很多人性化的功能,例如加载进度框、更好的控制API等。


(二)

特点:

1.适用于所有ActivityFragment

2.提供异步加载数据功能

3.监视数据源,当数据发生变化时可以传送数据给界面

(三)

核心类和接口:

Class/Interface

描述

LoaderManager

一个与ActivityFragment有关联的抽象类,用于管理一个或多个Loader实例。这有助于app管理长运行操作。使用它的最显著的例子是CursorLoader。每个ActivityFragment只能有一个LoaderManager。而一个LoaderManager可以有多个loaders

LoaderManager.LoaderCallbacks

提供给客户端的一个callback接口,用于和LoaderManager进行交互。例如,你可以使用onCreateLoader()callback来创建一个新的loader

AsyncTaskLoader

一个抽象Loader,提供一个AsyncTask进行工作。

CursorLoader

AsyncTaskLoader的子类,用于向ContentResover请求,返回一个Cursor。这个类以标准的游标查询方式实现了Loader协议,建立了AsyncTaskLoader,使用一个后台线程来进行游标查询,不会阻塞appUI。因此,使用这个loader是从ContentProvider加载异步数据的最好的方式。

(四)

Demo1LoaderManagerExample

Demo直接抄袭官方例子,用于获取手机里面的通讯录,使用了官方提供的类CursorLoader

public class CursorLoaderListFragment extends ListFragment
		implements SearchView.OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> {

	// This is the Adapter being used to display the list's data.
	SimpleCursorAdapter mAdapter;

	// If non-null, this is the current filter the user has provided.
	String mCurFilter;

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);

		// Give some text to display if there is no data.  In a real
		// application this would come from a resource.
		setEmptyText("No phone numbers");

		// 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 SimpleCursorAdapter(getActivity(),
				android.R.layout.simple_list_item_2, null,
				new String[]{ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.CONTACT_STATUS},
				new int[]{android.R.id.text1, android.R.id.text2}, 0);
		setListAdapter(mAdapter);

		// Prepare the loader.  Either re-connect with an existing one,
		// or start a new one.
<span style="white-space:pre">		</span>getLoaderManager().initLoader(0, null, this);
	}

	@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);
		item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
		SearchView sv = new SearchView(getActivity());
		sv.setOnQueryTextListener(this);
		item.setActionView(sv);
	}

	public boolean onQueryTextChange(String newText) {
		// Called when the action bar search text has changed.  Update
		// the search filter, and restart the loader to do a new query
		// with this filter.
		mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
		getLoaderManager().restartLoader(0, null, this);
		return true;
	}

	@Override
	public boolean onQueryTextSubmit(String query) {
		// Don't care about this.
		return true;
	}

	@Override
	public void onListItemClick(ListView l, View v, int position, long id) {
		// Insert desired behavior here.
		Log.i("FragmentComplexList", "Item clicked: " + id);
	}

	// These are the Contacts rows that we will retrieve.
	static final String[] CONTACTS_SUMMARY_PROJECTION = new String[]{
			ContactsContract.Contacts._ID,
			ContactsContract.Contacts.DISPLAY_NAME,
			ContactsContract.Contacts.CONTACT_STATUS,
			ContactsContract.Contacts.CONTACT_PRESENCE,
			ContactsContract.Contacts.PHOTO_ID,
			ContactsContract.Contacts.LOOKUP_KEY,
	};

<span style="white-space:pre">	</span>public Loader<Cursor> onCreateLoader(int id, Bundle args) {
		// This is called when a new Loader needs to be created.  This
		// sample only has one Loader, so we don't care about the ID.
		// First, pick the base URI to use depending on whether we are
		// currently filtering.
		Uri baseUri;
		if (mCurFilter != null) {
			baseUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI,
					Uri.encode(mCurFilter));
		} else {
			baseUri = ContactsContract.Contacts.CONTENT_URI;
		}

		// Now create and return a CursorLoader that will take care of
		// creating a Cursor for the data being displayed.
		String select = "((" + ContactsContract.Contacts.DISPLAY_NAME + " NOTNULL) AND ("
				+ ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1) AND ("
				+ ContactsContract.Contacts.DISPLAY_NAME + " != '' ))";
<span style="white-space:pre">		</span>return new CursorLoader(getActivity(), baseUri,
				CONTACTS_SUMMARY_PROJECTION, select, null,
				ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
	}

	public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
		// Swap the new cursor in.  (The framework will take care of closing the
		// old cursor once we return.)
		mAdapter.swapCursor(data);
	}

	public void onLoaderReset(Loader<Cursor> loader) {
		// This is called when the last Cursor provided to onLoadFinished()
		// above is about to be closed.  We need to make sure we are no
		// longer using it.
		mAdapter.swapCursor(null);
	}
}

LoaderManager.LoaderCallbacks includes thesemethods:

  • onLoadFinished()— Called when a previously created loader has finished its load.
  • onLoaderReset() — Called when a previously created loader is being reset, thus making itsdata unavailable. 

LoaderManager.LoaderCallbacks包含了三个方法:

onCreateLoader()--- 实例化和返回一个新创建的给定ID的loader

onLoadFinished()--- 当一个创建好的loader完成了load,调用此函数

onLoaderReset()--- 当一个创建好的loader要被reset,调用此函数,这样导致它的数据无效


Demo2:AsyncTaskLoaderExample

有时候CursorLoader并不能满足我们的需要,这时,AsyncTaskLoader就应运而生了,我们可以通过继承AsyncTaskLoader来实现我们想要的异步加载数据的功能(比如从网络上获取数据)。


public class MyAsyncTaskLoader extends AsyncTaskLoader<List<App>> {
	private List<App> mApps;

	public MyAsyncTaskLoader(Context context) {
		super(context);
	}

<strong>	@Override
	public List<App> loadInBackground() {
		System.out.println("LifeCycle loadInBackground");
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet("yourdata.google.com");
		mApps = new ArrayList<App>();
		try {
			HttpResponse httpResponse = httpClient.execute(httpGet);
			if (httpResponse.getStatusLine().getStatusCode() == 200) {
				String result = EntityUtils.toString(httpResponse.getEntity());
				Gson gson = new Gson();
				AppJson appJson = gson.fromJson(result, AppJson.class);
				mApps = appJson.getData().getApps();
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
		return mApps;
	}

	@Override
	protected void onStartLoading() {
		System.out.println("LifeCycle onStartLoading");
		forceLoad();
	}

}

这样之后我们就可以在Activity中使用了:

public class MyActivity extends ActionBarActivity implements LoaderManager.LoaderCallbacks<List<App>> {

	private ListView mListView;
	private ProgressBar mProgressBar;
	private MyAdapter mMyAdapter;
	private ArrayList<App> mApps;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		System.out.println("LifeCycle MyActivity->onCreate");
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_my);
		mListView = (ListView) findViewById(R.id.listView);
		mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
		mApps = new ArrayList<App>();
		mMyAdapter = new MyAdapter(this, mApps);
		mListView.setAdapter(mMyAdapter);

		getSupportLoaderManager().initLoader(0, null, this);

	}

	@Override
	public Loader<List<App>> onCreateLoader(int id, Bundle args) {
		System.out.println("LifeCycle MyActivity->onCreateLoader");
		return new MyAsyncTaskLoader(this);
	}

	@Override
	public void onLoadFinished(Loader<List<App>> loader, List<App> data) {
		System.out.println("LifeCycle MyActivity->onLoadFinished");
		mProgressBar.setVisibility(View.GONE);
		if (data instanceof ArrayList) {
			mApps.clear();
			mApps.addAll(data);
			mMyAdapter.notifyDataSetChanged();
		}

	}

	@Override
	public void onLoaderReset(Loader<List<App>> loader) {
		System.out.println("LifeCycle MyActivity->onLoaderReset");
		mApps.clear();
		mMyAdapter.notifyDataSetChanged();
		mProgressBar.setVisibility(View.INVISIBLE);
	}
}
Demo3:AdvancedLoaderManagerExample

在上面的例子都是简单的例子,并没有涉及判断一些条件和释放资源什么的,下面这个还是官方的例子,用于获取手机中安装的所有程序,有很多关于释放资源的,代码就不贴了,直接拷代码吧。


(五)备注:

loadInBackground方法

这是Loader的核心方法,必须重载,在这个方法里做的就是繁重的任务,比如获取网络数据啊什么的。  

deliverResult方法

当数据到达客户端后,这个方法将被调用,该方法可以不重载,注意官方例子种的两个if语句:一个是isReset()这个方法用来判断Loader是否已经被重置,如果重置了,把资源释放掉;一个是isStarted(),如果Loader被启动那么就把数据传递出去(调super的传递方法)

onStartLoading方法

这个它似乎没有强制我们重载,但是如果没有在里面调用forceLoad()方法,你会发现loadInBackground不会被调用(这个略坑爹)。

阅读更多

扫码向博主提问

加勒比海鲜

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • 深度学习
  • Python
去开通我的Chat快问
版权声明:原创文章,转载前请告知博主。 https://blog.csdn.net/guolindonggld/article/details/39555011
个人分类: Android
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭