ContentProvider、SQLiteDatabase、Cursor--query()分析--基于Android N

看ContentResolver的query方法

public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable String selection,
            @Nullable String[] selectionArgs, @Nullable String sortOrder,
            @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        Cursor qCursor = null;
        try {
            long startTime = SystemClock.uptimeMillis();

            ICancellationSignal remoteCancellationSignal = null;
            if (cancellationSignal != null) {
                cancellationSignal.throwIfCanceled();
                remoteCancellationSignal = unstableProvider.createCancellationSignal();
                cancellationSignal.setRemote(remoteCancellationSignal);
            }
            try {
               //无论是unstable还是stable,都是调用ContentProvider的Bp端query方法,后面可以知道,qCursor是BulkCursorToCursorAdapter类型
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                // The remote process has died...  but we only hold an unstable
                // reference though, so we might recover!!!  Let's try!!!!
                // This is exciting!!1!!1!!!!1
                unstableProviderDied(unstableProvider);
                stableProvider = acquireProvider(uri);
                if (stableProvider == null) {
                    return null;
                }
                qCursor = stableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            }
            if (qCursor == null) {
                return null;
            }

            // Force query execution.  Might fail and throw a runtime exception here.
            qCursor.getCount();
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);

            // Wrap the cursor object into CursorWrapperInner object.
            final IContentProvider provider = (stableProvider != null) ? stableProvider
                    : acquireProvider(uri);
            final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);//从ContentResolver返回的是一个CursorWrapperInner
            stableProvider = null;
            qCursor = null;
            return wrapper;
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
            return null;
        } finally {
            if (qCursor != null) {
                qCursor.close();
            }
            if (cancellationSignal != null) {
                cancellationSignal.setRemote(null);
            }
            if (unstableProvider != null) {
                releaseUnstableProvider(unstableProvider);
            }
            if (stableProvider != null) {
                releaseProvider(stableProvider);
            }
        }
    }

从ContentProviderProxy#query()入手:

public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
            String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
                    throws RemoteException {
        BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();//注意这个类型的对象
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(IContentProvider.descriptor);//写入binder标志

            data.writeString(callingPkg);//写入方法的参数
            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());//传递一个ContentObserver,一个Binder对象
            data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);

            mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);//向Bn端发起请求

            DatabaseUtils.readExceptionFromParcel(reply);//读取返回值

            if (reply.readInt() != 0) {
//所有参数都包含在了BuklCursorDescriptor中,包括IBulkCursor和CursorWindow,后面会分析这个createFromParcel(),
                BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);//读取一个序列化对象,并反序列化该对象
                adaptor.initialize(d);//把BuildCursorDescriptor和adaptor绑定,最后返回这个Cursor,所以从ContentProvider的Bp端返回的是BulkCursorToCursorAdapter
            } 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();
        }
    }

主要向ContentProvider的Bn端发送了一个ContentObserver的Bp端,还有进行查询的SQL语句信息

分析BulkCursorDescriptor.CREATOR.createFromParcel(reply):

public final class BulkCursorDescriptor implements Parcelable {
    public static final Parcelable.Creator<BulkCursorDescriptor> CREATOR =
            new Parcelable.Creator<BulkCursorDescriptor>() {
        @Override
        public BulkCursorDescriptor createFromParcel(Parcel in) {
            BulkCursorDescriptor d = new BulkCursorDescriptor();//创建对象
            d.readFromParcel(in);//反序列化一个对象
            return d;
        }

        @Override
        public BulkCursorDescriptor[] newArray(int size) {
            return new BulkCursorDescriptor[size];
        }
    };

