標籤:android loader cursorloader 非同步載入
從Android3.0開始,Android SDK提供了Loader技術,使用Loader技術可以很容易進行資料的非同步載入。Loader技術為我們提供的核心類有:
- LoaderManager:可以通過Activity或者的Fragment的getLoaderManager()方法得到LoaderManager,用來對Loader進行管理,一個Activity或者Fragment只能有一個LoaderManager。
- LoaderManager.LoaderCallbacks:用於同LoaderManager進行互動,可以在其中建立Loader對象。
- AsyncTaskLoader:抽象類別,可以進行非同步載入資料的Loader,貌似內部也是通過AsynTask實現的,可以通過繼承它構建自己的Loader,也可以使用現有的子類,例如非同步查詢資料庫可以使用CursorLoader。
使用Loader一般可以經過以下步驟:
1、初始化Loader,可以使用initLoader(intid, Bundle args, LoaderManager.LoaderCallbacks<D> callback);方法進行初始化。
id:標識Loader的ID,一個Activity或者Fragment只能有一個LoaderManager,但可以有多個Loader,通過ID區 分。在建立Loader時,如果發現已經有相同ID的Loader就會複用該Loader,而不會重新建立。
args:傳給建立Loader的參數。
Callback:回調介面。
2、實現LoaderManager.LoaderCallbacks中的方法,LoaderManager.LoaderCallbacks中需要實現的方法有:
- publicLoader<D> onCreateLoader(int id, Bundle args):建立新的Loader,id為LoaderID,如果已經有相同ID的Loader就會複用該Loader,而不會重新建立。 args為初始化時傳遞的參數。該方法開始非同步查詢,並返回一個泛型類,如果是查詢資料庫可以返回一個CursorLoader,可以返回自訂的Loader。
- public voidonLoadFinished(Loader<D> loader, D data):非同步查詢結束的會調用這個方法,並返回查詢結果 data。
- public void onLoaderReset(Loader<D> loader): 當調用Loader.reset()將Loader資料清空時,但在系統銷毀Loader時會自動調用Loader.reset()方法,我們一般不需要手動調用,只需要在onLoaderReset方法中,將使用Loader的移除。
3、使用 restartLoader(intid, Bundle args, LoaderManager.LoaderCallbacks<D> callback)方法進行資料更新,和初始化一個,如果有相同id的Loader存在,會複用Loader,並清空原有Loader中的資料,如果沒有就建立一個。這個方法一般使用在需要更新資料時,例如下面例子中,在搜尋關鍵改變時,需要調用這個方法,從新非同步查詢資料。
下面一個例子使用CursorLoader查詢連絡人名字,並顯示在ListActivity中,在ActionBar上放置了一個SearchView可以根據連絡人姓名關鍵字查詢連絡人。
Activity代碼如下:
public class MainActivity extends ListActivity implements LoaderManager.LoaderCallbacks<Cursor>, SearchView.OnQueryTextListener {private SimpleCursorAdapter cursorAdapter; private String filterName=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv= (TextView) findViewById(android.R.id.empty); tv.setText("請稍後"); cursorAdapter=new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, null, new String[]{ContactsContract.Contacts.DISPLAY_NAME}, new int[]{android.R.id.text1},0); setListAdapter(cursorAdapter); //初始化Loader getLoaderManager().initLoader(0,null,this); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { Uri uri; String[] pro=new String[]{ ContactsContract.Contacts.DISPLAY_NAME,ContactsContract.Contacts._ID}; if(TextUtils.isEmpty(filterName)){ uri= ContactsContract.Contacts.CONTENT_URI; }else{ uri=Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI,Uri.encode(filterName)); } //建立Loader對象,開始非同步載入資料 return new CursorLoader(this,uri,pro, null, null,null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { //得到非同步載入資料,更新Adapter cursorAdapter.swapCursor(data); } @Override public void onLoaderReset(Loader<Cursor> loader) { //移除adapter使用的Loader,系統會釋放不再使用的Loader cursorAdapter.swapCursor(null); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main,menu); SearchView v= (SearchView) menu.findItem(R.id.menu_search).getActionView(); v.setOnQueryTextListener(this); return true; } @Override public boolean onQueryTextSubmit(String query) { return true; } @Override public boolean onQueryTextChange(String newText) { filterName=newText; //使用新的Loader(清空舊的資料) getLoaderManager().restartLoader(0,null,this); return false; }}
菜單資源檔:
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity"> <item android:actionViewClass="android.widget.SearchView" android:id="@+id/menu_search" android:showAsAction="ifRoom|collapseActionView" android:title="搜尋" tools:ignore="AppCompatResource" /></menu>
最後來個:
Android Loader 技術的簡單實用