Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ffdfa0c authored by Brad Fitzpatrick's avatar Brad Fitzpatrick
Browse files

Speed up ContentProvider.query() in simple case by ~30%

When query() uses bulkQuery() and we know we're going to need some
metadata right afterwards (number of rows and column index of _id, if
present), just asked for it in the initial binder transaction instead
of immediately fetching it again.

Also, this defers loading column names until the client asks for them.

This gets down the simpler (and very common) use cases of
ContentProvider.query() down to 3 binder calls:

   QUERY_TRANSACTION to android.content.ContentProvider$Transport
   GET_CURSOR_WINDOW_TRANSACTION to android.database.CursorToBulkCursorAdaptor
   CLOSE_TRANSACTION to android.database.CursorToBulkCursorAdaptor

More can still be done, but this is a good bite-sized first piece.

Change-Id: I7ad45949f53e0097ff18c2478d659f0f36929693
parent 33d1fdd6
Loading
Loading
Loading
Loading
+54 −9
Original line number Diff line number Diff line
@@ -73,7 +73,10 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
                case QUERY_TRANSACTION:
                {
                    data.enforceInterface(IContentProvider.descriptor);

                    Uri url = Uri.CREATOR.createFromParcel(data);

                    // String[] projection
                    int num = data.readInt();
                    String[] projection = null;
                    if (num > 0) {
@@ -82,6 +85,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
                            projection[i] = data.readString();
                        }
                    }

                    // String selection, String[] selectionArgs...
                    String selection = data.readString();
                    num = data.readInt();
                    String[] selectionArgs = null;
@@ -91,19 +96,33 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
                            selectionArgs[i] = data.readString();
                        }
                    }

                    String sortOrder = data.readString();
                    IContentObserver observer = IContentObserver.Stub.
                        asInterface(data.readStrongBinder());
                    CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);

                    // Flag for whether caller wants the number of
                    // rows in the cursor and the position of the
                    // "_id" column index (or -1 if non-existent)
                    // Only to be returned if binder != null.
                    boolean wantsCursorMetadata = data.readInt() != 0;

                    IBulkCursor bulkCursor = bulkQuery(url, projection, selection,
                            selectionArgs, sortOrder, observer, window);
                    reply.writeNoException();
                    if (bulkCursor != null) {
                        reply.writeStrongBinder(bulkCursor.asBinder());

                        if (wantsCursorMetadata) {
                            reply.writeInt(bulkCursor.count());
                            reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex(
                                bulkCursor.getColumnNames()));
                        }
                    } else {
                        reply.writeStrongBinder(null);
                    }

                    return true;
                }

@@ -266,9 +285,12 @@ final class ContentProviderProxy implements IContentProvider
        return mRemote;
    }

    public IBulkCursor bulkQuery(Uri url, String[] projection,
            String selection, String[] selectionArgs, String sortOrder, IContentObserver observer,
            CursorWindow window) throws RemoteException {
    // Like bulkQuery() but sets up provided 'adaptor' if not null.
    private IBulkCursor bulkQueryInternal(
        Uri url, String[] projection,
        String selection, String[] selectionArgs, String sortOrder,
        IContentObserver observer, CursorWindow window,
        BulkCursorToCursorAdaptor adaptor) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();

@@ -297,6 +319,12 @@ final class ContentProviderProxy implements IContentProvider
        data.writeStrongBinder(observer.asBinder());
        window.writeToParcel(data, 0);

        // Flag for whether or not we want the number of rows in the
        // cursor and the position of the "_id" column index (or -1 if
        // non-existent).  Only to be returned if binder != null.
        final boolean wantsCursorMetadata = (adaptor != null);
        data.writeInt(wantsCursorMetadata ? 1 : 0);

        mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);

        DatabaseUtils.readExceptionFromParcel(reply);
@@ -305,6 +333,14 @@ final class ContentProviderProxy implements IContentProvider
        IBinder bulkCursorBinder = reply.readStrongBinder();
        if (bulkCursorBinder != null) {
            bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);

            if (wantsCursorMetadata) {
                int rowCount = reply.readInt();
                int idColumnPosition = reply.readInt();
                if (bulkCursor != null) {
                    adaptor.set(bulkCursor, rowCount, idColumnPosition);
                }
            }
        }

        data.recycle();