    public IBulkCursor cursor;//重要成员
    public String[] columnNames;
    public boolean wantsAllOnMoveCalls;
    public int count;
    public CursorWindow window;/重要成员

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeStrongBinder(cursor.asBinder());
        out.writeStringArray(columnNames);
        out.writeInt(wantsAllOnMoveCalls ? 1 : 0);
        out.writeInt(count);
        if (window != null) {
            out.writeInt(1);
            window.writeToParcel(out, flags);
        } else {
            out.writeInt(0);
        }
    }

    public void readFromParcel(Parcel in) {
        cursor = BulkCursorNative.asInterface(in.readStrongBinder());//获取一个Bp对象,IBulkCursor
        columnNames = in.readStringArray();
        wantsAllOnMoveCalls = in.readInt() != 0;
        count = in.readInt();
        if (in.readInt() != 0) {
            window = CursorWindow.CREATOR.createFromParcel(in);//查询到的行数不为0,则获取这个共享内存对象
        }
    }
}

接收了来自ContentProvider Bn端的CursorToBulkCursorAdaptor的Bp端。CursorToBulkCursorAdaptor继承了和实现了BulkCursorNative,一个反序列化的CursorWindow。


看ContentProvider的Bn端是怎么处理请求并返回数据,从ContentProviderNative的onTransaction()开始分析:

case QUERY_TRANSACTION://这是处理query请求的
                {
                    data.enforceInterface(IContentProvider.descriptor);

                    String callingPkg = data.readString();
                    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());//创建一个ContentObserver的Bp端
                    ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                            data.readStrongBinder());

                    Cursor cursor = query(callingPkg, url, projection, selection, selectionArgs,
                            sortOrder, cancellationSignal);//调用Bn端的query方法,并返回一个Cursor,这个Cursor是SQLiteCursor类型
                    if (cursor != null) {
                        CursorToBulkCursorAdaptor adaptor = null;

                        try {
                            adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
                                    getProviderName());//创建一个包括Cursor和ContentObserver的对象
                            cursor = null;

                            BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();//获取一个Parcelable对象,里面还调用了一个重要方法
                            adaptor = null;

                            reply.writeNoException();
                            reply.writeInt(1);
//将
                            d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);//将BulkCursorDescriptor中的IBulkCursor写到reply中
                        } finally {
                            // Close cursor if an exception was thrown while constructing the adaptor.
                            if (adaptor != null) {
                                adaptor.close();
                            }
                            if (cursor != null) {
                                cursor.close();
                            }
                        }
                    } else {
                        reply.writeNoException();
                        reply.writeInt(0);
                    }

                    return true;
                }
public void writeToParcel(Parcel out, int flags) {
        out.writeStrongBinder(cursor.asBinder());
        out.writeStringArray(columnNames);
        out.writeInt( ? 1 : 0);
        out.writeInt(count)wantsAllOnMoveCalls;
        if (window != null) {
            out.writeInt(1);
            window.writeToParcel(out, flags);
        } else {
            out.writeInt(0);
        }
    }

CursorWindow的writeToParcel():

public void writeToParcel(Parcel dest, int flags) {
        acquireReference();
        try {
            dest.writeInt(mStartPos);//写入数据在共享内存的起始位置
            nativeWriteToParcel(mWindowPtr, dest);//写入对应native层CursorWindow实例的指针
        } finally {
            releaseReference();
        }

        if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
            releaseReference();
        }
    }

可以看到ContentProvider的Bn端给Bp端写入IBulkCursor、columnNames,还有native层CursorWindow的指针

再看看BulkCursorDescrptor的生成,CursorToBulkCursorAdaptor#getBulkCursorDescriptor():

/**
     * Returns an object that contains sufficient metadata to reconstruct
     * the cursor remotely.  May throw if an error occurs when executing the query
     * and obtaining the row count.
     */
    public BulkCursorDescriptor getBulkCursorDescriptor() {
        synchronized (mLock) {
            throwIfCursorIsClosed();

            BulkCursorDescriptor d = new BulkCursorDescriptor();
            d.cursor = this;
            d.columnNames = mCursor.getColumnNames();
            d.wantsAllOnMoveCalls = mCursor.getWantsAllOnMoveCalls();
            d.count = mCursor.getCount();//mCursor指向的SQLiteCursor
            d.window = mCursor.getWindow();
            if (d.window != null) {
                // Acquire a reference to the window because its reference count will be
                // decremented when it is returned as part of the binder call reply parcel.
                d.window.acquireReference();
            }
            return d;
        }
    }

