ContentResolver.query流程分析

news2024/12/23 6:45:04

文章目录

      • 1.Context.getContentResolver()
      • 2.ContentResolver.query()
      • 3.ContentProviderProxy.query()
      • 4.Transport.query()

总结

  • 增删改查ContentProvider时,通过Binder实现
  • ContentProvider在App进程启动时进行实例化,具体时机是在Application.onCreate()执行前
  • 流程图如下:

1.Context.getContentResolver()

我们知道Context实现类是ContextImpl,可以看到getContentResolver()返回的是ApplicationContentResolver,相关类图如下:
在这里插入图片描述

// frameworks\base\core\java\android\content\Context.java
public abstract ContentResolver getContentResolver();

// frameworks\base\core\java\android\app\ContextImpl.java
private final ApplicationContentResolver mContentResolver
public ContentResolver getContentResolver() {
   return mContentResolver;
}

private static final class ApplicationContentResolver extends ContentResolver {
    private final ActivityThread mMainThread;

    public ApplicationContentResolver(Context context, ActivityThread mainThread) {
        super(context);
        mMainThread = Objects.requireNonNull(mainThread);
    }

    @Override
    protected IContentProvider acquireProvider(Context context, String auth) {
        return mMainThread.acquireProvider(context,
                ContentProvider.getAuthorityWithoutUserId(auth),
                resolveUserIdFromAuthority(auth), true);
    }
    ......
}

2.ContentResolver.query()

query()有几个重载,最后都执行了第一个,整个逻辑大致如下:从ActivityThread中缓存的mProviderMap中获取对应的ContentProvider,执行其query(),将结果包装成​CursorWrapperInner返回​
在这里插入图片描述

// frameworks\base\core\java\android\content\ContentResolver.java
/**
 * Query the given URI, returning a {@link Cursor} over the result set
 * with support for cancellation.
 *
 * <p>For best performance, the caller should follow these guidelines:
 *
 * <li>Provide an explicit projection, to prevent reading data from storage
 * that aren't going to be used.
 *
 * Provider must identify which QUERY_ARG_SORT* arguments were honored during
 * the preparation of the result set by including the respective argument keys
 * in the {@link Cursor} extras {@link Bundle}. See {@link #EXTRA_HONORED_ARGS}
 * for details.
 *
 * @see #QUERY_ARG_SORT_COLUMNS
 * @see #QUERY_ARG_SORT_DIRECTION
 * @see #QUERY_ARG_SORT_COLLATION
 *
 * @param uri The URI, using the content:// scheme, for the content to
 *         retrieve.
 * @param projection A list of which columns to return. Passing null will
 *         return all columns, which is inefficient.
 * @param queryArgs A Bundle containing additional information necessary for
 *            the operation. Arguments may include SQL style arguments, such
 *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
 *            the documentation for each individual provider will indicate
 *            which arguments they support.
 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
 * If the operation is canceled, then {@link OperationCanceledException} will be thrown
 * when the query is executed.
 * @return A Cursor object, which is positioned before the first entry. May return
 *         <code>null</code> if the underlying content provider returns <code>null</code>,
 *         or if it crashes.
 * @see Cursor
 */
@Override
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    Objects.requireNonNull(uri, "uri");
    // 从ApplicationContentResolver构造方法可以看到mWrapped=null
    try {
        if (mWrapped != null) {
            return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
        }
    } catch (RemoteException e) {
        return null;
    }
    // 从ActivityThread.acquireProvider()中获取,这里可以看ContentProvider启动流程
    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 {
            qCursor = unstableProvider.query(mContext.getAttributionSource(), uri, projection,
                    queryArgs, 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(mContext.getAttributionSource(), uri, projection,
                    queryArgs, 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, queryArgs);
        // Wrap the cursor object into CursorWrapperInner object.
        final IContentProvider provider = (stableProvider != null) ? stableProvider
                : acquireProvider(uri);
        final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
        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);
        }
    }
}

3.ContentProviderProxy.query()

接下来我们搜索IContentProvider的实现类,发现是ContentProviderNative和ContentProviderProxy

