Skip to content

Commit 825c513

Browse files
author
Jeff Brown
committed
Add a CrossProcessCursorWrapper.
Bug: 5220669 The CrossProcessCursorWrapper is a useful building block for wrapping cursors that will be sent to processes remotely. It can also transform normal Cursors into CrossProcessCursors transparently. The new class fixes common performance problems and bugs that applications have encountered when implementing ContentProviders. Change-Id: Icc59fec10add3f7d8cfbd0495447860c1872c752
1 parent b4009c7 commit 825c513

6 files changed

Lines changed: 138 additions & 16 deletions

File tree

api/current.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6782,6 +6782,13 @@ package android.database {
67826782
method public abstract boolean onMove(int, int);
67836783
}
67846784

6785+
public class CrossProcessCursorWrapper extends android.database.CursorWrapper implements android.database.CrossProcessCursor {
6786+
ctor public CrossProcessCursorWrapper(android.database.Cursor);
6787+
method public void fillWindow(int, android.database.CursorWindow);
6788+
method public android.database.CursorWindow getWindow();
6789+
method public boolean onMove(int, int);
6790+
}
6791+
67856792
public abstract interface Cursor {
67866793
method public abstract void close();
67876794
method public abstract void copyStringToBuffer(int, android.database.CharArrayBuffer);

core/java/android/content/ContentResolver.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import android.content.res.AssetFileDescriptor;
2727
import android.content.res.Resources;
2828
import android.database.ContentObserver;
29+
import android.database.CrossProcessCursorWrapper;
2930
import android.database.Cursor;
3031
import android.database.CursorWrapper;
3132
import android.database.IContentObserver;
@@ -1562,7 +1563,7 @@ private void maybeLogUpdateToEventLog(
15621563
samplePercent);
15631564
}
15641565

