Android 的这 13 道 ContentProvider 面试题,你都会了吗?
前言
- 作为
Android
的四大组件之一,ContentProvider
可以说是无处不在了。 - 但是对于我而言,开发过程中看似
ContentProvider
用得很娴熟,却一直没能形成一个完整的体系。 - 也许大家也有着和我类似的烦恼,于是我特地花了几天的时间,总结了我所知道的知识点,以及面试中可能遇到的问题。将本文分享给大家,希望能帮助大家重新梳理下我们的这个老朋友
ContentProvider
。
希望大家阅读愉快!
文章目录
-
ContentProvider
应用程序间非常通用的共享数据的一种方式,也是Android
官方推荐的方式。 -
Android
中许多系统应用都使用该方式实现数据共享,比如通讯录、短信等。
1.1 Android 为什么要设计 ContentProvider 这个组件?
- 很多做
Android
开发的人都不怎么使用它,觉得直接读取数据库会更简单方便。 - 那么
Android
搞一个内容提供者在数据和应用之间,只是为了装高大上,故弄玄虚?我认为其设计用意在于:
- 封装。对数据进行封装,提供统一的接口,使用者完全不必关心这些数据是在
DB
,XML
、Preferences
或者网络请求来的。当项目需求要改变数据来源时,使用我们的地方完全不需要修改。 - 提供一种跨进程数据共享的方式。
- 应用程序间的数据共享还有另外的一个重要话题,就是数据更新通知机制了。因为数据是在多个应用程序中共享的,当其中一个应用程序改变了这些共享数据的时候,它有责任通知其它应用程序,让它们知道共享数据被修改了,这样它们就可以作相应的处理。
1.2 如何访问自定义 ContentProvider
-
ContentResolver
接口的notifyChange
函数来通知那些注册了监控特定 URI的ContentObserver 对象,使得它们可以相应地执行一些处理。 - ContentObserver 可以通过 registerContentObserver 进行注册。
- 通过
ContentProvider
的Uri
访问开放的数据。
-
ContenResolver
对象通过Context
提供的方法getContenResolver()
来获得。 -
ContenResolver
提供了以下方法来操作:insert
delete
update
query
这些方法分别会调用ContenProvider
中与之对应的方法并得到返回的结果。
1.3 通过 ContentResolver 获取 ContentProvider 内容的基本步骤
- 得到
ContentResolver
类对象:ContentResolver cr = getContentResolver ( )
。 - 定义要查询的字段
String
数组。 - 使用
cr.query()
; 返回一个Cursor
对象。 - 使用
while
循环得到Cursor
里面的内容。
1.4 ContentProvider 是如何实现数据共享的:
- 在
Android
中如果想将自己应用的数据 ( 一般多为数据库中的数据 ) 提供给第三发应用, 那么我们只能通过ContentProvider
来实现了。ContentProvider
是应用程序之间共享数据的接口。 - 使用的时候首先自定义 一个类继承
ContentProvider
, 然后覆写query
、insert
、update
、delete
等 方法。 - 因为其是四大组件之一因此必须在
AndroidManifest
文件中进行注册。 - 把自己的数据通过
uri
的形式共享出去android
系统下 不同程序 数据默认是不能共享访问 需要去实现一个类去继承ContentProvider
。
public class PersonContentProvider extends ContentProvider{ public boolean onCreate(){ } query(Url, String[], String, String[], String); insert(Uri,ContentValues); update(Uri,ContentValues,String[]); delete(Uri,String,String[]); }
1.5 为什么要用 ContentProvider ?它和 sql 的实现上有什么差别?
-
ContentProvider
屏蔽了数据存储的细节 , 内部实现对用户完全透明 , 用户只需要关心操作数据的uri
就可以了,ContentProvider
可以实现不同app
之间 共享。 -
Sql
也有增删改查的方法, 但是sql
只能查询本应用下的数据库。 - 而
ContentProvider
还可以去增删改查本地文件.xml
文件的读取等。
1.6 Uri 介绍
- 为系统的每一个资源给其一个名字,比方说通话记录。
- 每一个
ContentProvider
都拥有一个公共的URI
,这个URI
用于表示这个ContentProvider
所提供的数据。 -
Android
所提供的ContentProvider
都存放在android.provider
包中。
- 将其分为
A,B,C,D
4个部分: -
A
:标准前缀,用来说明一个Content Provider
控制这些数据,无法改变的;"content://"
; -
B
:URI
的标识,用于唯一标识这个ContentProvider
,外部调用者可以根据这个标识来找到它。它定义了是哪个ContentProvider
提供这些数据。对于第三方应用程序,为了保证URI
标识的唯一性,它必须是一个完整的、小写的类名。这个标识在元素的authorities
属性中说明:一般是定义该ContentProvider
的包类的名称; -
C
:路径(path
),通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就可以了;"content://com.bing.provider.myprovider/tablename"
。 -
D
:如果URI中包含表示需要获取的记录的ID
;则就返回该id对应的数据,如果没有ID
,就表示返回全部;"content://com.bing.provider.myprovider/tablename/#"
#
表示数据id
。
1.7 如何访问 asserts 资源目录下的数据库?
- 把数据库
db
复制到/data/data/packagename/databases/
目录下, 然后直接就能访问了。
1.8 多个进程同时调用一个 ContentProvider 的 query 获取数据,ContentPrvoider 是如何反应的呢?
- 一个
ContentProvider
可以接受来自另外一个进程的数据请求。 - 尽管
ContentResolver
与ContentProvider
类隐藏了实现细节,但是ContentProvider
所提供的query()
,insert()
,delete()
,update()
都是在ContentProvider
进程的线程池中被调用执行的,而不是进程的主线程中。 - 这个线程池是有
Binder
创建和维护的,其实使用的就是每个应用进程中的Binder
线程池。
1.9 Android 设计 ContentProvider 的目的是什么呢?
- 隐藏数据的实现方式,对外提供统一的数据访问接口;
- 更好的数据访问权限管理。
ContentProvider
可以对开发的数据进行权限设置,不同的URI
可以对应不同的权限,只有符合权限要求的组件才能访问到ContentProvider
的具体操作。 -
ContentProvider
封装了跨进程共享的逻辑,我们只需要Uri
即可访问数据。由系统来管理ContentProvider
的创建、生命周期及访问的线程分配,简化我们在应用间共享数据( 进程间通信 )的方式。我们只管通过ContentResolver
访问ContentProvider
所提示的数据接口,而不需要担心它所在进程是启动还是未启动。
1.10 运行在主线程的 ContentProvider 为什么不会影响主线程的UI操作?
-
ContentProvider
的onCreate()
是运行在UI
线程的,而query()
,insert()
,delete()
,update()
是运行在线程池中的工作线程的 - 所以调用这向个方法并不会阻塞
ContentProvider
所在进程的主线程,但可能会阻塞调用者所在的进程的UI
线程! - 所以,调用
ContentProvider
的操作仍然要放在子线程中去做。 - 虽然直接的
CRUD
的操作是在工作线程的,但系统会让你的调用线程等待这个异步的操作完成,你才可以继续线程之前的工作。
1.11 外提供数据共享,那么如何限制对方的使用呢?
-
android:exported
属性非常重要。这个属性用于指示该服务是否能够被其他应用程序组件调用或跟它交互。 - 如果设置为
true
,则能够被调用或交互,否则不能。 - 设置为
false
时,只有同一个应用程序的组件或带有相同用户ID
的应用程序才能启动或绑定该服务。 - 对于需要开放的组件应设置合理的权限,如果只需要对同一个签名的其它应用开放
ContentProvider
,则可以设置signature
级别的权限。 - 大家可以参考一下系统自带应用的代码,自定义了
signature
级别的permission
:
<permission android:name="com.android.gallery3d.filtershow.permission.READ" android:protectionLevel="signature" /> <permission android:name="com.android.gallery3d.filtershow.permission.WRITE" android:protectionLevel="signature" /> <provider android:name="com.android.gallery3d.filtershow.provider.SharedImageProvider" android:authorities="com.android.gallery3d.filtershow.provider.SharedImageProvider" android:grantUriPermissions="true" android:readPermission="com.android.gallery3d.filtershow.permission.READ" android:writePermission="com.android.gallery3d.filtershow.permission.WRITE" />
1.11.1 如果我们只需要开放部份的 URI
给其他的应用访问呢?
- 可以参考
Provider
的URI
权限设置,只允许访问部份URI
,可以参考原生ContactsProvider2
的相关代码( 注意path-permission
这个选项 ):
<provider android:name="ContactsProvider2" android:authorities="contacts;com.android.contacts" android:label="@string/provider_label" android:multiprocess="false" android:exported="true" android:grantUriPermissions="true" android:readPermission="android.permission.READ_CONTACTS" android:writePermission="android.permission.WRITE_CONTACTS"> <path-permission android:pathPrefix="/search_suggest_query" android:readPermission="android.permission.GLOBAL_SEARCH" /> <path-permission android:pathPrefix="/search_suggest_shortcut" android:readPermission="android.permission.GLOBAL_SEARCH" /> <path-permission android:pathPattern="/contacts/.*/photo" android:readPermission="android.permission.GLOBAL_SEARCH" /> <grant-uri-permission android:pathPattern=".*" /> </provider>
1.12 ContentProvider 接口方法运行在哪个线程中呢?
-
ContentProvider
可以在AndroidManifest.xml
中配置一个叫做android:multiprocess
的属性,默认值是 false ,表示 ContentProvider 是单例的 - 无论哪个客户端应用的访问都将是同一个
ContentProvider
对象,如果设为true
,系统会为每一个访问该ContentProvider
的进程创建一个实例。
1.12.1 这点还是比较好理解的,那如果我要问每个 ContentProvider 的操作是在哪个线程中运行的呢?( 其实我们关心的是 UI 线程和工作线程 )
- 比如我们在UI线程调用getContentResolver().query查询数据,而当数据量很大时(或者需要进行较长时间的计算)会不会阻塞UI线程呢?
- 要分两种情况回答这个问题:
-
ContentProvider
和调用者在同一个进程,ContentProvider
的方法(query/insert/update/delete
等 )和调用者在同一线程中; -
ContentProvider
和调用者在不同的进程,ContentProvider
的方法会运行在它自身所在进程的一个 Binder 线程中。但是,注意这两种方式在 `ContentProvider` 的方法没有执行完成前都会 `blocked` 调用者。所以你应该知道这个上面这个问题的答案了吧。
- 也可以看看
CursorLoader
这个类的源码,看Google
自己是怎么使用getContentResolver().query
的。
1.13 ContentProvider 是如何在不同应用程序之间传输数据的?
- 一个应用进程有
16
个Binder
线程去和远程服务进行交互,而每个线程可占用的缓存空间是128KB
这样,超过会报异常。 -
ContentResolver
虽然是通过Binder
进程间通信机制打通了应用程序之间共享数据的通道,但ContentProvider
组件在不同应用程序之间传输数据是基于匿名共享内存机制来实现的。 - 有兴趣的可以查看一下老罗的文章Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划。
总结
- 在这篇文章中,我对我所知道的
ContentProvider
知识总进行了详细的总结,希望大家通过本次阅读都能有所收获。 -
重点
:学Android
有一段时间了,我打算好好的梳理一下所学知识,到现在为止,我才总结完Activity
、Service
、BroadcastRecevier
等,有关 事件分发、滑动冲突、新能优化等重要模块,后面也将详尽的总结,欢迎大家关注,方便及时接收更新 。 - 如果有可以补充的知识点,欢迎大家在评论区指出。
最后
今天分享的面试题就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊~

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
云原生安全性如何保护无形资产
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 很多企业对管理IT设备并不感兴趣,只对其结果更感兴趣。他们希望快速交付软件,并专注于其核心产品或服务。这就是云原生计算概念出现,以及提高其安全性变得至关重要的原因。 传统上,企业拥有许多服务器和资源。如今正在朝着更简单的现实迈进。开发人员将其工作重点放在部署业务逻辑所需的内容上,而无论其部署在何处。这就是企业在不使用服务器以及不增加IT开销的复杂管理的情况下迁移到云平台具有吸引力的原因。 如果企业能够在没有硬件、补丁程序管理、存储容量等情况下推广其产品,并且能够以更经济、更快的速度完成这些任务,那将是一场伟大的胜利。 范式变化 2012年是IT行业发展的黄金时代,管理员主导了IT世界,开发人员也加入了这一行列。从那时到2016年,人们已经进入了云计算时代,而开发人员成为主导者。 多年来,IT部门一直在引导或实现组织内部的创新。开发人员的创新路径仅限于他们控制的领域,即应用层。除此之外的一切都是IT部门的职责。这其中包括托管、负载平衡、数据库管理以及是否采用虚拟化或高级网络技术。 随着网络...
- 下一篇
详解 Flink DataStream中min(),minBy(),max(),max()之间的区别
解释 官方文档中: The difference between min and minBy is that min returns the minimum value, whereas minBy returns the element that has the minimum value in this field (same for max and maxBy). 翻译: min和minBy之间的区别是min返回最小值,而minBy返回在此字段中具有最小值的元素(与max和maxBy相同)。 但是事实上,min与max 也会返回整个元素。 不同的是min会根据指定的字段取最小值,并且把这个值保存在对应的位置上,对于其他的字段取了最先获取的值,不能保证每个元素的数值正确,max同理。 而minBy会返回指定字段取最小值的元素,并且会覆盖指定字段小于当前已找到的最小值元素。maxBy同理。 示例论证 先拿min()与minBy()举例: 取第三个元素的最小值 public static void main(String[] args) throws Exception { Strea...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合Redis,开启缓存,提高访问速度
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7设置SWAP分区,小内存服务器的救世主
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作