summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@android.com>2010-03-09 13:18:02 -0800
committerBrad Fitzpatrick <bradfitz@android.com>2010-03-09 15:20:21 -0800
commit9ffdfa0c238fce3b85741d7f6828fd484cd8f195 (patch)
treeb187b4936d33363f652e5d0cd5d1d2e9bfa577b7
parent33d1fdd6aa9ac9b59295756f151a49f2cf520691 (diff)
downloadframeworks_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
-rw-r--r--core/java/android/content/ContentProviderNative.java63
-rw-r--r--core/java/android/database/BulkCursorToCursorAdaptor.java45
-rw-r--r--core/java/android/database/CursorWindow.java2
-rw-r--r--core/java/android/database/IBulkCursor.java8
-rw-r--r--core/java/android/database/sqlite/SQLiteClosable.java15
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() {