@@ -313,18 +349,27 @@ final class ContentProviderProxy implements IContentProvider
        return bulkCursor;
    }

    public IBulkCursor bulkQuery(Uri url, String[] projection,
            String selection, String[] selectionArgs, String sortOrder, IContentObserver observer,
            CursorWindow window) throws RemoteException {
        return bulkQueryInternal(
            url, projection, selection, selectionArgs, sortOrder,
            observer, window,
            null /* BulkCursorToCursorAdaptor */);
    }

    public Cursor query(Uri url, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) throws RemoteException {
        //TODO make a pool of windows so we can reuse memory dealers
        CursorWindow window = new CursorWindow(false /* window will be used remotely */);
        BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
        IBulkCursor bulkCursor = bulkQuery(url, projection, selection, selectionArgs, sortOrder,
                adaptor.getObserver(), window);

        IBulkCursor bulkCursor = bulkQueryInternal(
            url, projection, selection, selectionArgs, sortOrder,
            adaptor.getObserver(), window,
            adaptor);
        if (bulkCursor == null) {
            return null;
        }
        adaptor.set(bulkCursor);
        return adaptor;
    }

+35 −10
Original line number Diff line number Diff line
@@ -46,18 +46,36 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {

            // Search for the rowID column index and set it for our parent
            mColumns = mBulkCursor.getColumnNames();
            int length = mColumns.length;
            for (int i = 0; i < length; i++) {
                if (mColumns[i].equals("_id")) {
                    mRowIdColumnIndex = i;
                    break;
                }
            }
            mRowIdColumnIndex = findRowIdColumnIndex(mColumns);
        } catch (RemoteException ex) {
            Log.e(TAG, "Setup failed because the remote process is dead");
        }
    }

    /**
     * Version of set() that does fewer Binder calls if the caller
     * already knows BulkCursorToCursorAdaptor's properties.
     */
    public void set(IBulkCursor bulkCursor, int count, int idIndex) {
        mBulkCursor = bulkCursor;
        mColumns = null;  // lazily retrieved
        mCount = count;
        mRowIdColumnIndex = idIndex;
    }

    /**
     * Returns column index of "_id" column, or -1 if not found.
     */
    public static int findRowIdColumnIndex(String[] columnNames) {
        int length = columnNames.length;
        for (int i = 0; i < length; i++) {
            if (columnNames[i].equals("_id")) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Gets a SelfDataChangeOberserver that can be sent to a remote
     * process to receive change notifications over IPC.
@@ -190,6 +208,14 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {

    @Override
    public String[] getColumnNames() {
        if (mColumns == null) {
            try {
                mColumns = mBulkCursor.getColumnNames();
            } catch (RemoteException ex) {
                Log.e(TAG, "Unable to fetch column names because the remote process is dead");
                return null;
            }
        }
        return mColumns;
    }

@@ -255,4 +281,3 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor {
        }
    }
}
+3 −5
Original line number Diff line number Diff line
@@ -30,8 +30,7 @@ import java.util.Map;
 *
 * {@hide}
 */
public interface IBulkCursor extends IInterface 
{
public interface IBulkCursor extends IInterface  {
    /**
     * Returns a BulkCursorWindow, which either has a reference to a shared
     * memory segment with the rows, or an array of JSON strings.
@@ -87,4 +86,3 @@ public interface IBulkCursor extends IInterface
    static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10;
    static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 11;
}
+8 −7
Original line number Diff line number Diff line
@@ -19,11 +19,12 @@ package android.database.sqlite;
import android.database.CursorWindow;

/**
 * An object create from a SQLiteDatabase that can be closed.
 * An object created from a SQLiteDatabase that can be closed.
 */
public abstract class SQLiteClosable {
    private int mReferenceCount = 1;
    private Object mLock = new Object();

    protected abstract void onAllReferencesReleased();
    protected void onAllReferencesReleasedFromContainer() {}

+1 −1

File changed.

Contains only whitespace changes.