SQLiteCursor#getCount():这个方法会将查询到的内容写到共享内存中。至于这个SQLiteCursor是怎么来的,后面会在分析,其实SQLite的query主要就是需要native层的三样东西,SQLiteDatabase的指针(6.0之后后由SQLiteConnection持有),statement指针(主要是sql语句),CursorWindow指针。看SQLiteCursor#getCount()源码:

@Override
    public int getCount() {
        if (mCount == NO_COUNT) {
            fillWindow(0);//分析这方法
        }
        return mCount;
    }

SQLiteCursor#fillWindow():

private void fillWindow(int requiredPos) {
        clearOrCreateWindow(getDatabase().getPath());

        try {
            if (mCount == NO_COUNT) {
                int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0);//内存起始位置
                mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);//mQuery是SQLiteQuery类型
                mCursorWindowCapacity = mWindow.getNumRows();//计算这个共享内存可以存多少行记录
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
                }
            } else {
                int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
                        mCursorWindowCapacity);
                mQuery.fillWindow(mWindow, startPos, requiredPos, false);//mQuery是SQLiteQuery类型
 }
        } catch (RuntimeException ex) {
            // Close the cursor window if the query failed and therefore will
            // not produce any results.  This helps to avoid accidentally leaking
            // the cursor window if the client does not correctly handle exceptions
            // and fails to close the cursor.
            closeWindow();
            throw ex;
        }
    }

看SQLiteQuery#fillWindow():

int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) {
        acquireReference();
        try {
            window.acquireReference();//增加引用
            try {
                int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(),
                        window, startPos, requiredPos, countAllRows, getConnectionFlags(),
                        mCancellationSignal);//getSession返回的实例是SQLiteSesion
                return numRows;
            } catch (SQLiteDatabaseCorruptException ex) {
                onCorruption();
                throw ex;
            } catch (SQLiteException ex) {
                Log.e(TAG, "exception: " + ex.getMessage() + "; query: " + getSql());
                throw ex;
            } finally {
                window.releaseReference();
            }
        } finally {
            releaseReference();
        }
    }

看SQLiteSession#executeForCursorWindow():

public int executeForCursorWindow(String sql, Object[] bindArgs,
            CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
            int connectionFlags, CancellationSignal cancellationSignal) {
        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }
        if (window == null) {
            throw new IllegalArgumentException("window must not be null.");
        }

        if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
            window.clear();
            return 0;
        }
//获取一个SQLiteConnection,让mConnection指向该connection,代表了一个数据库连接,肯定持有了SQLiteDatabase指针
        acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
        try {
            return mConnection.executeForCursorWindow(sql, bindArgs,//分析这个方法
                    window, startPos, requiredPos, countAllRows,
                    cancellationSignal); // might throw
        } finally {
            releaseConnection(); // might throw
        }
    }

看SQLiteConnection#executeForCursorWindow():