从这几个类命名和内容,很容易想到和平时写aidl生成的代码很相似,手写了Binder,根据binder调用套路,可以知道调用流程为

ContentProviderProxy.query() -->ContentProviderNative.onTransact() --> ContentProviderNative.query()


// frameworks\base\core\java\android\content\ContentProviderNative.java
abstract public class ContentProviderNative extends Binder implements IContentProvider {
    public ContentProviderNative()
    {
        attachInterface(this, descriptor);
    }
    ......
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
            switch (code) {
                case QUERY_TRANSACTION:
                {
                    data.enforceInterface(IContentProvider.descriptor);

                    AttributionSource attributionSource = AttributionSource.CREATOR
                            .createFromParcel(data);
                    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();
                        }
                    }

                    Bundle queryArgs = data.readBundle();
                    IContentObserver observer = IContentObserver.Stub.asInterface(
                            data.readStrongBinder());
                    ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                            data.readStrongBinder());

                    Cursor cursor = query(attributionSource, url, projection, queryArgs,
                            cancellationSignal);
                    if (cursor != null) {
                        CursorToBulkCursorAdaptor adaptor = null;

                        try {
                            adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
                                    getProviderName());
                            cursor = null;

                            BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
                            adaptor = null;

                            reply.writeNoException();
                            reply.writeInt(1);
                            d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                        } 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;
                }
        ......      
    }

    @Override
    public IBinder asBinder()
    {
        return this;
    }
}

final class ContentProviderProxy implements IContentProvider
{
    public ContentProviderProxy(IBinder remote)
    {
        mRemote = remote;
    }

    @Override
    public IBinder asBinder()
    {
        return mRemote;
    }

    @Override
    public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable ICancellationSignal cancellationSignal)
            throws RemoteException {
        BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(IContentProvider.descriptor);

            attributionSource.writeToParcel(data, 0);
            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.writeBundle(queryArgs);
            data.writeStrongBinder(adaptor.getObserver().asBinder());
            data.writeStrongBinder(
                    cancellationSignal != null ? cancellationSignal.asBinder() : null);

            mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);

            DatabaseUtils.readExceptionFromParcel(reply);

            if (reply.readInt() != 0) {
                BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
                Binder.copyAllowBlocking(mRemote, (d.cursor != null) ? d.cursor.asBinder() : null);
                adaptor.initialize(d);
            } 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();
        }
    }

}

4.Transport.query()

接着搜索谁继承了ContentProviderNative,发现是Transport(Transport为ContentProvider内部类),其query()最终调用了ContentProvider.query(),至此整个ContentResolver.query()流程分析完毕

// frameworks\base\core\java\android\content\ContentProvider.java
/**
 * Binder object that deals with remoting.
 *
 * @hide
 */
