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

本文详细剖析了Android中ContentResolver通过ContentProvider与SQLite数据库交互的过程,包括ContentResolver的query()方法如何调用到ContentProvider的query(),再到SQLite数据库的查询。重点讲解了CursorWindow的创建、填充以及在进程间通信中的作用,以及如何通过moveToFirst()建立数据通道。文章深入探讨了Android数据访问的底层机制,对于理解Android数据操作具有重要意义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上面是ContentProvider和IContentProvider的关系

先总结一下:

1.ContentResolver#query()->IContentProvider#query()->ContentProvider#query()->SQLiteDatabase#query()->return a SQLiteCursor.

2.上面返回的SQLiteCursor中还未创建CursorWindow。只是有一个与SQLiteDatabase的Connection和SQLiteQuery,即可以从数据库中获取指定数据。但是需要执行SQLiteCursor#fillWindow()会创建CursorWindow并申请共享内存,然后把查询到的数据放到CursorWindow所代表的共享内存中。

3.从onTransact写入到reply的是BulkCursorDescriptor(包含了CursorToBulkCursorAdapter和CursorWindow)

4.IContentProvider#query()返回的是CursorToBulkCursorAdapter,将CursorToBulkCursorAdapter和CursorWindow封装起来,CursorToBulkCursorAdapter extends AbstractWindowedCursor。

5.ContentResolver#query()返回的是ContentProviderClient#CursorWrapperInner。其中CursorWrapperInner封装了CursorToBulkCursorAdapter,CursorWrapperInner只做了一点点简单的事。

6.SQLiteCursor并没有返回给Bp端,实际只返回了CursorWindow和IBulkCursor(CursorToBulkCursorAdapter),CursorToBulkCursorAdapter组合了SQLite,所以IBulkCursor允许Client访问服务端的SQLiteCursor。然后Bp端收到CursorWindow和IBulkCursor后,把这两个成员封装到BulkCursorToCursorAdapter以便给客户使用,因为客户只认识Cursor接口。在一般情况下,client只需通过CursorWindow便可以获取到想要的数据,而不用与SQLiteCursor通信。只有在以下条件下才需要与SQLiteCursor通信:

CursorToBulkCursorAdapter#onMove

if (mWindow == null
                    || newPosition < mWindow.getStartPosition()
                    || newPosition >= mWindow.getStartPosition() + mWindow.getNumRows()) {
                setWindow(mBulkCursor.getWindow(newPosition));
            }

上面代码可以看出,当position超出Window中的数据范围时,需要重新fillWindow。因为Window中不一定包含了所有查询到的数据,因为window只申请了2M的空间,不一定所有数据都能装下。

定义空间大小:

sCursorWindowSize = Resources.getSystem().getInteger(
                com.android.internal.R.integer.config_cursorWindowSize) * 1024;//window的大小就是这么大,config_cursorWindowSize为2048

看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 Bul
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值