public int executeForCursorWindow(String sql, Object[] bindArgs,
            CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
            CancellationSignal cancellationSignal) {
        if (sql == null) {
            throw new IllegalArgumentException("sql must not be null.");
        }
        if (window == null) {
            throw new IllegalArgumentException("window must not be null.");
        }

        window.acquireReference();//对window增加引用
        try {
            int actualPos = -1;
            int countedRows = -1;
            int filledRows = -1;
            final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
                    sql, bindArgs);
            try {
                final PreparedStatement statement = acquirePreparedStatement(sql);//对应native层的sqlite3_stmt
                try {
                    throwIfStatementForbidden(statement);
                    bindArguments(statement, bindArgs);
                    applyBlockGuardPolicy(statement);
                    attachCancellationSignal(cancellationSignal);
                    try {
//这个是一个jni方法,在android_base_SQLiteConnection.cpp中
                       final long result = nativeExecuteForCursorWindow(
                                mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
                                startPos, requiredPos, countAllRows);//mConnectionPtr、mStatementPtr、mWindowPtr就是对应native层对象的指针
                        actualPos = (int)(result >> 32);
                        countedRows = (int)result;
                        filledRows = window.getNumRows();
                        window.setStartPosition(actualPos);
                        return countedRows;//返回查询到的行数
                    } finally {
                        detachCancellationSignal(cancellationSignal);
                    }
                } finally {
                    releasePreparedStatement(statement);
                }
            } catch (RuntimeException ex) {
                mRecentOperations.failOperation(cookie, ex);
                throw ex;
            } finally {
                if (mRecentOperations.endOperationDeferLog(cookie)) {
                    mRecentOperations.logOperation(cookie, "window='" + window
                            + "', startPos=" + startPos
                            + ", actualPos=" + actualPos
                            + ", filledRows=" + filledRows
                            + ", countedRows=" + countedRows);
                }
            }
        } finally {
            window.releaseReference();//使用完后释放引用,引用减1
        }
    }

看android_base_SQLiteConnection#nativeExecuteForCursorWindow():

static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
        jlong connectionPtr, jlong statementPtr, jlong windowPtr,
        jint startPos, jint requiredPos, jboolean countAllRows) {
    SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);//把long值转换成对应的类型的指针,下面两个也是
    sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
    CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);

    status_t status = window->clear();
    if (status) {
        String8 msg;
        msg.appendFormat("Failed to clear the cursor window, status=%d", status);
        throw_sqlite3_exception(env, connection->db, msg.string());
        return 0;
    }

    int numColumns = sqlite3_column_count(statement);
    status = window->setNumColumns(numColumns);//设置每行有多少列
    if (status) {
        String8 msg;
        msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
                numColumns, status);
        throw_sqlite3_exception(env, connection->db, msg.string());
        return 0;
    }

    int retryCount = 0;
    int totalRows = 0;
    int addedRows = 0;
    bool windowFull = false;
    bool gotException = false;
//下面这个循环,它将遍历SQL的结果集,并将数据取出来保存到CursorWindow对象中
    while (!gotException && (!windowFull || countAllRows)) {
        int err = sqlite3_step(statement);//有点像iterator,一次取一行
        if (err == SQLITE_ROW) {//正常的是进入这里,SQLite_ROW表示获取到了一行
            LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
            retryCount = 0;//后面在忙时会重复尝试连接,retry大于50次就放弃
            totalRows += 1;//每次取了一行都加1

            // Skip the row if the window is full or we haven't reached the start position yet.
            if (startPos >= totalRows || windowFull) {
                continue;
            }

            CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);//将数据写到window中,就是写到共享内存中
            if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) {
                // We filled the window before we got to the one row that we really wanted.
                // Clear the window and start filling it again from here.
                // TODO: Would be nicer if we could progressively replace earlier rows.
                window->clear();
                window->setNumColumns(numColumns);
                startPos += addedRows;
                addedRows = 0;
                cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
            }

            if (cpr == CPR_OK) {
                addedRows += 1;
            } else if (cpr == CPR_FULL) {
                windowFull = true;
            } else {
                gotException = true;
            }
        } else if (err == SQLITE_DONE) {//表示上次已经获取最后一行的,这次取的话就会进入这里
            // All rows processed, bail
            LOG_WINDOW("Processed all rows");
            break;
        } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {//数据库忙或者锁被占用了
            // The table is locked, retry
            LOG_WINDOW("Database locked, retrying");
            if (retryCount > 50) {
                ALOGE("Bailing on database busy retry");
                throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
                gotException = true;
            } else {
                // Sleep to give the thread holding the lock a chance to finish
                usleep(1000);
                retryCount++;
            }
        } else {
            throw_sqlite3_exception(env, connection->db);
            gotException = true;
        }
    }

    LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
            "to the window in %d bytes",
            statement, totalRows, addedRows, window->size() - window->freeSpace());
    sqlite3_reset(statement);

    // Report the total number of rows on request.
    if (startPos > totalRows) {
        ALOGE("startPos %d > actual rows %d", startPos, totalRows);
    }
    jlong result = jlong(startPos) << 32 | jlong(totalRows);
    return result;
}


