Android 面试题之ContentProvider使用+实例

简介: 一、什么是ContentProvider二、什么是Uri三、什么是ContentResolver四、创建ContentProvider五、使用ContentProvider六、跨应用使用ContentProvider七、java.lang.SecurityException: Permission Denial: opening provider com.scc.cp.UserProvider from ProcessRecord源码:ContentProviderDemo.zip

好问的人,只做了五分钟的愚人;耻于发问的人,终身为愚人。


  Android提供了5种方式来让用户保存持久化应用程序数据。根据自己的需求来做选择,比如数据是否是应用程序私有的,是否能被其他程序访问,需要多少数据存储空间等,分别是:


  1. 使用SharedPreferences存储数据


  1. 文件存储数据


  1. SQLite数据库存储数据


  1. 「使用ContentProvider存储数据」

  1. 网络存储数据


       咱们今天就来学习一下ContentProvider。作为Android四大巨头之一的他有点名不副实,因为在一般的开发过程中,使用的次数比较少。大部分应用的数据缓存用SharedPreferences就能搞定了。


「ContentProvider(数据提供者)是应用程序之间共享数据的一种接口机制,是一种更为高级的数据共享方法。」

       ContentProvider可以指定需要共享的数据,而其他应用程序则可以在不知道数据来源、路径的情况下,对共享数据进行增删改查等操作。


       在Android系统中,许多Android系统内置的数据也是通过ContentProvider提供给用户使用,例如通讯录、音视频文件和图像文件等。


微信图片_20220520120022.png


一、什么是ContentProvider


ContentProvider是Android的四大组件之一,以标准化的方式在Android 应用间共享数据。


ContentProvider封装的数据存储以及增删改查等,并且必须实现一个对外统一的接口(Uri)。


二、什么是Uri


       Uri(通用资源标识符 Universal Resource Identifer),代表数据操作的地址,每一个ContentProvider都会有唯一的地址。


ContentProvider使用的Uri语法结构如下:


content://authority/data_path/id


  • 「content://」 是通用前缀,表示该Uri用于ContentProvider定位资源。


  • 「authority」 是授权者名称,用来确定具体由哪一个ContentProvider提供资源。因此一般authority都由类的小写全称组成,以保证唯一性。


  • 「data_path」 是数据路径,用来确定请求的是哪个数据集。


  • 「id」 是数据编号,用来请求单条数据。如果是多条这个字段忽略。


样例:


content://com.scc.userprovider/user多条
content://com.scc.userprovider/user/10单条


三、什么是ContentResolver


  • ContentResolver是数据调用者,ContentProvider将数据发布出来,通过ContentResolver对象结合Uri进行调用。


  • 一般来说ContentProvider是单例模式,多个应用可通过ContentResolver调用ContentProvider的增删改查操作数据,ContentResolver调用的数据操作会让同一个ContentProvider处理。


微信图片_20220520120249.png


四、创建ContentProvider


1、创建一个类让其继承ContentProvider,并重载6个函数


微信图片_20220520120344.png


需要实现的主要方法是:


  • 「insert()」、「delete()」、「update()」、「query()」:用于对数据集的增删改查操作。


  • 「onCreate()」:一般用来初始化底层数据集和建立数据连接等工作


  • 「getType()」:用来返回指定Uri的MIME数据类型,


  • 若Uri是单条数据,则返回的MIME数据类型以vnd.Android.cursor.item开头;


  • 若Uri是多条数据,则返回的MIME数据类型以vnd.android.cursor.dir/开头。


       数据访问方法「如insert(Uri,ContentValues)和update(Uri,ContentValues,Bundle)」 可以同时从多个线程调用,并且必须是线程安全的。其他方法「如onCreate()」 仅从应用程序主线程调用,并且必须避免执行冗长的操作。请参阅其预期线程行为的方法描述。


2、声明Uri规则,实现UriMatcher


       咱先来看看UriMatcher是干嘛的,UriMatcher本质上是一个文本过滤器,有助于解析Uri,用在ContentProvider中帮助我们过滤,分辨出查询者想要查询哪个数据表。


       UriMatcher的构造函数中,UriMatcher.NO_MATCH是Uri无匹配时的返回代码,值为-1。addUri()方法用来添加新的匹配项,语法为:


public void addUri(String authority, String path, int code)


  • authority表示匹配的授权者名称;
  • path表示数据路径;
  • code表示返回代码。


下面咱搞个实例:


