A. android中Cursor用完之後有必要關閉嗎
把能關的資源關掉是一個好習慣,可以提高程序的性能
B. android在非同步任務中如何關閉Cursor
在我們開發應用的時候,很多時候會遇到這種問題。 查詢數據會比較耗時,所以我們想把查詢數據放在一個非同步任務中,查詢結果獲得Cursor,然輪拍後在onPostExecute (Cursor result)方法中設置Adapter,我們可能會想到使用Activity的managedQuery來生成Cursor,這樣Cursor就會與Acitivity的生命周期一致了,多麼完美的解決方法!然而事實上managedQuery也有很大的局限性,managedQuery生成的Cursor必須確保不會被替換,因為可臘檔羨能很多程序事實上查詢條件都是不確定的,因此我們經常會用新查詢的Cursor來替換掉原先的Cursor。因此這種方法適用范圍也是很小。 我們不能直接蠢則將Cursor關閉掉,但是注意,CursorAdapter在Acivity結束時並沒有自動的將Cursor關閉掉,因此,你需要在onDestroy函數中,手動關閉。 @Overrideprotected void onDestroy() { super.onDestroy(); mPhotoLoader.stop(); if(mAdapter != null && mAdapter.getCursor() != null) { mAdapter.getCursor().close();}} 如果沒有在Adapter中用到Cursor,可以手動關閉Cursor。 1Cursor cursor =null;2try{3cursor = mContext.getContentResolver().query(uri, null, null,null,
C. android SQLiteDatabase Cursor需要關閉不
cursor.close就可以關閉 sqlitedatabase不需要關閉 ,你可以拆芹襲把sqlitedatabase預先建好,要用旅兄時就拿來用,不需首腔要每次都新建。
D. android 當cursor為null 用不用關閉
如果已經crusor為null,是不用關閉的,因為已經不佔據資源了,會被回收掉
E. 如何檢測 Android Cursor 泄漏
簡介:
本文介紹如何在 Android 檢測 Cursor 泄漏的原理以及使用方法,還指出幾種常見的出錯示例。有一些泄漏在代碼中難以察覺,但程序長時間運行後必然會出現異常。同時該方法同樣適合於其他需要檢測資源泄露的情況。
最近發現某蔬菜手機連接程序在查詢媒體存儲(MediaProvider)資料庫時出現嚴重 Cursor 泄漏現象,運行一段時間後會導致系統中所有使用到該資料庫的程序無法使用。另外在工作中也常發現有些應用有 Cursor 泄漏現象,由於需要長時間運行才會出現異常,所以有的此類 bug 很長時間都沒被發現。
但是一旦 Cursor 泄漏累計到一定數目(通常為數百個)必然會出現無法查詢資料庫的情況,只有等資料庫服務所在進程死掉重啟才能恢復正常。通常的出錯信息如下,指出某 pid 的程序打開了 866 個 Cursor 沒有關閉,導致了 exception:
3634 3644 E javaBinder: *** Uncaught remote exception! (Exceptions are not yet supported across processes.)
3634 3644 E JavaBinder: android.database.: Cursor window allocation of 2048 kb failed. # Open Cursors=866 (# cursors opened by pid 1565=866)
3634 3644 E JavaBinder: at android.database.CursorWindow.(CursorWindow.java:104)
3634 3644 E JavaBinder: at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
3634 3644 E JavaBinder: at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:147)
3634 3644 E JavaBinder: at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:141)
3634 3644 E JavaBinder: at android.database.CursorToBulkCursorAdaptor.getBulkCursorDescriptor(CursorToBulkCursorAdaptor.java:143)
3634 3644 E JavaBinder: at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:118)
3634 3644 E JavaBinder: at android.os.Binder.execTransact(Binder.java:367)
3634 3644 E JavaBinder: at dalvik.system.NativeStart.run(Native Method)
1. Cursor 檢測原理
在 Cursor 對象被 JVM 回收運行到 finalize() 方法的時候,檢測 close() 方法有沒有被調用,此辦法在 ContentResolver 裡面也得到應用。簡化後的示例代碼如下:
1 import android.database.Cursor;
2 import android.database.CursorWrapper;
3 import android.util.Log;
4
5 public class TestCursor extends CursorWrapper {
6 private static final String TAG = "TestCursor";
7 private boolean mIsClosed = false;
8 private Throwable mTrace;
9
10 public TestCursor(Cursor c) {
11 super(c);
12 mTrace = new Throwable("Explicit termination method 'close()' not called");
13 }
14
15 @Override
16 public void close() {
17 mIsClosed = true;
18 }
19
20 @Override
21 public void finalize() throws Throwable {
22 try {
23 if (mIsClosed != true) {
24 Log.e(TAG, "Cursor leaks", mTrace);
25 }
26 } finally {
27 super.finalize();
28 }
29 }
30 }
然後查詢的時候,把 TestCursor 作為查詢結果返回給 APP:
1 return new TestCursor(cursor); // cursor 是普通查詢得到的結果,例如從 ContentProvider.query()
該方法同樣適合於所有需要檢測顯式釋放資源方法沒有被調用的情形,是一種通用方法。但在 finalize() 方法里檢測需要注意
優點:准確。因為該資源在 Cursor 對象被回收時仍沒被釋放,肯定是發生了資源泄露。
缺點:依賴於 finalize() 方法,也就依賴於 JVM 的垃圾回收策略。例如某 APP 現在有 10 個 Cursor 對象泄露,並且這 10 個對象已經不再被任何引用指向處於可回收狀態,但是 JVM 可能並不會馬上回收(時間不可預測),如果你現在檢查不能夠發現問題。另外,在某些情況下就算對象被回收 finalize() 可能也不會執行,也就是不能保證檢測出所有問題。關於 finalize() 更多信息可以參考《Effective Java 2nd Edition》的 Item 7: Avoid Finalizers
2. 使用方法
對於 APP 開發人員
從 GINGERBREAD 開始 Android 就提供了 StrictMode 工具協助開發人員檢查是否不小心地做了一些不該有的操作。使用方法是在 Activity 裡面設置 StrictMode,下面的例子是打開了檢查泄漏的 SQLite 對象以及 Closeable 對象(普通 Cursor/FileInputStream 等)的功能,發現有違規情況則記錄 log 並使程序強行退出。
1 import android.os.StrictMode;
2
3 public class TestActivity extends Activity {
4 private static final boolean DEVELOPER_MODE = true;
5 public void onCreate() {
6 if (DEVELOPER_MODE) {
7 StrictMode.setVMPolicy(new StrictMode.VMPolicy.Builder()
8 .detectLeakedSqlLiteObjects()
9 .detectLeakedClosableObjects()
10 .penaltyLog()
11 .penaltyDeath()
12 .build());
13 }
14 super.onCreate();
15 }
16 }
對於 framework 開發人員
如果是通過 ContentProvider 提供資料庫數據,在 ContentResolver 裡面已有 CloseGuard 類實行類似檢測,但需要自行打開(上例也是打開 CloseGuard):
1 CloseGuard.setEnabled(true);
更值得推薦的辦法是按照本文第一節中的檢測原理,在 ContentResolver 內部類 CursorWrapperInner 裡面加入。其他需要檢測類似於資源泄漏的,同樣可以使用該檢測原理。
3. 容易出錯的地方
忘記調用 close() 這種低級錯誤沒什麼好說的,這種應該也占不小的比例。下面說說不太明顯的例子。
提前返回
有時候粗心會犯這種錯誤,在 close() 調用之前就 return 了,特別是函數比較大邏輯比較復雜時更容易犯錯。這種情況可以通過把 close() 放在 finally 代碼塊解決
1 private void method() {
2 Cursor cursor = query(); // 假設 query() 是一個查詢資料庫返回 Cursor 結果的函數
3 if (flag == false) { // !!提前返回
4 return;
5 }
6 cursor.close();
7 }
類的成員變數
假設類裡面有一個在類全局有效的成員變數,在方法 A 獲取了查詢結果,後面在其他地方又獲取了一次查詢結果,那麼第二次查詢的時候就應該先把前面一個 Cursor 對象關閉。
1 public class TestCursor {
2 private Cursor mCursor;
3
4 private void methodA() {
5 mCursor = query();
6 }
7
8 private void methodB() {
9 // !!必須先關閉上一個 cursor 對象
10 mCursor = query();
11 }
12 }
注意:曾經遇到過有人對 mCursor 感到疑惑,明明是同一個變數為什麼還需要先關閉?首先 mCursor 是一個 Cursor 對象的引用,在 methodA 時 mCursor 指向了 query() 返回的一個 Cursor 對象 1;在 methodB() 時它又指向了返回的另外一個 Cursor 對象 2。在指向 Cursor 對象 2 之前必須先關閉 Cursor 對象 1,否則就出現了 Cursor 對象 1 在 finalize() 之前沒有調用 close() 的情況。
異常處理
打開和關閉 Cursor 之間的代碼出現 exception,導致沒有跑到關閉的地方:
1 try {
2 Cursor cursor = query();
3 // 中間省略某些出現異常的代碼
4 cursor.close();
5 } catch (Exception e) {
6 // !!出現異常沒跑到 cursor.close()
7 }
這種情況應該把 close() 放到 finally 代碼塊裡面:
1 Cursor cursor = null;
2 try {
3 cursor = query();
4 // 中間省略某些出現異常的代碼
5 } catch (Exception e) {
6 // 出現異常
7 } finally {
8 if (cursor != null)
9 cursor.close();
10 }
F. Android Cursor(游標)解析
SQLiteDatabase db = dataBaseHelper.getWritableDatabase();
Cursor cursor = db.rawQuery("select *from User",null);
1.游標的行數:int getCount()
2.當前游標的位置:int getPosition()
返回的值從零開始, 當第一次返回行集時游標將位於位置 -1,即第一行之前。在返回最後一行之後,對 next() 的另一個調用將使游標離開最後一個條目,位於 count() 的位置。
3.從當前位置應用的偏移量:boolean move(int offset)
將游標從當前位置向前或向後移動一個相對量。 正偏移向前移動,負偏移向後移動。 如果最終位置在結果集的邊界之外,則結果位置將分別固定為 -1 或 count(),具體取決於該值是在集合的前端還是末尾。如果請求的目的地可達,此方法將返回 true,否則返回 false。
4.將游標移動到絕對位置:boolean moveToPosition(int position)
值的有效范圍是 -1 <= 位置 <= 計數。如果請求目的地可達,此方法將返回 true,否則返回 false。
5.將游標移動到第一行:boolean moveToFirst()
6.將游標移動到最後一行:boolean moveToLast()
7.將游標移動到下一行:boolean moveToNext()
8.將游標移動到上一行:boolean moveToPrevious()
9.返回遊標是否指向第一行:boolean isFirst()
10.返回遊標是否指向最後一行:boolean isLast()
11.返回遊標是否指向第一行之前的位置:boolean isBeforeFirst()
12.返回遊標是否指向最後一行之後的位置:boolean isAfterLast()
13.給定列名的從零開始的列索引,如果列名不存在,則返回 -1:int getColumnIndex(String columnName)
14.給定列名的從零開始的索引,如果該列不存在則拋出非法參數異常:int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException
15.給定的從零開始的列索引處的列名:String getColumnName(int columnIndex)
16.返回一個字元串數組,其中按列在結果中的順序保存結果集中所有列的名稱。:String[] getColumnNames()
17.返回總列數:int getColumnCount()
18.各類型返回值
(1)以位元組數組的形式返回請求列的值:byte[] getBlob(int columnIndex)
(2)以字元串形式返回請求列的值:String getString(int columnIndex)
(3)以整數形式返回請求列的值:int getInt(int columnIndex)
(4)以 long 形式返回請求列的值:long getLong(int columnIndex)
(5)以浮點數形式返回請求列的值:float getFloat(int columnIndex)
(6)以雙精度形式返回請求列的值:double getDouble(int columnIndex)
(7)返回給定列值的數據類型:int getType(int columnIndex)
(8)列值是否為空:boolean isNull(int columnIndex)
(9)以短形式返回請求列的值:short getShort(int columnIndex)
19.檢索請求的列文本並將其存儲在提供的緩沖區中:void StringToBuffer(int columnIndex, CharArrayBuffer buffer)
20.關閉游標:void close()
21.游標是否關閉:boolean isClosed()
22.注冊一個觀察者,當支持此游標的內容發生變化時調用該觀察者:void registerContentObserver(ContentObserver observer)
23.銷毀注冊的觀察者:void unregisterContentObserver(ContentObserver observer)
24.注冊一個觀察者,當數據集的內容發生變化時被調用:void registerDataSetObserver(DataSetObserver observer)
25.銷毀注冊的觀察者:void unregisterDataSetObserver(DataSetObserver observer)
26.注冊以查看內容 URI 的更改。這可以是特定數據行的 URI,也可以是內容類型的通用URI:void setNotificationUri(ContentResolver cr, Uri uri)
cr是上下文,uri是需要觀看的內容
27.是否所有游標移動都應導致調用 onMove():boolean getWantsAllOnMoveCalls()
只有在此方法返回 true 時,才會跨進程調用 onMove()
28.返回一組額外的值:Bundle getExtras()
29.游標用戶與游標通信的帶外方式:Bundle respond(Bundle extras)
30.設置 Bundle 返回的getExtras():void setExtras(Bundle extras)
G. android cursor finalized without prior close 怎麼查
[java] view plain
Cursor循環內再申請Cursor,記得將內部申請的每個Cursor都加上close
[java] view plain
ContentResolver resolver = getContentResolver();
Uri URI = ContactsContract.Contacts.CONTENT_URI;
String[] columns = new String[] {ContactsContract.Contacts._ID
,PhoneLookup.DISPLAY_NAME};
//查詢聯系人ID和聯系人名稱兩列
Cursor cursor = resolver.query(URI, columns,
PhoneLookup.HAS_PHONE_NUMBER+"=1", null, null);
//限定只返回有號碼的聯系人
while(cursor.moveToNext()){
String phoneNum="";
Cursor cursor2=resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI
, new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER},
ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"="+cursor.getLong(0)
, null, null);
//因為號茄喚碼與聯系人不存在一個表中,一個聯系人可能存在多個號碼,
//所以根據聯系人ID查找號碼,存在phoneNum中
while(cursor2.moveToNext()) {
phoneNum+=cursor2.getString(0)+"||";
//循環把該聯系的所屬的號碼加進phoneNum
}
cursor2.close();
//同學,就是這臘芹里了,cursor里申請第二個cursor2,
//記得將每個cursor2給關閉了,不然就資源泄漏了。
Log.d("LocTestDemo", "--" + cursor.getLong(0) + "顫局凱:"
+ cursor.getString(1) + "::" + phoneNum);
//往JSONArray里添加一個聯系人信息
singer = new JSONObject();
try {
singer.put("id", cursor.getLong(0));
singer.put("name", cursor.getString(1));
singer.put("phone", phoneNum);
} catch (JSONException e) {
e.printStackTrace();
}
singers.put(singer);
}
cursor.close();
//這里其實還得加上try,里外的Cursor都要加,
//在catch里判斷cursor是否為空,不為空還得給關閉。
cursor循環內再申請cursor記得將內部申請的每個cursor都關閉
H. android cursor is deactivated prior to calling this method.怎麼回事
此異常由明衫賣於app在使用已經關閉的cursor,可以按照如下方法修改解決:
修改packages/apps/Browser/src/com/android/browser/BrowserSnapshotPage.java
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Cursor c = mAdapter.getItem(position);
String title = c.getString(SNAPSHOT_TITLE);
String url = "file://" + c.getString(SNAPSHOT_VIEWSTATE_PATH);
--刪除這一行 c.close();
mCallback.openSnapshot(id, title, url);
}
因為我們之前並不是打開一個激逗cursor,所以這兒不需要關閉塌改。
I. android資料庫每次查詢完都要關閉嗎
關於數宏派據庫句柄,每次用完是必須手首脊動調用close()方法關掉的;關於cursor一般情況也是要求關掉的,如果只獲取了一次結果集,不關掉cursor對程序的邏輯沒有影響,只是會拋一個非必要蔽芹賀性異常,但是如果多次獲取結果集,就必須先關掉cursor,再重新獲取結果集,否則cursor沒釋放,之後的結果集是獲取不到的,且會報錯。所以,總的來說,都要關掉。