看看CursorWindow的创建过程,首先要从SQLiteCursor#fillWindow()中的调用 clearOrCreateWindow (getDatabase().getPath());分析起,该方法是其父类AbstractWindowedCursor实现的。

protected void clearOrCreateWindow(String name) {
        if (mWindow == null) {
            mWindow = new CursorWindow(name);
        } else {
            mWindow.clear();
        }
    }

看CursorWindow的构造方法:

public CursorWindow(String name) {
        mStartPos = 0;
        mName = name != null && name.length() != 0 ? name : "<unnamed>";
        if (sCursorWindowSize < 0) {
            /** The cursor window size. resource xml file specifies the value in kB.
             * convert it to bytes here by multiplying with 1024.
             */
            sCursorWindowSize = Resources.getSystem().getInteger(
                com.android.internal.R.integer.config_cursorWindowSize) * 1024;//window的大小就是这么大,config_cursorWindowSize为2048
        }
//创建一个native层的CursorWindow方法,java层的CursorWindow持有native层window的指针到mWindowPtr
        mWindowPtr = nativeCreate(mName, sCursorWindowSize);
        if (mWindowPtr == 0) {
            throw new CursorWindowAllocationException("Cursor window allocation of " +
                    (sCursorWindowSize / 1024) + " kb failed. " + printStats());
        }
        mCloseGuard.open("close");
        recordNewWindow(Binder.getCallingPid(), mWindowPtr);
    }

看看nativeCreate(),在android_base_CursorWindow.cpp中:

static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
    String8 name;
    const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
    name.setTo(nameStr);
    env->ReleaseStringUTFChars(nameObj, nameStr);

    CursorWindow* window;
    status_t status = CursorWindow::create(name, cursorWindowSize, &window);
    if (status || !window) {
        ALOGE("Could not allocate CursorWindow '%s' of size %d due to error %d.",
                name.string(), cursorWindowSize, status);
        return 0;
    }

    LOG_WINDOW("nativeInitializeEmpty: window = %p", window);
    return reinterpret_cast<jlong>(window);
}



下面是是ContentProvider的Bp端调用Bn端的query方法的交互过程,其中左边ContentProviderNative应改成ContentProviderProxy




Cursor家族:

下面先看看ContentProvider的Bn端query()

ContentProvider$Transport extends ContentProviderNative,看ContentProvider$Transport#query():

@Override
        public Cursor query(String callingPkg, Uri uri, String[] projection,
                String selection, String[] selectionArgs, String sortOrder,
                ICancellationSignal cancellationSignal) {
            validateIncomingUri(uri);
            uri = getUriWithoutUserId(uri);
            if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                // The caller has no access to the data, so return an empty cursor with
                // the columns in the requested order. The caller may ask for an invalid
                // column and we would not catch that but this is not a problem in practice.
                // We do not call ContentProvider#query with a modified where clause since
                // the implementation is not guaranteed to be backed by a SQL database, hence
                // it may not handle properly the tautology where clause we would have created.
                if (projection != null) {
                    return new MatrixCursor(projection, 0);
                }

                // Null projection means all columns but we have no idea which they are.
                // However, the caller may be expecting to access them my index. Hence,
                // we have to execute the query as if allowed to get a cursor with the
                // columns. We then use the column names to return an empty cursor.
                Cursor cursor = ContentProvider.this.query(uri, projection, selection,
                        selectionArgs, sortOrder, CancellationSignal.fromTransport(
                                cancellationSignal));
                if (cursor == null) {
                    return null;
                }

                // Return an empty cursor for all columns.
                return new MatrixCursor(cursor.getColumnNames(), 0);
            }
            final String original = setCallingPackage(callingPkg);
            try {
                return ContentProvider.this.query(
                        uri, projection, selection, selectionArgs, sortOrder,
                        CancellationSignal.fromTransport(cancellationSignal));//最终调用ContentProvider子类的query()
            } finally {
                setCallingPackage(original);
            }
        }

