Concepts of Cursor and CursorWindow on android platform
来源:互联网 发布:js字符串数组方法 编辑:程序博客网 时间:2024/06/05 04:42
简记android平台下ContenProvider使用SQLite数据库实现时的代码片段和逻辑结构,主要描述Cursor和CursorWindow.
Cursor can be treated as an iterater of the traditional ODBC record set and is the current position of the data record. It describes and reflects the logical result record set in android.
AbstractWindowedCursor both in client side and provider-implementing sideassociates a cursor window, which is an ashmem actually on android platform. Same name, same ashmem.
BulkCursorToCursorAdaptor and CursorToBulkCursorAdaptor are used for cursor transfer between processes, they are used to binderize the cursor.
CursorWindow uses the /dev/ashmem to share the memory and avoids data block transfer to improve efficiency. CursorWindow is a cache window/buffer of the record set which is an obvious design to achieve efficiency. Using ashmem is avoiding record block transfer and is more efficiency in the requirement and circumstance.
IN ALL, CursorWindow is created with RW in service process, and is created with R in client process for with RW failed.
The relation of Cursors:
Cursor
CrossProcessCursor
AbstractCursor
AbstractWindowedCursor
SQLiteCursor
For BuilkCursorToCursorAdapater's hierachy:
Cursor
CrossProcessCursor
AbstractCursor
AbstractWindowedCursor
BulkCursorToCursorAdaptor
The top 4 base classes are same used in client and provider-implementing side.
------- Code of Control Flow------------
ContentResolver.java303 public final Cursor query(Uri uri, String[] projection,304 String selection, String[] selectionArgs, String sortOrder) {305 IContentProvider provider = acquireProvider(uri);306 if (provider == null) {307 return null;308 }309 try {310 long startTime = SystemClock.uptimeMillis();311 Cursor qCursor = provider.query(uri, projection, selection, selectionArgs, sortOrder);312 if (qCursor == null) {313 releaseProvider(provider);314 return null;315 }316 // force query execution317 qCursor.getCount();318 long durationMillis = SystemClock.uptimeMillis() - startTime;319 maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);320 // Wrap the cursor object into CursorWrapperInner object321 return new CursorWrapperInner(qCursor, provider);322 } catch (RemoteException e) {323 releaseProvider(provider);324325 // Arbitrary and not worth documenting, as Activity326 // Manager will kill this process shortly anyway.327 return null;328 } catch (RuntimeException e) {329 releaseProvider(provider);330 throw e;331 }332 }IContentProvider.javastatic final int QUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;ContentProviderNative.javafinal class ContentProviderProxy implements IContentProvider315{316 public ContentProviderProxy(IBinder remote)317 {318 mRemote = remote;319 }320321 public IBinder asBinder()322 {323 return mRemote;324 }325326 public Cursor query(Uri url, String[] projection, String selection,327 String[] selectionArgs, String sortOrder) throws RemoteException {328 BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();329 Parcel data = Parcel.obtain();330 Parcel reply = Parcel.obtain();331 try {332 data.writeInterfaceToken(IContentProvider.descriptor);333334 url.writeToParcel(data, 0);335 int length = 0;336 if (projection != null) {337 length = projection.length;338 }339 data.writeInt(length);340 for (int i = 0; i < length; i++) {341 data.writeString(projection[i]);342 }343 data.writeString(selection);344 if (selectionArgs != null) {345 length = selectionArgs.length;346 } else {347 length = 0;348 }349 data.writeInt(length);350 for (int i = 0; i < length; i++) {351 data.writeString(selectionArgs[i]);352 }353 data.writeString(sortOrder);354 data.writeStrongBinder(adaptor.getObserver().asBinder());355356 mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);357358 DatabaseUtils.readExceptionFromParcel(reply);359360 IBulkCursor bulkCursor = BulkCursorNative.asInterface(reply.readStrongBinder());361 if (bulkCursor != null) {362 int rowCount = reply.readInt();363 int idColumnPosition = reply.readInt();364 boolean wantsAllOnMoveCalls = reply.readInt() != 0;365 adaptor.initialize(bulkCursor, rowCount, idColumnPosition, wantsAllOnMoveCalls);366 } else {367 adaptor.close();368 adaptor = null;369 }370 return adaptor;371 } catch (RemoteException ex) {372 adaptor.close();373 throw ex;374 } catch (RuntimeException ex) {375 adaptor.close();376 throw ex;377 } finally {378 data.recycle();379 reply.recycle();380 }381 }ContentProviderNative.java77 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)78 throws RemoteException {79 try {80 switch (code) {81 case QUERY_TRANSACTION:82 {83 data.enforceInterface(IContentProvider.descriptor);8485 Uri url = Uri.CREATOR.createFromParcel(data);8687 // String[] projection88 int num = data.readInt();89 String[] projection = null;90 if (num > 0) {91 projection = new String[num];92 for (int i = 0; i < num; i++) {93 projection[i] = data.readString();94 }95 }9697 // String selection, String[] selectionArgs...98 String selection = data.readString();99 num = data.readInt();100 String[] selectionArgs = null;101 if (num > 0) {102 selectionArgs = new String[num];103 for (int i = 0; i < num; i++) {104 selectionArgs[i] = data.readString();105 }106 }107108 String sortOrder = data.readString();109 IContentObserver observer = IContentObserver.Stub.asInterface(110 data.readStrongBinder());111112 Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder);113 if (cursor != null) {114 CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(115 cursor, observer, getProviderName());116 final IBinder binder = adaptor.asBinder();117 final int count = adaptor.count();118 final int index = BulkCursorToCursorAdaptor.findRowIdColumnIndex(119 adaptor.getColumnNames());120 final boolean wantsAllOnMoveCalls = adaptor.getWantsAllOnMoveCalls();121122 reply.writeNoException();123 reply.writeStrongBinder(binder);124 reply.writeInt(count);125 reply.writeInt(index);126 reply.writeInt(wantsAllOnMoveCalls ? 1 : 0);127 } else {128 reply.writeNoException();129 reply.writeStrongBinder(null);130 }131132 return true;133 } ……………..298 }299 } catch (Exception e) {300 DatabaseUtils.writeExceptionToParcel(reply, e);301 return true;302 }303304 return super.onTransact(code, data, reply, flags);305 }306SettingsProvider.java for example.641 @Override642 public Cursor query(Uri url, String[] select, String where, String[] whereArgs, String sort) {643 SqlArguments args = new SqlArguments(url, where, whereArgs);644 SQLiteDatabase db = mOpenHelper.getReadableDatabase();645646 // The favorites table was moved from this provider to a provider inside Home647 // Home still need to query this table to upgrade from pre-cupcake builds648 // However, a cupcake+ build with no data does not contain this table which will649 // cause an exception in the SQL stack. The following line is a special case to650 // let the caller of the query have a chance to recover and avoid the exception651 if (TABLE_FAVORITES.equals(args.table)) {652 return null;653 } else if (TABLE_OLD_FAVORITES.equals(args.table)) {654 args.table = TABLE_FAVORITES;655 Cursor cursor = db.rawQuery("PRAGMA table_info(favorites);", null);656 if (cursor != null) {657 boolean exists = cursor.getCount() > 0;658 cursor.close();659 if (!exists) return null;660 } else {661 return null;662 }663 }664665 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();666 qb.setTables(args.table);667668 Cursor ret = qb.query(db, select, args.where, args.args, null, null, sort);669 ret.setNotificationUri(getContext().getContentResolver(), url);670 return ret;671 }SQLiteQueryBuilder.java327 public Cursor query(SQLiteDatabase db, String[] projectionIn,328 String selection, String[] selectionArgs, String groupBy,329 String having, String sortOrder, String limit) {330 if (mTables == null) {331 return null;332 }333334 if (mStrict && selection != null && selection.length() > 0) {335 // Validate the user-supplied selection to detect syntactic anomalies336 // in the selection string that could indicate a SQL injection attempt.337 // The idea is to ensure that the selection clause is a valid SQL expression338 // by compiling it twice: once wrapped in parentheses and once as339 // originally specified. An attacker cannot create an expression that340 // would escape the SQL expression while maintaining balanced parentheses341 // in both the wrapped and original forms.342 String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,343 having, sortOrder, limit);344 validateSql(db, sqlForValidation); // will throw if query is invalid345 }346347 String sql = buildQuery(348 projectionIn, selection, groupBy, having,349 sortOrder, limit);350351 if (Log.isLoggable(TAG, Log.DEBUG)) {352 Log.d(TAG, "Performing query: " + sql);353 }354 return db.rawQueryWithFactory(355 mFactory, sql, selectionArgs,356 SQLiteDatabase.findEditTable(mTables)); // will throw if query is invalid357 }SQLiteDatabase.java1568 public Cursor rawQueryWithFactory(1569 CursorFactory cursorFactory, String sql, String[] selectionArgs,1570 String editTable) {1571 verifyDbIsOpen();1572 BlockGuard.getThreadPolicy().onReadFromDisk();15731574 SQLiteDatabase db = getDbConnection(sql);1575 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(db, sql, editTable);15761577 Cursor cursor = null;1578 try {1579 cursor = driver.query(1580 cursorFactory != null ? cursorFactory : mFactory,1581 selectionArgs);1582 } finally {1583 releaseDbConnection(db);1584 }1585 return cursor;1586 }SQLiteDirectCursorDriver.java40 public Cursor query(CursorFactory factory, String[] selectionArgs) {41 // Compile the query42 SQLiteQuery query = null;4344 try {45 mDatabase.lock(mSql);46 mDatabase.closePendingStatements();47 query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);4849 // Create the cursor50 if (factory == null) {51 mCursor = new SQLiteCursor(this, mEditTable, query);52 } else {53 mCursor = factory.newCursor(mDatabase, this, mEditTable, query);54 }5556 mQuery = query;57 query = null;58 return mCursor;59 } finally {60 // Make sure this object is cleaned up if something happens61 if (query != null) query.close();62 mDatabase.unlock();63 }64 }
It can be seen that only allocate the SQLiteCursor and save the query statement.
If ContentResolver user and ContentProvider in the same process, for example
06-26 16:30:11.439 E/AndroidRuntime(13356): at android.database.CursorWindow.<init>(CursorWindow.java:104)
06-26 16:30:11.439 E/AndroidRuntime(13356): at android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
06-26 16:30:11.439 E/AndroidRuntime(13356): at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:162)
06-26 16:30:11.439 E/AndroidRuntime(13356): at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
06-26 16:30:11.439 E/AndroidRuntime(13356): at android.content.ContentResolver.query(ContentResolver.java:317)
06-26 16:30:11.439 E/AndroidRuntime(13356): at com.android.providers.downloads.DownloadService$UpdateThread.run(DownloadService.java:307)
SQLiteCursor.getCount is called directly
153 @Override154 public int getCount() {155 if (mCount == NO_COUNT) {156 fillWindow(0);157 }158 return mCount;159 }160161 private void fillWindow(int startPos) {162 clearOrCreateWindow(getDatabase().getPath());163 mWindow.setStartPosition(startPos);164 int count = getQuery().fillWindow(mWindow);165 if (startPos == 0) { // fillWindow returns count(*) only for startPos = 0166 if (Log.isLoggable(TAG, Log.DEBUG)) {167 Log.d(TAG, "received count(*) from native_fill_window: " + count);168 }169 mCount = count;170 } else if (mCount <= 0) {171 throw new IllegalStateException("Row count should never be zero or negative "172 + "when the start position is non-zero");173 }174 }Using the sqlite query to fillWindow.78 /* package */ int fillWindow(CursorWindow window) {79 mDatabase.lock(mSql);80 long timeStart = SystemClock.uptimeMillis();81 try {82 acquireReference();83 try {84 window.acquireReference();85 int startPos = window.getStartPosition();86 int numRows = nativeFillWindow(nHandle, nStatement, window.mWindowPtr,87 startPos, mOffsetIndex);88 if (SQLiteDebug.DEBUG_LOG_SLOW_QUERIES) {89 long elapsed = SystemClock.uptimeMillis() - timeStart;90 if (SQLiteDebug.shouldLogSlowQuery(elapsed)) {91 Log.d(TAG, "fillWindow took " + elapsed92 + " ms: window=\"" + window93 + "\", startPos=" + startPos94 + ", offset=" + mOffsetIndex95 + ", filledRows=" + window.getNumRows()96 + ", countedRows=" + numRows97 + ", query=\"" + mSql + "\""98 + ", args=[" + (mBindArgs != null ?99 TextUtils.join(", ", mBindArgs.values()) : "")100 + "]");101 }102 }103 mDatabase.logTimeStat(mSql, timeStart);104 return numRows;105 } catch (IllegalStateException e){106 // simply ignore it107 return 0;108 } catch (SQLiteDatabaseCorruptException e) {109 mDatabase.onCorruption();110 throw e;111 } catch (SQLiteException e) {112 Log.e(TAG, "exception: " + e.getMessage() + "; query: " + mSql);113 throw e;114 } finally {115 window.releaseReference();116 }117 } finally {118 releaseReference();119 mDatabase.unlock();120 }121 }Use 38static jint nativeFillWindow(JNIEnv* env, jclass clazz, jint databasePtr,39 jint statementPtr, jint windowPtr, jint startPos, jint offsetParam) querying sqlite3 to fill window. If ContentResolver user and ContentProvider in the different process, qCursur.getCount() returns directly.79 @Override80 public int getCount() {81 throwIfCursorIsClosed();82 return mCount;83 }8485 @Override86 public boolean onMove(int oldPosition, int newPosition) {87 throwIfCursorIsClosed();8889 try {90 // Make sure we have the proper window91 if (mWindow == null92 || newPosition < mWindow.getStartPosition()93 || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) {94 setWindow(mBulkCursor.getWindow(newPosition));95 } else if (mWantsAllOnMoveCalls) {96 mBulkCursor.onMove(newPosition);97 }98 } catch (RemoteException ex) {99 // We tried to get a window and failed100 Log.e(TAG, "Unable to get window because the remote process is dead");101 return false;102 }103104 // Couldn't obtain a window, something is wrong105 if (mWindow == null) {106 return false;107 }108109 return true;110 }
When the qCursor is moved, onMove is called, and setWindow(mBulkCursor.getWindow()) branch is taken for the first time or cursor is out of the current range. And mBulkCursor.onMove branch is called for maintaining cursor position consistent.
In BulkCursorProxy183 public CursorWindow getWindow(int startPos) throws RemoteException184 {185 Parcel data = Parcel.obtain();186 Parcel reply = Parcel.obtain();187 try {188 data.writeInterfaceToken(IBulkCursor.descriptor);189 data.writeInt(startPos);190191 mRemote.transact(GET_CURSOR_WINDOW_TRANSACTION, data, reply, 0);192 DatabaseUtils.readExceptionFromParcel(reply);193194 CursorWindow window = null;195 if (reply.readInt() == 1) {196 window = CursorWindow.newFromParcel(reply);197 }198 return window;199 } finally {200 data.recycle();201 reply.recycle();202 }203 }204205 public void onMove(int position) throws RemoteException {206 Parcel data = Parcel.obtain();207 Parcel reply = Parcel.obtain();208 try {209 data.writeInterfaceToken(IBulkCursor.descriptor);210 data.writeInt(position);211212 mRemote.transact(ON_MOVE_TRANSACTION, data, reply, 0);213 DatabaseUtils.readExceptionFromParcel(reply);214 } finally {215 data.recycle();216 reply.recycle();217 }218 }219220 public int count() throws RemoteException221 {222 Parcel data = Parcel.obtain();223 Parcel reply = Parcel.obtain();224 try {225 data.writeInterfaceToken(IBulkCursor.descriptor);226227 boolean result = mRemote.transact(COUNT_TRANSACTION, data, reply, 0);228 DatabaseUtils.readExceptionFromParcel(reply);229230 int count;231 if (result == false) {232 count = -1;233 } else {234 count = reply.readInt();235 }236 return count;237 } finally {238 data.recycle();239 reply.recycle();240 }241 } In BulkCursorNative56 @Override57 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)58 throws RemoteException {59 try {60 switch (code) {61 case GET_CURSOR_WINDOW_TRANSACTION: {62 data.enforceInterface(IBulkCursor.descriptor);63 int startPos = data.readInt();64 CursorWindow window = getWindow(startPos);65 reply.writeNoException();66 if (window == null) {67 reply.writeInt(0);68 } else {69 reply.writeInt(1);70 window.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);71 }72 return true;73 }7475 case COUNT_TRANSACTION: {76 data.enforceInterface(IBulkCursor.descriptor);77 int count = count();78 reply.writeNoException();79 reply.writeInt(count);80 return true;81 }} }
public abstract class BulkCursorNative extends Binder implements IBulkCursor, while public final class CursorToBulkCursorAdaptor extends BulkCursorNative
In CursorToBulkCursorAdaptor134 @Override135 public CursorWindow getWindow(int startPos) {136 synchronized (mLock) {137 throwIfCursorIsClosed();138139 if (!mCursor.moveToPosition(startPos)) {// will cause SQLiteCursor create CursorWindow140 closeFilledWindowLocked();// close Adaptor’s self mFilledWindow if exsiting141 return null;142 }143144 CursorWindow window = mCursor.getWindow(); // get SQLiteCursor’s mWindow145 if (window != null) { // when mCursor.moveToPosition is called, clearOrCreateWindow-ed146 closeFilledWindowLocked();// close Adaptor’s self mFilledWindow if exsiting147 } else {// create the window as mFilledWindow if SQLiteCursor has not its Window148 window = mFilledWindow;149 if (window == null) {150 mFilledWindow = new CursorWindow(mProviderName);151 window = mFilledWindow;152 mCursor.fillWindow(startPos, window);// SQLiteCursor fill passed-in window??153 } else if (startPos < window.getStartPosition()154 || startPos >= window.getStartPosition() + window.getNumRows()) {155 window.clear();156 mCursor.fillWindow(startPos, window);157 }158 }159160 // Acquire a reference before returning from this RPC.161 // The Binder proxy will decrement the reference count again as part of writing162 // the CursorWindow to the reply parcel as a return value.163 if (window != null) {164 window.acquireReference();165 }166 return window;167 }168 }
Note the comment in CursorToBulkCursorAdaptor,
26 * Wraps a BulkCursor around an existing Cursor making it remotable.
27 * <p>
28 * If the wrapped cursor returns non-null from {@link CrossProcessCursor#getWindow}
29 * then it is assumed to own the window. Otherwise, the adaptor provides a
30 * window to be filled and ensures it gets closed as needed during deactivation
31 * and requeries.
32 * </p>
mCursor is instance of SQLiteCursor .
In most cases, SQLiteCursor’s CursorWindow is used, so SQLiteCursor::fileWindow is called, same flow with cs same process case.
If SQLiteCursor’s CursorWindow is null, why? Maybe some othrer database rather than SQLite.
A new CursorWindow will be created as CursorToBulkCursorAdaptor::mFilledWindow and is passed to database to fill the window.
191 @Override192 public void AbstractCursor::fillWindow(int position, CursorWindow window) {193 DatabaseUtils.cursorFillWindow(this, position, window);194 }261 public static void cursorFillWindow(final Cursor cursor,262 int position, final CursorWindow window) {263 if (position < 0 || position >= cursor.getCount()) {264 return;265 }266 window.acquireReference();267 try {268 final int oldPos = cursor.getPosition();269 final int numColumns = cursor.getColumnCount();270 window.clear();271 window.setStartPosition(position);272 window.setNumColumns(numColumns);273 if (cursor.moveToPosition(position)) {274 do {275 if (!window.allocRow()) {276 break;277 }…………………309 if (!success) {310 window.freeLastRow();311 break;312 }313 }314 position += 1;315 } while (cursor.moveToNext());316 }317 cursor.moveToPosition(oldPos);318 } catch (IllegalStateException e){319 // simply ignore it320 } finally {321 window.releaseReference();322 }323 }cursor.getCount() is called.
END
- Concepts of Cursor and CursorWindow on android platform
- Bjarne Stroustrup Expounds on Concepts and the Future of C++
- Concepts, Techniques, and Models of Computer Programming
- All kind of Concepts: in Android ...
- remount on android platform command
- USB audio on Android platform
- Introduction to Adaptive Cursor Sharing Concepts and Multimedia Demo [Video] (文档 ID 1115994.1)
- Test Driven Development and GUI Testing on the Android platform: Temperature Converter sample
- Android Dev Intro - Some Concepts on Android Graphics Architecture
- android Platform Styles and Themes
- single-task message and cursor pin s on x
- CursorWindow can not be created due to cursor [memory] leak
- CursorWindow can not be created due to cursor [memory] leak
- Concepts of OpenGoo
- Basic Concepts of Statistic
- Machine learning of neural representations of suicide and emotion concepts identifies suicidal youth
- Mobile Python: Rapid prototyping of applications on the mobile platform
- Introduction of the FLOW On the SRX securtiy platform
- 10位IT领袖给应届毕业生的10条忠告
- 强类型引起的麻烦吗?
- iOS开发入门之UIPickerView控件的简单使用
- 创业,建立在顾客的痛点之上
- ffmpeg与RTMP流媒体连接用法
- Concepts of Cursor and CursorWindow on android platform
- 互联网的回归7:基于API的互联网
- killall 命令, 很有用的(转)
- declare命令
- 网络子系统关键函数分析(未完待续)
- pdf文档操作--写操作 PdfUtils java
- 如何修改微软人体工学键盘的Zoom键
- 王爽《汇编语言》学习笔记--第五章+第六章
- PMP认证