Android 面试题之ContentProvider使用+实例

举报
帅次 发表于 2021/12/23 00:07:37 2021/12/23
【摘要】 好问的人,只做了五分钟的愚人;耻于发问的人,终身为愚人。         Android提供了5种方式来让用户保存持久化应用程序数据。根据自己的需求来做选择,比如数据是否是应用程序私有的,是否能被其他程序访问,需要多少数据存储空间等,分别是: ...

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

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

  1. 使用SharedPreferences存储数据

  2. 文件存储数据

  3. SQLite数据库存储数据

  4. 「使用ContentProvider存储数据」

  5. 网络存储数据

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

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

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

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

一、什么是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」 是数据编号,用来请求单条数据。如果是多条这个字段忽略。

样例:


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

三、什么是ContentResolver

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

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

四、创建ContentProvider

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

需要实现的主要方法是:

  • 「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表示返回代码。

下面咱搞个实例:


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

3、注册ContentProvider

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


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

五、使用ContentProvider

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


  
  1. ContentValues cv = new ContentValues();
  2. cv.put(UserInfoContent._ID, bean.get_id());
  3. cv.put(UserInfoContent.USER_NAME, bean.getName());
  4. cv.put(UserInfoContent.USER_AGE, bean.getAge());
  5. cv.put(UserInfoContent.USER_UPDATE_TIME, bean.getUpdate_time());
  6. Uri uri = getContentResolver().insert(UserInfoContent.CONTENT_URI, cv);
  7. Log.e(getClass().getName(), "insert:" + uri);
  • 通过bulkInsert()方法添加多条数据


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

2、指定ID删除单条数据


  
  1. int delete = getContentResolver().delete(UserInfoContent.CONTENT_URI, "_id=12"null);
  2. Log.e(getClass().getName(), "delete(失败返回-1):" + delete);
  • 通过selection语句删除多条数据


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

3、修改数据


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

4、查询数据


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

六、跨应用使用ContentProvider

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

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

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

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

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

1、新增数据+查找数据

2、修改数据+删除数据

七、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

文章来源: shuaici.blog.csdn.net,作者:帅次,版权归原作者所有,如需转载,请联系作者。

原文链接:shuaici.blog.csdn.net/article/details/118222691

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。