ContentProvider子类query最终会去SQLiteDatabase的query方法,无论是SQLiteDatabase#query还是rawQuery,最终会去到SQLiteDatabase#queryWithFactory():

public Cursor queryWithFactory(CursorFactory cursorFactory,
            boolean distinct, String table, String[] columns,
            String selection, String[] selectionArgs, String groupBy,
            String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
        acquireReference();
        try {
//根据各种查询参数构造一个SQL的SELECT语句
            String sql = SQLiteQueryBuilder.buildQueryString(
                    distinct, table, columns, selection, groupBy, having, orderBy, limit);
//其中cursorFactory为null
            return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
                    findEditTable(table), cancellationSignal);
        } finally {
            releaseReference();
        }
    }
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();
        }
    }

看SQLiteDirectCursorDriver#query():

public Cursor query(CursorFactory factory, String[] selectionArgs) {
        final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);
        final Cursor cursor;
        try {
            query.bindAllArgsAsStrings(selectionArgs);

            if (factory == null) {//在rawQueryWithFactory方法中说过,这个参数为null
                cursor = new SQLiteCursor(this, mEditTable, query);//从SQLiteDatabase返回的就是SQLiteCursor
            } else {
                cursor = factory.newCursor(mDatabase, this, mEditTable, query);
            }
        } catch (RuntimeException ex) {
            query.close();
            throw ex;
        }

        mQuery = query;
        return cursor;
    }


上面的分析,现在在ContentProvider的Bn端已经把数据查询后放到了CursorWindow中(前面说了,在调用SQLiteCursor.getCount后,就开始了真正的查询操作)。那么现在就是要等Bn端获取共享内存的内容了,那么Bn端首先要获取CursorWindow指针。CursorWindow就是对一块共享内存的封装。另外我们也看到了如何将执行SELECT语句后得到的结果集填充到这块共享内存中。但是这块内存现在还仅属于服务端进程,只有客户端进程得到这块内存后,客户端才能真正获取执行SELECT后的结果。

现在就要开始回到客户端分析了ContentProviderProxy返回的是BulkCursorToCursorAdapter,而ContentResolver返回的是CursorWrapperInner,CursorWrapperInner封装了BulkCursorToCursorAdapter。所以接下来将分析BulkCursorToCursorAdapter和CursorWrapperInner.

moveToFirst函数分析

客户端还未拿到那块至关重要的共享内存,即进程间的数据通道还没打通。那么,数据通道是何时打通的,就是调用moveToFirst才打通的,所以使用cursor获取数据前,必须调用moveToFirst,其中CursorWrapperInner在ContentProviderClient.java 中。moveToFirst就是在CursorWrapper实现的。

[-->CursorWrapper.java::moveToFirst]

 publicboolean moveToFirst() {

   //mCursor指向BulkCursorToCursorAdaptor

   returnmCursor.moveToFirst();

}

mCursor成员变量的真实类型是BulkCursorToCursorAdaptor,但其moveToFirst函数却是该类的老祖宗AbstractCursor实现,代码如下:

[-->AbstractCursor.java::moveToFirst]

 publicfinal boolean moveToFirst() {

    returnmoveToPosition(0);//调用moveToPosition,直接来看该函数

}

//moveToPosition分析,其参数position表示将移动游标到哪一行

