An analysis of Android cursor

Source: Internet
Author: User

1. The purpose of this article

Android ContentProvider provides a mechanism for inter-process data exchange. And the database query is the application of this mechanism. So what is the cursor that the app gets by querying the database with a URI? Why can we provide data for another process? This article takes Getcontentresolver (). Query (...) function as the starting point, a comprehensive analysis of the cursor family Relationship diagram, to clarify the cursor cross-process communication mechanism.

1.1 Client's Cursor object

Suppose there is a contentprovider,a process in the B process that queries the ContentProvider through a URI to get a cursor. The possible code is as follows:

Contentresolver CR = Mcontext.getcontentresolver ();//mcontext is a context object cursor CS = cr.query (uri,null,null,null, NULL);

In order to know the actual appearance of the cursor from the above code, we need to look at the invocation path of the above query. The query function is implemented as follows:

    Public final Cursor query (final Uri URI, string[] projection, String selection, string[] Selectionargs, STR ing SortOrder, cancellationsignal cancellationsignal) {Icontentprovider Unstableprovider = Acquireunsta        Bleprovider (URI);            Omit unrelated code try {//Omit extraneous code Cursor qcursor; try {qcursor = Unstableprovider.query (URI, projection, selection, Selectionargs, so            Rtorder, remotecancellationsignal); } catch (Deadobjectexception e) {//Omit extraneous code} if (qcursor = = null) {Retu            RN null; }//Omit extraneous code Cursorwrapperinner wrapper = new Cursorwrapperinner (Qcursor, Stableprov Ider! = null?            Stableprovider:acquireprovider (URI));            Stableprovider = null;        return wrapper; } catch (RemoteException e) {//Omit extraneous code} finally {//Omit extraneous code}} 

For ease of analysis, irrelevant code is omitted. From the above code can be seen:

1. The cursor returned by the query function is a Cursorwrapperinner object;

2.CursorWrapperInner is a wrapper class that is constructed from the cursor object returned by the Icontentprovider query function;

Then there are two questions:

The 1.IContentProvider query function returns the cursor, what is the real object?

What does the 2.CursorWrapperInner class do, and why should I construct a cursorwrapperinner from the cursor as the parameter returned by the Icontentprovider query function?

First look at the class diagram:

From the above-mentioned class diagram you can learn:

Cursorwrapperinner is the inner class of Contentresolver and inherits from Crossprocesscursorwrapper. Crossprocesscursorwrapper from the name is a cursor wrapper class that implements cross-process communication. This is also verified from the class diagram, and Crossprocesscursorwrapper inherits from Cursorwrapper, which implements the Crossprocesscursor interface. While 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 tell that this is a proxy mode. Cursorwrapper delegates its internal Mcursor object to implement all the cursor function interfaces. Crossprocesscursor inherits from the cursor, which is primarily used for cross-process communication.

In summary, we can now know that Crossprocescursorwrapper implements all cursor interfaces, but the completion of these interface functions is all delegated to the internal mcursor of its parent class to complete. So what is the real object of Mcursor? Presumably, Mcursor should be an object that implements Crossprocesscursor.

Summary: The real object of the cursor that the client gets is: the Cursorwarpprtinner class.

1.2 Cursorwrapper inside the cursor true face

From the previous section we already know that through the proxy mode, Cursorwrapperinner will eventually entrust Cursorwrapper to complete the actual function. Now look at the true face of the mcursor inside Cursorwrapper. Mcursor the cursor object returned by the query function from Icontentprovider. So what is this cursor object? Then look at the implementation of the query function of Icontentprovider. The Icontentprovider is actually a Contentproviderproxy object. It is a proxy class, also a binder of the BP side, the function calls the parameters of the package sent to Contentprovidernative, and ultimately by the contentprovidernative to call ContentProvider specific functions.

Contentproviderproxy,contentprovidernative,contentprovider,icontentprovider's relationship is as follows:

Here is the query implementation of Contentproviderproxy:

    Public Cursor query (Uri URL, string[] projection, string selection, string[] Selectionargs, String Sortorde R, Icancellationsignal cancellationsignal) throws remoteexception {Bulkcursortocursoradaptor ad        Aptor = new Bulkcursortocursoradaptor ();        Parcel data = Parcel.obtain ();        Parcel reply = Parcel.obtain ();            try {data.writeinterfacetoken (icontentprovider.descriptor);            Url.writetoparcel (data, 0);            int length = 0;            if (projection! = null) {length = Projection.length;            } data.writeint (length);            for (int i = 0; i < length; i++) {data.writestring (projection[i]);            } data.writestring (selection);            if (Selectionargs! = null) {length = Selectionargs.length;            } else {length = 0;            } data.writeint (length); for (int i = 0; I < leNgth;            i++) {data.writestring (selectionargs[i]);            } data.writestring (SortOrder);            Data.writestrongbinder (Adaptor.getobserver (). Asbinder ());            Data.writestrongbinder (cancellationsignal! = null? Cancellationsignal.asbinder (): null);            Mremote.transact (icontentprovider.query_transaction, data, reply, 0);            Databaseutils.readexceptionfromparcel (reply);                if (reply.readint () = 0) {Bulkcursordescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel (reply);            Adaptor.initialize (d);                } else {adaptor.close ();            adaptor = null;        } return adaptor;            } catch (RemoteException ex) {adaptor.close ();        Throw ex;            } catch (RuntimeException ex) {adaptor.close ();        Throw ex;            } finally {data.recycle ();        Reply.recycle (); }    }
From the code above, the real object of the cursor returned by the query function is bulkcursortocursoradaptor. Inside the query function, passTransactThe function passes the query request to the contentprovidernative side.TransactA parcel reply is returned when execution is complete. Construct a reply from theBulkcursordescriptor。 And then by thisBulkcursordescriptorInitializes the Bulkcursortocursoradaptor. A fairly important assignment in Bulkcursortocursoradaptor is as follows:

    public void Initialize (Bulkcursordescriptor d) {        mbulkcursor = d.cursor;        Mcolumns = D.columnnames;        Mrowidcolumnindex = Databaseutils.findrowidcolumnindex (mcolumns);        Mwantsallonmovecalls = D.wantsallonmovecalls;        MCount = D.count;        if (D.window! = null) {            SetWindow (D.window);        }    }

D.window is a Cursorwindow object. This object actually represents a piece of shared memory that stores the result set after the query. For details, see: http://blog.csdn.net/ifloveelse/article/details/28394103. And Mbulkcursor is a ibulkcursor interface. This interface acts as a data transfer. In this step, the cursor's "family picture" is more detailed:


Summary: Questions raised at the beginning of this section: Who is the true face of the cursor inside cursorwrapper? Now also can answer: Bulkcursortocursoradaptor. From the name, this is an adapter that implements the cursor's functional interface by converting it, calling Ibulkcursor. Since then, this class diagram above is the entire relationship of the cursor in the app-side process. So what does Ibulkcursor do? The next section explains.

1.3 Ibulkcursor Family

The mbulkcursor of Bulkcursortocursoradaptor is derived from the return value of contentprovidernative. In order to understand the true face of Ibulkcursor, but also to see the realization of contentprovidernative.

    public boolean ontransact (int code, PARCEL data, Parcel reply, int flags) throws RemoteException {T ry {switch (code) {case Query_transaction: {data.enforceint                    Erface (Icontentprovider.descriptor);                    Uri URL = Uri.CREATOR.createFromParcel (data);                    string[] projection int num = Data.readint ();                    string[] projection = null;                        if (num > 0) {projection = new String[num];                        for (int i = 0; i < num; i++) {Projection[i] = data.readstring ();                    }}//String selection, string[] Selectionargs ...                    String selection = data.readstring ();                    num = Data.readint ();                    string[] Selectionargs = null;              if (num > 0) {          Selectionargs = new String[num];                        for (int i = 0; i < num; i++) {Selectionargs[i] = data.readstring ();                    }} String SortOrder = Data.readstring ();                    Icontentobserver observer = IContentObserver.Stub.asInterface (Data.readstrongbinder ()); Icancellationsignal cancellationsignal = ICancellationSignal.Stub.asInterface (da                    Ta.readstrongbinder ()); cursor cursor = query (URL, projection, selection, Selectionargs, SortOrder, cancellationsignal)                    ;                                if (cursor! = NULL) {Cursortobulkcursoradaptor adaptor = new Cursortobulkcursoradaptor (                        Cursor, observer, Getprovidername ());                    Bulkcursordescriptor d = adaptor.getbulkcursordescriptor ();    Reply.writenoexception ();                        Reply.writeint (1);                    D.writetoparcel (reply, parcelable.parcelable_write_return_value);                        } else {reply.writenoexception ();                    Reply.writeint (0);                } return true; } }............

The above code is the processing of query by the Ontransact function in contentprovidernative. In this part of the code, there's aCursortobulkcursoradaptorObject. The constructor for this object is a cursor object. So who is this cursor object? The logic is getting more and more complicated. The cursor is returned by the query function. The ContentProvider class diagram provided in section 1.2, which is called by the query function of ContentProvider, is available. So where does the cursor in the ContentProvider query function come from? Here's a straight answer: Sqlitedatabase. ContentProvider is an abstract class that we need to implement in our own query function. In general, in query, we get a cursor object by querying the custom database with Sqlitedatabase. This process is omitted. What we need to know is that the Sqlitedatabase query function returns a cursor. This cursor is used to construct aCursortobulkcursoradaptorObject.

Here's a look at the true face of the cursor returned by Sqlitedatabase. The following is the final call function for query in Sqlitedatabase. The specific code can refer to the Sqlitedatabase.java file:

    Public Cursor rawquerywithfactory (            cursorfactory cursorfactory, String sql, string[] Selectionargs,            string Edittable, cancellationsignal cancellationsignal) {        acquirereference ();        try {            Sqlitecursordriver driver = new Sqlitedirectcursordriver (this, SQL, edittable,                    cancellationsignal);            Return Driver.query (cursorfactory! = null? cursorfactory:mcursorfactory,                    Selectionargs);        } finally {            Releasereference ();        }    }
From the code above, the cursor is from the Sqlitedirectcursordriver query. Let's look at the implementation of its query:

    Public Cursor query (Cursorfactory Factory, string[] Selectionargs) {        final sqlitequery query = new Sqlitequery (mdatab ASE, MSQL, mcancellationsignal);        final cursor cursor;        try {            query.bindallargsasstrings (Selectionargs);            if (factory = = null) {                cursor = new Sqlitecursor (this, medittable, query);            } else {                cursor = Factory.newcursor (Mdatabase, this, medittable, query);            }        } catch (RuntimeException ex) {            query.close ();            Throw ex;        }        mquery = query;        return cursor;    }

Here we assume that factory is empty, then the cursor here finally reveals its true face:Sqlitecursor。 The track is really hard, to the bottom!

Let's organize our ideas by following the class diagram:


It can be seen that the mbulkcursor of the member variable of Bulkcursortocursoradaptor is a ibuilcursor interface, and its real object is actually a bulkcursorproxy. Bulkcursorproxy is a proxy and a BP side that forwards function call requests to the BN-side bulkcursornative in another process through binder communication. In the diagram, the Green Line box section runs in the app process, and the Red box section runs in the ContentProvider process.

The real object of Mcursor in Cursortobulkcursoradaptor also revealed: Sqlitecursor. It is also known from the name that this object is related to SQLite. It has an internal sqlitequery, which is responsible for querying the database and establishing the Cursorwindow. The intersection of the red and Green Line boxes Cursorwindow is the abstraction of shared memory. There is a mapping in two processes.

Since then, the analysis of the cursor has all ended.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.