//这里的名称必须与AndroidManifest.xml中android:authorities保持一致
public static final String AUTHORITY = "com.scc.userprovider";
//数据路径
public static final String PATH_USERS = "user";
//访问ContentProvider的URL
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + PATH_USERS);
//返回代码
public static final int USER_INFO = 1;
//创建UriMatcher对象
private static UriMatcher uriMatcher;
//创建静态代码块
static {
    //实例化UriMatcher对象
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    //参数1:authority;参数2:路径;参数3:自定义代码
    uriMatcher.addURI(UserInfoContent.AUTHORITY, UserInfoContent.PATH_USERS, USER_INFO);
}


3、注册ContentProvider


在AndroidManifest.xml文件中的 application节点下使用标签注册。样例:


<!--
android:name指定ContentProvider实现的类名
android:authorities指定ContentProvider对应Uri(相当于ContentProvider分配一个域名)
android:exported指定ContentProvider是否允许其他应用调用。
如果将该属性设置为true,则允许其他应用调用-->
<android:authorities="com.scc.userprovider"
    android:name=".UserProvider"
    android:exported="true"/>


五、使用ContentProvider


1、通过insert()方法添加单条数据


ContentValues cv = new ContentValues();
cv.put(UserInfoContent._ID, bean.get_id());
cv.put(UserInfoContent.USER_NAME, bean.getName());
cv.put(UserInfoContent.USER_AGE, bean.getAge());
cv.put(UserInfoContent.USER_UPDATE_TIME, bean.getUpdate_time());
Uri uri = getContentResolver().insert(UserInfoContent.CONTENT_URI, cv);
Log.e(getClass().getName(), "insert:" + uri);


  • 通过bulkInsert()方法添加多条数据


1.ContentValues[] arrayValues = new ContentValues[10];
//实例化每一个ContentValues...
int count = getContentResolver().bulkInsert(UserInfoContent.CONTENT_URI, arrayValues);


微信图片_20220520120716.png


2、指定ID删除单条数据


int delete = getContentResolver().delete(UserInfoContent.CONTENT_URI, "_id=12", null);
Log.e(getClass().getName(), "delete(失败返回-1):" + delete);


  • 通过selection语句删除多条数据


String selection = UserInfoContent._ID + ">12";
int result = getContentResolver().delete(UserInfoContent.CONTENT_URI, selection, null);


微信图片_20220520120825.png


3、修改数据


UserInfoBean bean = new UserInfoBean("蚩尤", 32, "12:00");
ContentValues cv = new ContentValues();
cv.put(UserInfoContent.USER_NAME, bean.getName());
cv.put(UserInfoContent.USER_AGE, bean.getAge());
cv.put(UserInfoContent.USER_UPDATE_TIME, bean.getUpdate_time());
getContentResolver().update(UserInfoContent.CONTENT_URI, cv, "_id=18", null);


4、查询数据


Cursor cursor = getContentResolver().query(UserInfoContent.CONTENT_URI, null, selection, null, null);
//循环取出游标指向的每条用户记录
while (cursor.moveToNext()) {
    UserInfoBean user = new UserInfoBean();
    user.name = cursor.getString(cursor.getColumnIndex(UserInfoContent.USER_NAME));
    user.age = cursor.getInt(cursor.getColumnIndex(UserInfoContent.USER_AGE));
    user._id = cursor.getString(cursor.getColumnIndex(UserInfoContent._ID));
    user.update_time = cursor.getString(cursor.getColumnIndex(UserInfoContent.USER_UPDATE_TIME));
    userList.add(user); //添加到用户信息列表
}
cursor.close(); //关闭数据库游标
Log.e(getClass().getName(), "Query用户:" + String.format("当前共找到%d个用户", userList.size()));


微信图片_20220520120922.png


六、跨应用使用ContentProvider


跨应用和本应用使用ContentProvider一样的方法,这边就不做复制了。


例五的Uri是拼接字段,拼接后的结果 :


「content://com.scc.userprovider/user。」

跨平台使用getContentResolver().方法 的第一个参数:


「Uri uricontent = Uri.parse("content://com.scc.userprovider/user");」

1、新增数据+查找数据


微信图片_20220520121541.png


2、修改数据+删除数据


微信图片_20220520121606.png


七、java.lang.SecurityException: Permission Denial: opening provider com.scc.cp.UserProvider from ProcessRecord


解决方案:


在AndroidManifest.xml文件中的 application节点下使用标签注册时android:exported="false"时,不允许其他应用调用


所以其他和应用使用ContentProvider会崩溃报错。将exported改为:android:exported="true"即可。


源码:ContentProviderDemo.zip