public final boolean moveToPosition(int position){

   //getCount返回结果集中的行数,这个值在搭建Binder通信通道时,已经由服务端计算并返回

   //给客户端了

   final int count = getCount();

   //mPos变量记录了当前游标的位置,该变量初值为-1

   if(position >= count) {

         mPos = count;

         return false;

   }

   if(position < 0) {

        mPos = -1;

         returnfalse;

   }

    if(position == mPos)  return true;

    //onMove函数为抽象函数,由子类实现

    booleanresult = onMove(mPos, position);

    if(result == false) mPos = -1;

     else {

           mPos = position;

           if (mRowIdColumnIndex != -1) {

               mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex));

           }

        }

    returnresult;

 }

在上边代码中,moveToPosition将调用子类实现的onMove函数。在本例中,子类就是BulkCursorToCursorAdaptor,接下来看它的onMove函数。

(1) BulkCursorToCursorAdaptor的onMove函数分析

[-->BulkCursorToCursorAdaptor.java::onMove]

public boolean onMove(int oldPosition, intnewPosition) {

 throwIfCursorIsClosed();

  try {

     //mWindow的类型就是CursorWindow。第一次调用该函数,mWindow为null

      if(mWindow == null

            ||newPosition < mWindow.getStartPosition()

            || newPosition >= mWindow.getStartPosition()+

                                 mWindow.getNumRows()){

            /*

              mBulkCurosr用于和位于服务端的IBulkCursor Bn端通信,其getWindow函数

              将返回一个CursorWindow类型的对象。也就是说,调用完getWindow函数后,

              客户端进程就得到了一个CursorWindow,从此,客户端和服务端之间的数据通道就

              打通了

            */

            setWindow(mBulkCursor.getWindow(newPosition));

        }else if (mWantsAllOnMoveCalls) {

            mBulkCursor.onMove(newPosition);

          }

        } ......

     if (mWindow== null)  return false;

     returntrue;

 }

建立数据通道的关键函数是IBulkCurosr的getWindow。对于客户端而言,IBulkCursor Bp端对象的类型是BulkCursorProxy,下面介绍它的getWindow函数。

(2) BulkCursorProxy的 getWindow函数分析

[-->BulkCursorNative.java::BulkCursorProxy:getWindow]

public CursorWindow getWindow(int startPos) throwsRemoteException

 {

    Parceldata = Parcel.obtain();

    Parcelreply = Parcel.obtain();

    try {

         data.writeInterfaceToken(IBulkCursor.descriptor);

         data.writeInt(startPos);

         mRemote.transact(GET_CURSOR_WINDOW_TRANSACTION, data, reply, 0);

         DatabaseUtils.readExceptionFromParcel(reply);

         CursorWindow window = null;

          if(reply.readInt() == 1) {

             /*

              根据服务端reply包构造一个本地的CursorWindow对象,读者可自行研究

              newFromParcel函数,其内部会调用nativeCreateFromParcel函数以创建

              一个Native的CursorWindow对象。整个过程就是笔者在前面提到的反序列化过程

              */

               window = CursorWindow.newFromParcel(reply);

          }

           return window;

        } ......

 }

再来看IBulkCursor Bn端的getWindow函数,此Bn端对象的真实类型是CursorToBulkCursorAdaptor。

(3) CursorToBulkCursorAdaptor的 getWindow函数分析

[-->CursorToBulkCursorAdaptor.java::getWindow]

public CursorWindow getWindow(int startPos) {

 synchronized (mLock) {

    throwIfCursorIsClosed();

     CursorWindow window;

      //mCursor是MediaProvider query返回的值,其真实类型是SQLiteCursor,满足

     //下面的if条件

     if(mCursor instanceof AbstractWindowedCursor) {

          AbstractWindowedCursor windowedCursor =

                                             (AbstractWindowedCursor)mCursor;

          //对于本例而言,SQLiteCursor已经和一个CursorWindow绑定了,所以window的值

         //不为空

          window = windowedCursor.getWindow();

           if (window == null) {

                 window = new CursorWindow(mProviderName, false);

                 windowedCursor.setWindow(window);

            }

              //调用SQLiteCursor的moveToPosition函数,该函数前面已经分析过了,在其               //内部将触发onMove函数的调用,此处将是SQLiteCursor的onMove函数

               mCursor.moveToPosition(startPos);

           } else {

              ......

           }

           if (window != null) {

               window.acquireReference();

           }

           return window;

        }

 }

