Source Code Analysis of Android Cursor

Keywords: Database SQL Windows Android

1. Purpose of this paper

Android ContentProvider provides a mechanism for data exchange between processes. and data base The query of the query is the application of this mechanism. So what is Cursor that app gets by querying the database through Uri? Why can we provide data for another process? In this paper, getContentResolver().query(... ) Function as a starting point, a comprehensive analysis of Cursor family relationship class diagram, clarify the mechanism of cross-process communication Cursor.

1.1 Client's Cursor Object

Assuming that there is a Content Provider in process B, process A queries the Content Provider through Uri to get a Cursor. The possible code is as follows:

  1. ContentResolver cr = mContext.getContentResolver();//mContext is a Context object  
  2. Cursor cs = cr.query(uri,null,null,null,null);  

In order to know what Cursor really looks like from the above code, we need to look at the way query is invoked. The query function is implemented as follows:

  1. public final Cursor query(final Uri uri, String[] projection,  
  2.         String selection, String[] selectionArgs, String sortOrder,  
  3.         CancellationSignal cancellationSignal) {  
  4.     IContentProvider unstableProvider = acquireUnstableProvider(uri);  
  5.     //Eliminate irrelevant code  
  6.     try {  
  7.         //Eliminate irrelevant code  
  8.         Cursor qCursor;  
  9.         try {  
  10.             qCursor = unstableProvider.query(uri, projection,  
  11.                     selection, selectionArgs, sortOrder, remoteCancellationSignal);  
  12.         } catch (DeadObjectException e) {  
  13.           //Eliminate irrelevant code  
  14.         }  
  15.         if (qCursor == null) {  
  16.             return null;  
  17.         }  
  18.        //Eliminate irrelevant code  
  19.         CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,  
  20.                 stableProvider != null ? stableProvider : acquireProvider(uri));  
  21.         stableProvider = null;  
  22.         return wrapper;  
  23.     } catch (RemoteException e) {  
  24.        //Eliminate irrelevant code  
  25.     } finally {  
  26.       //Eliminate irrelevant code  
  27.     }  




















In order to facilitate analysis, the code is omitted. As can be seen from the above code:

1. Cursor returned by query function is a CursorWrapperInner object.

2. CursorWrapper Inner is a wrapper class constructed by the Cursor object returned by the query function of IContentProvider.

So there are two questions:

1.IContentProvider's query function returns Cursor. What is the real object?

2. What role does the CursorWrapperInner class play? Why take Cursor returned from the query function of IContentProvider as a parameter to construct a new CursorWrapperInner?

First, let's look at the class diagram:

From the above class diagram, we can see that:

CursorWrapper Inner is an internal class of ContentResolver, inherited from CrossProcess CursorWrapper. CrossProcess Cursor Wrapper is by name a Cursor wrapper class that implements cross-process communication. This is also verified from the class diagram. CrossProcess Cursor Wrapper inherits from Cursor Wrapper and implements the CrossProcess Cursor interface. CursorWrapper is a wrapper class that implements all the functions of the Cursor interface. It contains a mCursor member variable that points to a Cursor interface, so you can know that this is a proxy mode. CursorWrapper delegates its internal mCursor object to implement all Cursor functional interfaces. CrossProcess Cursor inherits from Cursor, which is mainly used for cross-process communication.

In summary, we can now know that CrossProcess Cursor Wrapper implements all Cursor interfaces, but the completion of these interface functions is delegated to mCursor within its parent class. So what is the real object of mCursor? For the time being, mCursor should be an object that implements CrossProcess Cursor.

Summary: The real object that the client gets is the Cursor WarpprtInner class.

1.2 The true face of Cursor within CursorWrapper

As we know from the previous section, Cursor Wrapper Inner will ultimately delegate Cursor Wrapper to complete the actual functionality through the proxy mode. Now let's look at the real face of mCursor inside CursorWrapper. MCursor comes from the Cursor object returned by the query function of IContentProvider. So what is this Cursor object? Let's look at the implementation of the query function of IContentProvider. IContentProvider is actually a ContentProviderProxy object. It is a proxy class and also a Binder's Bp-side. It packages the parameters of function calls and sends them to ContentProvider Native. Eventually, ContentProvider Native calls the specific functions of ContentProvider.

Content Provider Proxy, Content Provider Native, Content Provider and I ContentProvider have the following relationships:

Following is the query implementation of ContentProviderProxy:

  1. public Cursor query(Uri url, String[] projection, String selection,  
  2.         String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)  
  3.                 throws RemoteException {  
  4.     BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();  
  5.     Parcel data = Parcel.obtain();  
  6.     Parcel reply = Parcel.obtain();  
  7.     try {  
  8.         data.writeInterfaceToken(IContentProvider.descriptor);  
  9.   
  10.         url.writeToParcel(data, 0);  
  11.         int length = 0;  
  12.         if (projection != null) {  
  13.             length = projection.length;  
  14.         }  
  15.         data.writeInt(length);  
  16.         for (int i = 0; i < length; i++) {  
  17.             data.writeString(projection[i]);  
  18.         }  
  19.         data.writeString(selection);  
  20.         if (selectionArgs != null) {  
  21.             length = selectionArgs.length;  
  22.         } else {  
  23.             length = 0;  
  24.         }  
  25.         data.writeInt(length);  
  26.         for (int i = 0; i < length; i++) {  
  27.             data.writeString(selectionArgs[i]);  
  28.         }  
  29.         data.writeString(sortOrder);  
  30.         data.writeStrongBinder(adaptor.getObserver().asBinder());  
  31.         data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);  
  32.   
  33.         mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);  
  34.   
  35.         DatabaseUtils.readExceptionFromParcel(reply);  
  36.   
  37.         if (reply.readInt() != 0) {  
  38.             BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);  
  39.             adaptor.initialize(d);  
  40.         } else {  
  41.             adaptor.close();  
  42.             adaptor = null;  
  43.         }  
  44.        return adaptor;  
  45.     } catch (RemoteException ex) {  
  46.         adaptor.close();  
  47.         throw ex;  
  48.     } catch (RuntimeException ex) {  
  49.         adaptor.close();  
  50.         throw ex;  
  51.     } finally {  
  52.         data.recycle();  
  53.         reply.recycle();  
  54.     }  
  55. }  
As you can see from the above code, the real object of Cursor returned by the query function is BulkCursorToCursorAdaptor. Within the query function, query requests are passed to the ContentProvider Native end through the transact function. A Parcel will be returned after the transact is executed Reply. Construct a Bulk Cursor Descriptor from reply. The BulkCursorToCursorAdaptor is then initialized by this BulkCursorDescriptor. A fairly important assignment operation in the Bulk CursorToCursorAdaptor is as follows:

  1. public void initialize(BulkCursorDescriptor d) {  
  2.     mBulkCursor = d.cursor;  
  3.     mColumns = d.columnNames;  
  4.     mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);  
  5.     mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls;  
  6.     mCount = d.count;  
  7.     if (d.window != null) {  
  8.         setWindow(d.window);  
  9.     }  
  10. }  

d.window is a CursorWindow s object. This object actually represents a shared memory that stores the queried result set. For further details, please refer to: http://blog.csdn.net/ifloveelse/article/details/28394103 . mBulkCursor is an IBulkCursor interface. This interface plays the role of data transmission. At this point, Cursor's "family space" is more detailed:


Summary: Questions raised at the beginning of this section: Who is the real face of Cursor inside Cursor Wrapper? Now the answer is: Bulk Cursor To Cursor Adaptor. Named as an adapter, it implements Cursor's functional interface through conversion and calls IBulk Cursor. Since then, the above class diagram is the whole relationship of Cursor in the APP process. So what does IBulk Cursor do? The next section explains.

1.3 IBulk Cursor family