相关文章
|
5天前
|
Java Android开发
Android面试题经典之Glide取消加载以及线程池优化
Glide通过生命周期管理在`onStop`时暂停请求,`onDestroy`时取消请求,减少资源浪费。在`EngineJob`和`DecodeJob`中使用`cancel`方法标记任务并中断数据获取。当网络请求被取消时,`HttpUrlFetcher`的`cancel`方法设置标志,之后的数据获取会返回`null`,中断加载流程。Glide还使用定制的线程池,如AnimationExecutor、diskCacheExecutor、sourceExecutor和newUnlimitedSourceExecutor,其中某些禁止网络访问,并根据CPU核心数动态调整线程数。
15 2
|
3天前
|
存储 安全 Java
Android面试题之ArrayList源码详解
ArrayList是Java中基于数组实现的列表,提供O(1)的索引访问,但插入和删除操作平均时间复杂度为O(n)。默认容量为10,当需要时会通过System.arraycopy扩容。允许存储null,非线程安全。面试常问:List是接口,ArrayList是其实现之一,推荐使用List接口编程以实现更好的灵活性。更多详情见[ArrayList源码](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ArrayList.java#ArrayList.Node)。
10 2
|
3天前
|
Android开发
Android面试题经典之如何全局替换App的字体
在Android应用中替换字体有全局和局部方法。全局替换涉及在`Application`的`onCreate`中设置自定义字体,并创建新主题。局部替换则可在布局中通过`ResourcesCompat.getFont()`加载字体文件并应用于`TextView`。
16 2
|
5天前
|
算法 Java API
Android性能优化面试题经典之ANR的分析和优化
Android ANR发生于应用无法在限定时间内响应用户输入或完成操作。主要条件包括:输入超时(5秒)、广播超时(前台10秒/后台60秒)、服务超时及ContentProvider超时。常见原因有网络、数据库、文件操作、计算任务、UI渲染、锁等待、ContentProvider和BroadcastReceiver的不当使用。分析ANR可借助logcat和traces.txt。主线程执行生命周期回调、Service、BroadcastReceiver等,避免主线程耗时操作
18 3
|
5天前
|
API Android开发
Android 监听Notification 被清除实例代码
Android 监听Notification 被清除实例代码
|
10天前
|
SQL XML Java
Android 这 13 道 ContentProvider 面试题,你都会了吗?
Android 这 13 道 ContentProvider 面试题,你都会了吗?
|
10天前
|
安全 Android开发 Kotlin
Android面试题之Kotlin协程并发问题和互斥锁
Kotlin的协程提供轻量级并发解决方案,如`kotlinx.coroutines`库。`Mutex`用于同步,确保单个协程访问共享资源。示例展示了`withLock()`、`lock()`、`unlock()`和`tryLock()`的用法,这些方法帮助在协程中实现线程安全,防止数据竞争。
13 1
|
11天前
|
安全 网络协议 算法
Android网络基础面试题之HTTPS的工作流程和原理
HTTPS简述 HTTPS基于TCP 443端口,通过CA证书确保服务器身份,使用DH算法协商对称密钥进行加密通信。流程包括TCP握手、证书验证(公钥解密,哈希对比)和数据加密传输(随机数加密,预主密钥,对称加密)。特点是安全但慢,易受特定攻击,且依赖可信的CA。每次请求可能复用Session ID以减少握手。
18 2
|
12天前
|
缓存 网络协议 Android开发
Android网络面试题之Http1.1和Http2.0
HTTP/1.1 引入持久连接和管道机制提升效率,支持分块传输编码和更多请求方式如PUT、PATCH。Host字段指定服务器域名,RANGE用于断点续传。HTTP/2变为二进制协议,实现多工处理,头信息压缩和服务器推送,减少延迟并优化资源加载。HTTP不断发展,从早期的简单传输到后来的高效交互。
18 0
Android网络面试题之Http1.1和Http2.0
|
7天前
|
缓存 编解码 安全
Android经典面试题之Glide的缓存大揭秘
Glide缓存机制包括内存和硬盘缓存。内存缓存使用弱引用的ActiveResources和LRU策略,硬盘缓存利用DiskLruCache。Engine.load方法首先尝试从内存和弱引用池加载,然后从LRU缓存中加载图片,增加引用计数并移出LRU。若缓存未命中,启动新任务或加入现有任务。内存大小根据设备内存动态计算,限制在0.4以下。DiskLruCache使用自定义读写锁,保证并发安全,写操作通过锁池管理,确保高效。
9 0