服务端返回的CursorWindow对象正是之前在count函数中创建的那个CursorWindow对象,其内部已经包含了执行本次query的查询结果。

另外,在将服务端的CursorWindow传递到客户端之前,系统会调用CursorWindow的writeToParcel函数进行序列化工作。读者可自行阅读CursorWindow的writeToParcel及其native实现nativeWriteToParcel函数。

(4) SQLiteCursor的 moveToPostion函数分析

该函数由SQLiteCursor的基类AbstractCursor实现。我们前面已经看过它的代码了,其内部的主要工作就是调用AbstractCursor子类(此处就是SQLiteCursor自己)实现onMove函数,因此可直接看SQLiteCursor的onMove函数。

[-->SQLiteCursor.java::onMove]

public boolean onMove(int oldPosition, intnewPosition) {

   if(mWindow == null || newPosition < mWindow.getStartPosition() ||

       newPosition >= (mWindow.getStartPosition() +

                                                       mWindow.getNumRows())) {

           fillWindow(newPosition);

     }

     returntrue;

 }

以上代码中的if判断很重要,具体解释如下:

·  当mWindow为空,即服务端未创建CursorWindow时(当然,就本例而言,CursorWindow早已在query时就创建好了),需调用fillWindow。该函数内部将调用clearOrCreateLocalWindow。如果CursorWindow不存在,则创建一个CursorWindow对象。如果已经存在,则清空CursorWindow对象的信息。

·  当newPosition小于上一次查询得到的CursorWindow的起始位置,或者newPosition大于上一次查询得到的CursorWindow的最大行位置,也需调用fillWindow。由于此时CursorWindow已经存在,则clearOrCreateLocalWindow会调用它的clear函数以清空之前保存的信息。

·  调用fillWindow后将执行SQL语句,以获得正确的结果集。例如,假设上次执行query时设置了查询从第10行开始的90条记录(即10~100行的记录),那么,当新的query若指定了从0行开始或从101行开始时,就需重新fillWindow,即将新的结果填充到CursorWindow中。如果新query查询的行数位于10~100之间,则无需再次调用fillWindow了。

这是服务端针对query做的一些优化处理,即当CursorWindow已经包含了所要求的数据时,就没有必要再次查询了。按理说,客户端也应该做类似的判断,以避免发起不必要的Binder请求。我们回过头来看客户端BulkCursorToCursorAdaptor的onMove函数。

[-->BulkCursorToCursorAdaptor.java::onMove]

public boolean onMove(int oldPosition, intnewPosition) {

 throwIfCursorIsClosed();

  try {

      //同样,客户端也做了对应的优化处理,如果不满足if条件,客户端根本无需调用

    //mBulkCurosr的getWindow函数,这样服务端也就不会收到对应的Binder请求了

      if(mWindow == null

            ||newPosition < mWindow.getStartPosition()

            || newPosition >=mWindow.getStartPosition() +

                                 mWindow.getNumRows()){

            setWindow(mBulkCursor.getWindow(newPosition));

    )

 ......

}

(5) moveToFirst函数分析总结

moveToFirst及相关的兄弟函数(如moveToLast和move等)的目的是移动游标位置到指定行。通过上面的代码分析,我们发现它的工作其实远不止移动游标位置这么简单。对于还未拥有CursorWindow的客户端来说,moveToFirst将导致客户端反序列化来自服务端的CursorWindow信息,从而使客户端和服务端之间的数据通道真正建立起来。



猜你喜欢

转载自blog.csdn.net/b1480521874/article/details/80271986
N!
n
N*