Android Cursor(光标)解析

Android Cursor(光标)解析,第1张

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 copyStringToBuffer(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)

Cursor,翻译叫做箭头。

其实就是指向某一条的意思。

一般在sql查询中使用

比如你在查询一张表格,而cursor会像手指一样,从第一条开始指向最后一条,这在java中就可以写成一个for循环,然后取出每一条的数据,放入list中,表中的数据就全部拿到了。

见笑了~

有一些泄漏在代码中难以察觉,但程序长时间运行后必然会出现异常。同时该方法同样适合于其他需要检测资源泄露的情况。 最近发现某蔬菜手机连接程序在查询媒体存储(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.CursorWindowAllocationException: 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 里面也得到应用。简化后的示例代码如下: 1import android.database.Cursor2import android.database.CursorWrapper3import android.util.Log4 5publicclass TestCursor extends CursorWrapper { 6privatestaticfinal String TAG = "TestCursor"7privateboolean mIsClosed = false8private Throwable mTrace910public TestCursor(Cursor c) { 11super(c)12 mTrace = new Throwable("Explicit termination method 'close()' not called")13 } 1415 @Override 16publicvoid close() { 17 mIsClosed = true18 } 1920 @Override 21publicvoid finalize() throws Throwable { 22try { 23if (mIsClosed != true) { 24 Log.e(TAG, "Cursor leaks", mTrace)25 } 26 } finally { 27super.finalize()28 } 29 } 30 }然后查询的时候,把 TestCursor 作为查询结果返回给 APP:1returnnew TestCursor(cursor)// cursor 是普通查询得到的结果,例如从 ContentProvider.query() 该方法同样适合于所有需要检测显式释放资源方法没有被调用的情形,是一种通用方法。但在 finalize() 方法里检测需要注意优点:准确。因为该资源在 Cursor 对象被回收时仍没被释放,肯定是发生了资源泄露。缺点:依赖于 finalize() 方法,也就依赖于 JVM 的垃圾回收策略。例如某 APP 现在有 10 个 Cursor 对象泄露,并且这 10 个对象已经不再被任何引用指向处于可回收状态,但是 JVM 可能并不会马上回收(时间不可预测),如果你现在检查不能够发现问题。另外,在某些情况下就算对象被回收 finalize() 可能也不会执行,也就是不能保证检测出所有问题。关于 finalize() 更多信息可以参考《Effective Java 2nd Edition》的 Item 7: Avoid Finalizers2. 使用方法对于APP 开发人员从GINGERBREAD 开始 Android 就提供了 StrictMode 工具协助开发人员检查是否不小心地做了一些不该有的 *** 作。使用方法是在 Activity 里面设置 StrictMode,下面的例子是打开了检查泄漏的 SQLite 对象以及 Closeable 对象(普通 Cursor/FileInputStream 等)的功能,发现有违规情况则记录 log 并使程序强行退出。 1import android.os.StrictMode2 3publicclass TestActivity extends Activity { 4privatestaticfinalboolean DEVELOPER_MODE = true5publicvoid onCreate() { 6if (DEVELOPER_MODE) { 7 StrictMode.setVMPolicy(new StrictMode.VMPolicy.Builder() 8 .detectLeakedSqlLiteObjects() 9 .detectLeakedClosableObjects() 10 .penaltyLog() 11 .penaltyDeath() 12 .build())13 } 14super.onCreate()15 } 16 } 对于framework 开发人员如果是通过 ContentProvider 提供数据库数据,在 ContentResolver 里面已有 CloseGuard 类实行类似检测,但需要自行打开(上例也是打开 CloseGuard):1 CloseGuard.setEnabled(true)更值得推荐的办法是按照本文第一节中的检测原理,在 ContentResolver 内部类 CursorWrapperInner 里面加入。其他需要检测类似于资源泄漏的,同样可以使用该检测原理。3. 容易出错的地方忘记调用 close() 这种低级错误没什么好说的,这种应该也占不小的比例。下面说说不太明显的例子。提前返回有时候粗心会犯这种错误,在 close() 调用之前就 return 了,特别是函数比较大逻辑比较复杂时更容易犯错。这种情况可以通过把 close() 放在 finally 代码块解决1privatevoid method() { 2 Cursor cursor = query()// 假设query() 是一个查询数据库返回 Cursor 结果的函数3if (flag == false) { //!!提前返回4return5 } 6 cursor.close()7 } 类的成员变量假设类里面有一个在类全局有效的成员变量,在方法 A 获取了查询结果,后面在其他地方又获取了一次查询结果,那么第二次查询的时候就应该先把前面一个 Cursor 对象关闭。 1publicclass TestCursor { 2private Cursor mCursor3 4privatevoid methodA() { 5 mCursor = query()6 } 7 8privatevoid 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,导致没有跑到关闭的地方:1try { 2 Cursor cursor = query()3// 中间省略某些出现异常的代码4 cursor.close()5 } catch (Exception e) { 6//!!出现异常没跑到 cursor.close()7 }这种情况应该把 close() 放到 finally 代码块里面: 1 Cursor cursor = null2try { 3 cursor = query()4// 中间省略某些出现异常的代码 5 } catch (Exception e) { 6// 出现异常 7 } finally { 8if (cursor != null) 9 cursor.close()10 }4. 总结思考在finalize() 里面检测是可行的,且基本可以满足需要。针对 finalize() 执行时间不确定以及可能不执行的问题,可以通过记录目前打开没关闭的 Cursor 数量来部分解决,超过一定数目发出警告,两种手段相结合。还有没有其他检测办法呢?有,在 Cursor 构造方法以及 close() 方法添加 log,运行一段时间后检查 log 看哪个地方没有关闭。简化代码如下: 1import android.database.Cursor2import android.database.CursorWrapper3import android.util.Log4 5publicclass TestCursor extends CursorWrapper { 6privatestaticfinal String TAG = "TestCursor"7private Throwable mTrace8 9public TestCursor(Cursor c) { 10super(c)11 mTrace = new Throwable("cusor opened here")12 Log.d(TAG, "Cursor " + this.hashCode() + " opened, stacktrace is: ", mTrace)13 } 1415 @Override 16publicvoid close() { 17 mIsClosed = true18 Log.d(TAG, "Cursor " + this.hashCode() + " closed.")19 } 20 }检查时看某个 hashCode() 的 Cursor 有没有调用过 close() 方法,没有的话说明资源有泄露。这种方法优点是同样准确,且更可靠。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12080578.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-20
下一篇 2023-05-20

发表评论

登录后才能评论

评论列表(0条)

保存