The mBulk Cursor of the Bulk CursorToCursor Adaptor comes from the return value of ContentProvider Native. To figure out what IBulk Cursor really looks like, look at the implementation of Content Provider Native.

  1.     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)  
  2.             throws RemoteException {  
  3.         try {  
  4.             switch (code) {  
  5.                 case QUERY_TRANSACTION:  
  6.                 {  
  7.                     data.enforceInterface(IContentProvider.descriptor);  
  8.   
  9.                     Uri url = Uri.CREATOR.createFromParcel(data);  
  10.   
  11.                     // String[] projection  
  12.                     int num = data.readInt();  
  13.                     String[] projection = null;  
  14.                     if (num > 0) {  
  15.                         projection = new String[num];  
  16.                         for (int i = 0; i < num; i++) {  
  17.                             projection[i] = data.readString();  
  18.                         }  
  19.                     }  
  20.   
  21.                     // String selection, String[] selectionArgs...  
  22.                     String selection = data.readString();  
  23.                     num = data.readInt();  
  24.                     String[] selectionArgs = null;  
  25.                     if (num > 0) {  
  26.                         selectionArgs = new String[num];  
  27.                         for (int i = 0; i < num; i++) {  
  28.                             selectionArgs[i] = data.readString();  
  29.                         }  
  30.                     }  
  31.   
  32.                     String sortOrder = data.readString();  
  33.                     IContentObserver observer = IContentObserver.Stub.asInterface(  
  34.                             data.readStrongBinder());  
  35.                     ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(  
  36.                             data.readStrongBinder());  
  37.   
  38.                     Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,  
  39.                             cancellationSignal);  
  40.                     if (cursor != null) {  
  41.                         CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(  
  42.                                 cursor, observer, getProviderName());  
  43.                         BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();  
  44.   
  45.                         reply.writeNoException();  
  46.                         reply.writeInt(1);  
  47.                         d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);  
  48.                     } else {  
  49.                         reply.writeNoException();  
  50.                         reply.writeInt(0);  
  51.                     }  
  52.   
  53.                     return true;  
  54.                 }  
  55.             }  
  56. ............  

The above code is the onTransact function in ContentProviderNative for query processing. In this part of the code, another CursorToBulkCursorAdaptor object appears. The constructor of this object is a Cursor object. So who is the Cursor object? Logic is getting more and more complicated. Cursor is returned by the query function. It is available from the class diagram of ContentProvider provided in Section 1.2, which calls the query function of ContentProvider. So where did Cursor come from in ContentProvider's query function? Here's the answer: SQLiteDatabase. ContentProvider is an abstract class, and we need to implement the query function ourselves. Generally, in query, we query a custom database through SQLiteDatabase and get a Cursor object. This process is omitted. What we need to know is that the query function of the SQLiteDatabase returns a Cursor. This Cursor is used to construct a CursorToBulkCursorAdaptor object.

Let's take a look at the real face of Cursor returned by SQLiteDatabase. The following is the final call function of query in SQLiteDatabase. Specific code can refer to SQLiteDatabase. Java Document:

  1. public Cursor rawQueryWithFactory(  
  2.         CursorFactory cursorFactory, String sql, String[] selectionArgs,  
  3.         String editTable, CancellationSignal cancellationSignal) {  
  4.     acquireReference();  
  5.     try {  
  6.         SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,  
  7.                 cancellationSignal);  
  8.         return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,  
  9.                 selectionArgs);  
  10.     } finally {  
  11.         releaseReference();  
  12.     }  
  13. }  
From the above code, Cursor comes from query of SQLiteDirect Cursor Driver. Let's look at its query implementation:

  1. public Cursor query(CursorFactory factory, String[] selectionArgs) {  
  2.     final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);  
  3.     final Cursor cursor;  
  4.     try {  
  5.         query.bindAllArgsAsStrings(selectionArgs);  
  6.   
  7.         if (factory == null) {  
  8.             cursor = new SQLiteCursor(this, mEditTable, query);  
  9.         } else {  
  10.             cursor = factory.newCursor(mDatabase, this, mEditTable, query);  
  11.         }  
  12.     } catch (RuntimeException ex) {  
  13.         query.close();  
  14.         throw ex;  
  15.     }  
  16.   
  17.     mQuery = query;  
  18.     return cursor;  
  19. }  

Here we assume that factory is empty, so the Cursor here finally reveals its true face: SQLite Cursor. It's really hard to follow up all the way. The water falls to the rocks!

Through the following class diagram, we organize our ideas:


As can be seen from the figure, the mBulkCursor of the member variable of the BulkCursorToCursorAdaptor is an IBuilCursor interface whose real object is actually a BulkCursorProxy. Bulk Cursor Proxy is both a proxy and a Bp-side, which forwards function call requests to the Bn-side Bulk Cursor Native in another process through Binder communication. In the figure, the green line box part runs in the app process, and the red line box part runs in the ContentProvider process.

The real object of mCursor in CursorToBulk Cursor Adaptor is also revealed: SQLiteCursor. It is also known by name that this object is related to SQLLite. It has an internal SQLiteQuery, responsible for database queries and the establishment of Cursor Windows. Cursor Windows is an abstraction of shared memory at the intersection of red and green boxes. There is a mapping in both processes.

Since then, Cursor's analysis has all ended.



Original address: http://blog.csdn.net/hlglinglong/article/details/40431633

Posted by yes3no2 on Sun, 02 Jun 2019 13:50:29 -0700