diff options
author | Brad Fitzpatrick <bradfitz@android.com> | 2010-03-09 13:18:02 -0800 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@android.com> | 2010-03-09 15:20:21 -0800 |
commit | 9ffdfa0c238fce3b85741d7f6828fd484cd8f195 (patch) | |
tree | b187b4936d33363f652e5d0cd5d1d2e9bfa577b7 | |
parent | 33d1fdd6aa9ac9b59295756f151a49f2cf520691 (diff) | |
download | frameworks_base-9ffdfa0c238fce3b85741d7f6828fd484cd8f195.zip frameworks_base-9ffdfa0c238fce3b85741d7f6828fd484cd8f195.tar.gz frameworks_base-9ffdfa0c238fce3b85741d7f6828fd484cd8f195.tar.bz2 |
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
5 files changed, 101 insertions, 32 deletions
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index de52e65..fdb3d20 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -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,26 +333,43 @@ 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(); reply.recycle(); - + 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; } diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index cf30dd9..1469ea2 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -43,25 +43,43 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { try { mCount = mBulkCursor.count(); mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls(); - + // 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. - * + * * @return A SelfContentObserver hooked up to this Cursor */ public synchronized IContentObserver getObserver() { @@ -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 { } } } - diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 99db81b..c756825 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -44,7 +44,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { /** * Returns the starting position of this window within the entire * Cursor's result set. - * + * * @return the starting position of this window within the entire * Cursor's result set. */ diff --git a/core/java/android/database/IBulkCursor.java b/core/java/android/database/IBulkCursor.java index 24354fd..46790a3 100644 --- a/core/java/android/database/IBulkCursor.java +++ b/core/java/android/database/IBulkCursor.java @@ -27,11 +27,10 @@ import java.util.Map; * This interface provides a low-level way to pass bulk cursor data across * both process and language boundries. Application code should use the Cursor * interface directly. - * + * * {@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. @@ -60,7 +59,7 @@ public interface IBulkCursor extends IInterface public boolean deleteRow(int position) throws RemoteException; public void deactivate() throws RemoteException; - + public void close() throws RemoteException; public int requery(IContentObserver observer, CursorWindow window) throws RemoteException; @@ -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; } - diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java index e589f34..71fa2c2 100644 --- a/core/java/android/database/sqlite/SQLiteClosable.java +++ b/core/java/android/database/sqlite/SQLiteClosable.java @@ -19,14 +19,15 @@ 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 { +public abstract class SQLiteClosable { private int mReferenceCount = 1; private Object mLock = new Object(); + protected abstract void onAllReferencesReleased(); - protected void onAllReferencesReleasedFromContainer(){} - + protected void onAllReferencesReleasedFromContainer() {} + public void acquireReference() { synchronized(mLock) { if (mReferenceCount <= 0) { @@ -36,7 +37,7 @@ public abstract class SQLiteClosable { mReferenceCount++; } } - + public void releaseReference() { synchronized(mLock) { mReferenceCount--; @@ -45,14 +46,14 @@ public abstract class SQLiteClosable { } } } - + public void releaseReferenceFromContainer() { synchronized(mLock) { mReferenceCount--; if (mReferenceCount == 0) { onAllReferencesReleasedFromContainer(); } - } + } } private String getObjInfo() { |