cursor管理
使用cursor的時(shí)候需要注意在使用完之后將其關(guān)閉,什么時(shí)候關(guān)閉也是一個(gè)需要注意的問(wèn)題,稍不小心就可能會(huì)出錯(cuò)。我們自己管理cursor可能不是那么容易,問(wèn)題出現(xiàn)這個(gè)或那樣的問(wèn)題,Android系統(tǒng)提供了一套curosr的管理,下面讓我們來(lái)了解一下。
managedQuery和query的區(qū)別
我們都知道在Android系統(tǒng)中,SQLite數(shù)據(jù)庫(kù)的相關(guān)操作方式被封裝為內(nèi)容提供 Content Provider,可以幫助那些不會(huì)SQL語(yǔ)言的開(kāi)發(fā)者快速實(shí)現(xiàn)Android平臺(tái)上的數(shù)據(jù)庫(kù)操作,但是平時(shí)我們?cè)诓樵儠r(shí)一般返回的是Cursor對(duì)象,從本質(zhì)上來(lái)看這兩個(gè)API是不同的類提供的。比如 ContentResolver.query(),以及 Activity.managedQuery()所以,我們看到一個(gè)是ContentResolver提供的查詢方法,位于android.content.ContextWrapper.getContentResolver(),另一個(gè)則為Activity。
這兩種方法的參數(shù)是一樣的,但是Activity類的方法在整個(gè)生命周期中受Activity的影響,而常規(guī)我們處理數(shù)據(jù)邏輯可能單獨(dú)分成一個(gè)類,直接使用Context對(duì)象傳遞實(shí)例句柄,對(duì)于數(shù)據(jù)庫(kù)查詢操作如果數(shù)據(jù)較為龐大盡量使用異步的 AsyncQueryHandler方法防止阻塞線程。
Activity 里面提供了一個(gè) managedQuery() 方法,按照 Android SDK 里面的說(shuō)明,“the activity will manage its lifecycle for you.” 聽(tīng)起來(lái)很好,Activity 可以替你管理 Cursor 的生命周期了,就不用記著去 close() 了,代碼可以更簡(jiǎn)潔。
但是 Activity 是怎么去管理 Cursor 的生命周期的呢?SDK 文檔沒(méi)說(shuō)。最近遇到一個(gè) bug,在一個(gè) Activity 中,用 managedQuery() 查詢數(shù)據(jù)庫(kù),將查詢得到的 Cursor 用 CursorAdapter 與 ListView 綁定。然后在 Activity 里面執(zhí)行批量刪除數(shù)據(jù)表記錄操作,因?yàn)楹臅r(shí)比較長(zhǎng),所以用了多線程處理。測(cè)試團(tuán)隊(duì)發(fā)現(xiàn)的 bug 是,在刪除操作進(jìn)行過(guò)程中,如果按下 Home 鍵,應(yīng)用就崩潰了。崩潰原因是 Cursor 被釋放了,導(dǎo)致工作線程的刪除操作異常。
看了 Activity.java 的源碼之后就明白為什么會(huì)崩潰了。managedQuery() 其實(shí)無(wú)非就是把查詢得到的 Cursor 放到了 Activity 類的一個(gè)數(shù)組成員變量mManagedCursors中,然后當(dāng) Activity stop 的時(shí)候,將這個(gè)數(shù)組里的每個(gè) cursor 都調(diào)用mCursor.deactivate();使curosr無(wú)效,直到下次再調(diào)用requery()方法,以及在 restart()的時(shí)候,將數(shù)組里的每個(gè) cursor 都重新查詢一次。所以在按下 Home 鍵之后,Activity 調(diào)用了 OnStop 了,cursor 也就無(wú)效了,如果有個(gè)線程還在繼續(xù)使用這個(gè) cursor,就會(huì)拋異常了。
因此,在用 managedQuery() 的時(shí)候,需要清楚 cursor 什么時(shí)候會(huì)被釋放,并考慮好自己的代碼在 cursor 被釋放后不再需要使用這個(gè) cursor.
可以用普通的query,然后運(yùn)行 startManagingCursor(cursor),同樣可以把cursor交給系統(tǒng)去管理,不用擔(dān)心cursor沒(méi)有close的情況了。?
?
使用CurosrLoader管理cursor
Android 3.0引入了CursorLoader實(shí)現(xiàn)異步加載數(shù)據(jù),為了避免同步查詢數(shù)據(jù)庫(kù)時(shí)阻塞UI線程的問(wèn)題。在API 11之前可以通過(guò)下載支持庫(kù),來(lái)使之前的系統(tǒng)支持此功能.
在Android 3.0之后,官方推薦使用CursorLoader來(lái)對(duì)curosr進(jìn)行管理,如果想在3.0之前的版本使用,需要繼承FragmentActivity,該類位于android-support-v4.jar包中,直接使用FragmentActivity會(huì)報(bào)錯(cuò),需要導(dǎo)入該包(該包位于F:\android-sdk\extras\android\support)。
相信不少人都知道Android compatibility這個(gè)兼容包的存在。是的,Android compatibility包里面就有FragmentActivity和Fragment這套東西。它有兩個(gè)版本v4和v13,其中v4就支持支持從android sdk1.6開(kāi)始,可以使用Fragment。Android compatibility包是一個(gè)靜態(tài)的jar包,我們只需要將它置于工程中,導(dǎo)入進(jìn)工程,就能很方便的引用到FragmentActivity和Fragment了。
下面的代碼展示了在Android 3.0之前使用Loader的用法:
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.widget.Toast;
?
public class MyActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Object> {
public void onCreate(Bundle savedInstanceState) {
? ? super.onCreate(savedInstanceState);
? ? setContentView(R.layout.main);
? ? getSupportLoaderManager().initLoader(0, null, this);
}
?
public Loader<Object> onCreateLoader(int i, Bundle bundle){
? ? return null; // TODO
}
?
public void onLoadFinished(Loader loader, Object o) {
? ? Toast.makeText(this, "onLoadFinished", Toast.LENGTH_SHORT).show();
}
?
public void onLoaderReset(Loader loader) ? ?{
? ? Toast.makeText(this, "onLoaderReset", Toast.LENGTH_SHORT).show();
}
}
?
?
Loaders,中文可理解為“加載器”,在Android3.0中新增。從字面含義可見(jiàn)其功能,即提供數(shù)據(jù)加載。特別地,加載數(shù)據(jù)的方式為異步。它具有以下特點(diǎn):
l??Loaders用于所有的Activity和Fragment;
l??提供異步數(shù)據(jù)裝載機(jī)制;
l??監(jiān)控他們的來(lái)源數(shù)據(jù)變化情況,在數(shù)據(jù)發(fā)生變化的時(shí)候傳遞新的結(jié)果;
l??自動(dòng)重連到最后一個(gè)數(shù)據(jù)加載器游標(biāo),因此不需要重新查詢數(shù)據(jù)
如何在應(yīng)用中使用Loaders
使用Loaders的先決條件:
l??需要一個(gè)Activity?或者?Fragmnet
l??一個(gè)LoaderManager實(shí)例
l??一個(gè)用于加載數(shù)據(jù)的的CursorLoader對(duì)象(依賴于ContentProvider)
l??一個(gè)LoaderManager.LoaderCallbacks的實(shí)現(xiàn)類.
l??一個(gè)數(shù)據(jù)展現(xiàn)適配器,比如SimpleCursorAdapter
l??一個(gè)數(shù)據(jù)源,比如ContentProvider
啟動(dòng)數(shù)據(jù)加載器Loaders
LoaderManager管理者一個(gè)Activity或者Fragment中的一個(gè)或多個(gè)Loader實(shí)例,每個(gè)Activity或者Fragment只有對(duì)應(yīng)一個(gè)LoaserManager。
?????????一般在Activity的onCreate方法或者Fragment的onActivityCreated方法中初始化一個(gè)Loader:
getLoaderManager().initLoader(0, null, this);
參數(shù):
1、??第一個(gè)參數(shù):0?為L(zhǎng)oader的唯一標(biāo)識(shí)ID;
2、??第二個(gè)參數(shù):?為L(zhǎng)oader的構(gòu)造器可選參數(shù),這里為null;
3、??第三個(gè)參數(shù):this,這里表示當(dāng)前Activity對(duì)象或者Fragment對(duì)象,提供給LoaderManager對(duì)象進(jìn)行數(shù)據(jù)匯報(bào)。
InitLoader()方法保證了Loader初始化及對(duì)象激活,執(zhí)行這個(gè)方法有2個(gè)可能的結(jié)果:
1、??如果ID存在,則重復(fù)利用;
2、??如果ID不存在,則出發(fā)LoaderManager.LoaderCallbacks的onCreateLoader()方法新創(chuàng)建一個(gè)Loader并返回;
?
不管在什么情況下,只有Loader狀態(tài)發(fā)生了變化,與之關(guān)聯(lián)的LoaderManager.LoaderCallbacks實(shí)現(xiàn)類都會(huì)被告知;
你可能注意到了,initLoader返回的Loader對(duì)象并未與任何變量關(guān)聯(lián),那是因?yàn)長(zhǎng)oaderManager有自動(dòng)的Loader管理功能;LoaderManager在必要的時(shí)候自動(dòng)啟動(dòng)及停止數(shù)據(jù)加載操作,并且維護(hù)者Loader的狀態(tài);這就意味著,你很少直接與Loader進(jìn)行交互。一般地,使用LoaderManager.LoaderCallbacks的onCreateLoader()方法去干預(yù)數(shù)據(jù)加載的過(guò)程中的特殊事件。
如何重啟數(shù)據(jù)加載器Loaders
在上面創(chuàng)建Loaders時(shí),如果ID不存在則創(chuàng)建,否則使用舊的Loader,但有些時(shí)候,我們需要清理掉舊的數(shù)據(jù)重新開(kāi)始。
使用restartLoaser()可以做到。比如,SearchView.OnQueryTextListener的實(shí)現(xiàn)類,在查詢條件發(fā)生改變時(shí)重啟Loaders,以便獲取最新的查詢結(jié)果。
public boolean onQueryTextChanged(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;}
如何使用LoaderManager的回調(diào)方法
LoaderManager.LoaderCallbacks?接口是客戶端與LoaderManager進(jìn)行交互的腰帶。
Loader?,特別是CursorLoader,期望在停止?fàn)顟B(tài)后保存它們的狀態(tài)。這樣的話,用戶在交互過(guò)程中,就避免了數(shù)據(jù)的重新加載而導(dǎo)致UI卡死的局面。使用回調(diào)函數(shù),就可以知道什么時(shí)候去創(chuàng)建一個(gè)新的Loader,并且告知應(yīng)用程序什么時(shí)候停止使用Loader加載的數(shù)據(jù)。
回調(diào)方法有:
l??onCreateLoader():根據(jù)給定的ID創(chuàng)建新的Loader;
l??onLoaderFinished():當(dāng)Loader完成數(shù)據(jù)加載后調(diào)用;
l??onLoaderReset():Loader重置,使之前的數(shù)據(jù)無(wú)效;
?
onCreateLoader使用實(shí)例:
// If non-null, this is the current filter the user has provided.
String mCurFilter;
...
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(Contacts.CONTENT_FILTER_URI,
? ? ? ? ? ? ? ????Uri.encode(mCurFilter));
? ? } else {
? ? ? ? baseUri = Contacts.CONTENT_URI;
? ? }
?
? ? // Now create and return a CursorLoader that will take care of
? ? // creating a Cursor for the data being displayed.
? ? String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
? ? ? ? ? ? + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
? ? ? ? ? ? + Contacts.DISPLAY_NAME + " != '' ))";
? ? return new CursorLoader(getActivity(), baseUri,
? ? ? ? ? ? CONTACTS_SUMMARY_PROJECTION, select, null,
? ? ? ? ? ? Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");}
CursorLoader(Context?context,?Uri?uri,?String[]?projection,?String?selection,?String[]?selectionArgs,?String?sortOrder)構(gòu)造器參數(shù)解釋:
Context:上下文,這里是Activity對(duì)象或者Fragment對(duì)象
Uri:內(nèi)容檢索地址
Projection:要顯示的列,傳null表示查詢所有的列
Selection:查詢過(guò)濾語(yǔ)句,類似SQL WHERE?,傳null,表示查詢所有
selectionArgs:查詢參數(shù),替換在selection中定義的???
sortOrder:排序定義,類似SQL ORDER BY
完整的實(shí)例
public static class CursorLoaderListFragment extends ListFragment
? ? ? ? implements 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[] { Contacts.DISPLAY_NAME, 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.
? ? ? ? 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[] {
? ? ? ? Contacts._ID,
? ? ? ? Contacts.DISPLAY_NAME,
? ? ? ? Contacts.CONTACT_STATUS,
? ? ? ? Contacts.CONTACT_PRESENCE,
? ? ? ? Contacts.PHOTO_ID,
? ? ? ? Contacts.LOOKUP_KEY,
? ? };
? ? 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(Contacts.CONTENT_FILTER_URI,
? ? ? ? ? ? ? ? ? ? Uri.encode(mCurFilter));
? ? ? ? } else {
? ? ? ? ? ? baseUri = Contacts.CONTENT_URI;
? ? ? ? }
?
? ? ? ? // Now create and return a CursorLoader that will take care of
? ? ? ? // creating a Cursor for the data being displayed.
? ? ? ? String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND ("
? ? ? ? ? ? ? ? + Contacts.HAS_PHONE_NUMBER + "=1) AND ("
? ? ? ? ? ? ? ? + Contacts.DISPLAY_NAME + " != '' ))";
? ? ? ? return new CursorLoader(getActivity(), baseUri,
? ? ? ? ? ? ? ? CONTACTS_SUMMARY_PROJECTION, select, null,
? ? ? ? ? ? ? ? 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);
? ? }}
?
轉(zhuǎn)載于:https://www.cnblogs.com/adm1989/archive/2013/01/14/2860207.html
總結(jié)
- 上一篇: php判断 二维数组中 是否 存在某个一
- 下一篇: 孕妇梦到墓地什么预兆