1565-
private final class CursorWrapperInner extends CursorWrapper {
1566+
private final class CursorWrapperInner extends CrossProcessCursorWrapper {
15661567
private final IContentProvider mContentProvider;
15671568
public static final String TAG="CursorWrapperInner";
15681569

core/java/android/database/CrossProcessCursor.java

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,63 @@
1616

1717
package android.database;
1818

19+
/**
20+
* A cross process cursor is an extension of a {@link Cursor} that also supports
21+
* usage from remote processes.
22+
* <p>
23+
* The contents of a cross process cursor are marshalled to the remote process by
24+
* filling {@link CursorWindow} objects using {@link #fillWindow}. As an optimization,
25+
* the cursor can provide a pre-filled window to use via {@link #getWindow} thereby
26+
* obviating the need to copy the data to yet another cursor window.
27+
*/
1928
public interface CrossProcessCursor extends Cursor {
2029
/**
21-
* returns a pre-filled window, return NULL if no such window
30+
* Returns a pre-filled window that contains the data within this cursor.
31+
* <p>
32+
* In particular, the window contains the row indicated by {@link Cursor#getPosition}.
33+
* The window's contents are automatically scrolled whenever the current
34+
* row moved outside the range covered by the window.
35+
* </p>
36+
*
37+
* @return The pre-filled window, or null if none.
2238
*/
2339
CursorWindow getWindow();
2440

2541
/**
26-
* copies cursor data into the window start at pos
42+
* Copies cursor data into the window.
43+
* <p>
44+
* Clears the window and fills it with data beginning at the requested
45+
* row position until all of the data in the cursor is exhausted
46+
* or the window runs out of space.
47+
* </p><p>
48+
* The filled window uses the same row indices as the original cursor.
49+
* For example, if you fill a window starting from row 5 from the cursor,
50+
* you can query the contents of row 5 from the window just by asking it
51+
* for row 5 because there is a direct correspondence between the row indices
52+
* used by the cursor and the window.
53+
* </p><p>
54+
* The current position of the cursor, as returned by {@link #getPosition},
55+
* is not changed by this method.
56+
* </p>
57+
*
58+
* @param position The zero-based index of the first row to copy into the window.
59+
* @param window The window to fill.
2760
*/
28-
void fillWindow(int pos, CursorWindow winow);
61+
void fillWindow(int position, CursorWindow window);
2962

3063
/**
3164
* This function is called every time the cursor is successfully scrolled
3265
* to a new position, giving the subclass a chance to update any state it
33-
* may have. If it returns false the move function will also do so and the
66+
* may have. If it returns false the move function will also do so and the
3467
* cursor will scroll to the beforeFirst position.
68+
* <p>
69+
* This function should be called by methods such as {@link #moveToPosition(int)},
70+
* so it will typically not be called from outside of the cursor class itself.
71+
* </p>
3572
*
36-
* @param oldPosition the position that we're moving from
37-
* @param newPosition the position that we're moving to
38-
* @return true if the move is successful, false otherwise
73+
* @param oldPosition The position that we're moving from.
74+
* @param newPosition The position that we're moving to.
75+
* @return True if the move is successful, false otherwise.
3976
*/
4077
boolean onMove(int oldPosition, int newPosition);
41-
4278
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (C) 2011 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License
15+
*/
16+
17+
package android.database;
18+
19+
import android.database.CrossProcessCursor;
20+
import android.database.Cursor;
21+
import android.database.CursorWindow;
22+
import android.database.CursorWrapper;
23+
24+
/**
25+
* Cursor wrapper that implements {@link CrossProcessCursor}.
26+
* <p>
27+
* If the wrapper cursor implemented {@link CrossProcessCursor}, then delegates
28+
* {@link #fillWindow}, {@link #getWindow()} and {@link #onMove} to it. Otherwise,
29+
* provides default implementations of these methods that traverse the contents
30+
* of the cursor similar to {@link AbstractCursor#fillWindow}.
31+
* </p><p>
32+
* This wrapper can be used to adapt an ordinary {@link Cursor} into a
33+
* {@link CrossProcessCursor}.
34+
* </p>
35+
*/
36+
public class CrossProcessCursorWrapper extends CursorWrapper implements CrossProcessCursor {
37+
/**
38+
* Creates a cross process cursor wrapper.
39+
* @param cursor The underlying cursor to wrap.
40+
*/
41+
public CrossProcessCursorWrapper(Cursor cursor) {
42+
super(cursor);
43+
}
44+
45+
@Override
46+
public void fillWindow(int position, CursorWindow window) {
47+
if (mCursor instanceof CrossProcessCursor) {
48+
final CrossProcessCursor crossProcessCursor = (CrossProcessCursor)mCursor;
49+
crossProcessCursor.fillWindow(position, window);
50+
return;
51+
}
52+
53+
DatabaseUtils.cursorFillWindow(mCursor, position, window);
54+
}
55+
56+
@Override
57+
public CursorWindow getWindow() {
58+
if (mCursor instanceof CrossProcessCursor) {
59+
final CrossProcessCursor crossProcessCursor = (CrossProcessCursor)mCursor;
60+
return crossProcessCursor.getWindow();
61+
}
62+
63+
return null;
64+
}
65+
66+
@Override
67+
public boolean onMove(int oldPosition, int newPosition) {
68+
if (mCursor instanceof CrossProcessCursor) {
69+
final CrossProcessCursor crossProcessCursor = (CrossProcessCursor)mCursor;
70+
return crossProcessCursor.onMove(oldPosition, newPosition);
71+
}
72+
73+
return true;
74+
}
75+
}

core/java/android/database/CursorToBulkCursorAdaptor.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,10 @@ public void onChange(boolean selfChange) {
8989

9090
public CursorToBulkCursorAdaptor(Cursor cursor, IContentObserver observer,
9191
String providerName) {
92-
try {
93-
mCursor = (CrossProcessCursor) cursor;
94-
} catch (ClassCastException e) {
95-
throw new UnsupportedOperationException(
96-
"Only CrossProcessCursor cursors are supported across process for now", e);
92+
if (cursor instanceof CrossProcessCursor) {
93+
mCursor = (CrossProcessCursor)cursor;
94+
} else {
95+
mCursor = new CrossProcessCursorWrapper(cursor);
9796
}
9897
mProviderName = providerName;
9998

core/java/android/database/CursorWrapper.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@
2525
* use for this class is to extend a cursor while overriding only a subset of its methods.
2626
*/
2727
public class CursorWrapper implements Cursor {
28+
/** @hide */
29+
protected final Cursor mCursor;
2830

29-
private final Cursor mCursor;
30-
31+
/**
32+
* Creates a cursor wrapper.
33+
* @param cursor The underlying cursor to wrap.
34+
*/
3135
public CursorWrapper(Cursor cursor) {
3236
mCursor = cursor;
3337
}

0 commit comments

Comments
 (0)