// Transport为ContentProvider内部类
class Transport extends ContentProviderNative {
    ......
    volatile ContentInterface mInterface = ContentProvider.this;
    @Override
    public Cursor query(@NonNull AttributionSource attributionSource, Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable ICancellationSignal cancellationSignal) {
        uri = validateIncomingUri(uri);
        uri = maybeGetUriWithoutUserId(uri);
        if (enforceReadPermission(attributionSource, uri)
                != PermissionChecker.PERMISSION_GRANTED) {
            // 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;
            final AttributionSource original = setCallingAttributionSource(
                    attributionSource);
            try {
                cursor = mInterface.query(
                        uri, projection, queryArgs,
                        CancellationSignal.fromTransport(cancellationSignal));
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            } finally {
                setCallingAttributionSource(original);
            }
            if (cursor == null) {
                return null;
            }
            // Return an empty cursor for all columns.
            return new MatrixCursor(cursor.getColumnNames(), 0);
        }
        traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
        final AttributionSource original = setCallingAttributionSource(
                attributionSource);
        try {
            return mInterface.query(
                    uri, projection, queryArgs,
                    CancellationSignal.fromTransport(cancellationSignal));
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        } finally {
            setCallingAttributionSource(original);
            Trace.traceEnd(TRACE_TAG_DATABASE);
        }
    }
    ......
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/53838.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

项目构建生命周期与插件

项目构建生命周期描述的是一次构建过程经历了多少个事件。 maven对项目构建的生命周期划分为3套&#xff1a; clean&#xff1a;清理工作。 default&#xff1a;核心工作&#xff0c;例如编译、测试、打包、部署等。 site&#xff1a;产生报告&#xff0c;发布站点等。 clean生…

工具-Obsidian生产力工具,安装第三方插件(GitHub)教程,以安装Syntax Highlight(代码高亮)为例

文章目录1、去GitHub上找到你需要的插件2、下载到本地3、在obsidian中新建文件4、将下载好的GitHub文件放置文件夹5、obsidian中设置6、插入代码块实例1、去GitHub上找到你需要的插件 在GitHub的搜索框中&#xff0c;直接搜索obsidian 插件名&#xff0c;obsidianSyntax Highl…

Halcon 图片分割 米粒分水岭(高斯滤波,区域距离计算,分水岭处理)

资源&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1kmxdMk67E-7QCsG5mKnc7A 提取码&#xff1a;555s 图片 代码 * 1.读取并显示图片 ************************************* dev_close_window () read_image (Image, ./img.png) get_image_size (Image, Width, He…

JVM垃圾回收

JVM 快速开始&#xff1a; 请谈谈你对JVM 的理解&#xff1f;java8 的虚拟机有什么更新&#xff1f; 什么是OOM &#xff1f;什么是StackOverflowError&#xff1f;有哪些方法分析&#xff1f; JVM 的常用参数调优你知道哪些&#xff1f; 内存快照抓取和MAT分析DUMP文件知道…

Android databinding的接入使用与详解(一)

一、介绍 DataBinding 是Google Android组件框架&#xff0c;管理view和data之间进行绑定。DataBinding主要管理数个布局文件&#xff0c;这样我们就不用去实例化layout的view。直接通过DataBindingUitl来完成初始化。 这样可以精简代码&#xff0c;也减少工作量&#xff0c;避…

2022双十二有哪些值得入手的数码好物?值得入手的数码好物推荐

双十二快到了&#xff0c;不少人都会选择在这个时候入手数码产品&#xff0c;但又不知道有哪些值得入手。下面&#xff0c;我来给大家推荐几款实用性高&#xff0c;入手性强的数码好物&#xff0c;感兴趣的一起来看看吧。 一、南卡小音舱蓝牙耳机 推荐理由&#xff1a;蓝牙5.…

带你初识JSP(JAVA服务器页面)

文章目录前言第一个 JSP 程序什么是Java Server Pages?为什么使用JSP&#xff1f;JSP的优势配置Java开发工具&#xff08;JDK&#xff09;设置Web服务器&#xff1a;Tomcat设置 CLASSPATH 环境变量JSP 结构JSP 处理JSP 生命周期JSP编译JSP初始化JSP执行JSP清理前言 JSP 与 PH…

ABAP CLEAR REFRESH FREE 说明(刘欣)

本文仔细测试总结了ABAP中的clear、refresh、free&#xff0c;因为很多时候程序的BUG就是出现在变量没有清理干净&#xff0c;希望整理一个定式出来以后少出BUG。 用clear、refresh、free对带表头的表执行的测试结果如下表&#xff1a; 看起来&#xff0c;最好的避免这些清空命…

Oracle11g安装

参考教程 Oracle11g安装配置详细教程 oracle11g安装步骤详细图文教程 但是这里的用户名如果是以system的话&#xff0c;密码错误 Oracle默认账号密码&#xff1a; &#xff08;1&#xff09;普通用户&#xff1a; SCOTT &#xff08;密码&#xff1a;tiger&#xff09; &…

Day17--购物车页面-商品列表-封装NumberBox

提纲挈领&#xff1a; 官方文档提供了uni-number-box组件 文档内容&#xff1a; 我的操作&#xff1a; 1》修改 my-goods.vue 组件的源代码&#xff0c;在类名为 goods-info-box 的 view 组件内部渲染 NumberBox 组件的基本结构&#xff1a; 2》美化其样式 *****************…

基于最小均方误差linear minimum mean square error(LMMSE)插值算法的图像超分辨重构研究-附Matlab代码

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、图像复原基本原理✳️ 三、基于多通道LMMSE图像复原法✳️ 3.1 最小均方误差LMMSE插值理论✳️ 3.2 理论公式对应的Matlab关键代码✳️ 四、实验验证✳️ 五、参考文献✳️ 六、Matlab程序获取与验证✳️ 一、引言 图像是一种表达信息的…

进程与线程的相爱相杀

✨✨hello&#xff0c;愿意点进来的小伙伴们&#xff0c;你们好呐&#xff01; &#x1f43b;&#x1f43b;系列专栏&#xff1a;【JavaEE初阶】 &#x1f432;&#x1f432;本篇内容&#xff1a;详解进程与线程 &#x1f42f;&#x1f42f;作者简介:一名现大二的三非编程小白&…

Linux文件系统

Linux文件系统 文章目录Linux文件系统1.对文件系统的理解1.1 文件系统当中的缓冲区1.2 文件系统当中的inode1.3 文件属性与文件数据分开存放原理2.对软硬链接的理解扩展&#xff1a;对文件三时间的理解1.对文件系统的理解 1.1 文件系统当中的缓冲区 我们来看看下面这段代码&a…

基于K8s构建Jenkins持续集成平台(部署流程)(转了一点)

转载至&#xff1a;https://blog.csdn.net/m0_59430185/article/details/123394853 一、传统Jenkins的Master-Slave方案的缺陷 Master节点发生单点故障时&#xff0c;整个流程都不可用了 每个 Slave节点的配置环境不一样&#xff0c;来完成不同语言的编译打包等操作&#xff0…

【Uni-App】点击分享,生成海报带二维码,保存到本地图片

目录一&#xff1a;需求二&#xff1a;分析三&#xff1a;准备工作1.qrcode准备2.并且在main.js去挂载四&#xff1a;页面构建1.html2.data3.js一&#xff1a;需求 1.产品需要这个商品&#xff0c;必须分享才有购买资格 2.一共三点&#xff0c;有海报&#xff0c;有二维码&…

k8s安装3节点集群Fate v1.8.0

集群配置信息 3节点配置信息如下图&#xff1a; 当kubefate最新版是1.9.0时&#xff0c;依赖的k8s和ingress-ngnix版本如下&#xff1a; Recommended version of dependent software: Kubernetes: v1.23.5 Ingress-nginx: v1.1.3 升级K8S到1.23.5 参考博客 https://blog.c…

HarmonyOS应用API手势方法-SwipeGesture

描述&#xff1a;用于触发滑动事件&#xff0c;滑动最小速度为100vp/s时识别成功。 Api&#xff1a;从API Version 8开始支持 接口&#xff1a;SwipeGesture(value?: { fingers?: number; direction?: SwipeDirection; speed?: number }) 参数&#xff1a; SwipeDirecti…

代码随想录刷题记录 day32无重叠区间 划分字母区间 合并区间

代码随想录刷题记录 day32无重叠区间 划分字母区间 合并区间 435. 无重叠区间 思想 其实要换个角度想&#xff0c;存放下尽可能多的区间&#xff0c;就是移除的最少的区间。可以按照左右边界进行排序。 1.如果按照右边界进行排序&#xff0c;从左往右遍历&#xff0c;选择右…

数字化门店转型| 舞蹈室管理系统| 门店小程序开发教程

很多拥有跳舞天赋的孩子从小家长就送到专业的舞蹈培训机构进行训练&#xff0c;即使成年也可以学&#xff0c;近些年来各种偶像节目繁多&#xff0c;也使得舞蹈行业市场规模增加&#xff0c;同时各个城市的舞蹈工作室也在逐年增加&#xff0c;线上各种宣传信息也比较多。 同时&…

【重磅】2022年CCF推荐期刊目录 (拟定版) 已发布~

重磅&#xff01;CCF推荐国际学术会议和期刊目录-2022(拟定版)已发布&#xff0c;一起看看有哪些新调整吧&#xff1a; MICCAI 首次被收录&#xff0c;空降B类会议&#xff1b; PRCV 首次被收录&#xff0c;空降C类会议&#xff1b; NAACL 从C类升级到B类会议&#xff1b; …