赞
踩
作为Android四大组件之一的ContentProvider,虽然在平常使用的过程中亮相并不多,但这不影响其在Android系统中的地位。因为平时我们的应用数据都是内部自我消化和独立的,一旦涉及和其他应用进行数据交互时,ContentProvider的作用就体现出来了。
ContentProvider(内容提供者),作用是为不同的应用之间数据共享,提供统一的接口,通过uri来标识其它应用要访问的数据,通过ContentResolver的增、删、改、查方法实现对共享数据的操作。还可以通过注册ContentObserver来监听数据是否发生了变化来对应的刷新页面。
其中ContentProvider只是承担了一个中间工作者的角色,内部的数据源的操作由提供的应用开发者自己实现,例如Sqlite,文件,XMl,网络等等。
在了解ContentProvider之前,我们先来简单认知一下什么是统一资源标识符(URI)。
URI本质上是一个字符串,这个字符串的作用是唯一地标记资源的位置或者名字。它不仅能够标记万维网的资源,也可以标记其他的,如邮件系统,本地文件系统等任意资源,而资源既可以是存在磁盘上的静态文本,页面数据也可以是由java,php提供的动态服务。
一个正常的URI标识如下图所示,往往由这四部分组成:
而在Android中 我们如果想用ContentProvider进行数据共享,就需要按下述规则进行:
示例: content://com.ftd.test.myprovider/test
大致步骤如下:
ContentProvider
类AndroidManifest
中进行注册首先我们先需要继承ContentProvider,实现其中的方法,如下:
package com.ftd.test; import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** * 内容提供者 * tangxianfeng * 2022.10.29 */ public class MyProvider extends ContentProvider { /** * 进行创建 */ @Override public boolean onCreate() { return false; } /** * 获取数据类型 */ @Nullable @Override public String getType(@NonNull Uri uri) { return null; } /** * 查询数据 */ @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { return null; } /** * 插入数据 */ @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { return null; } /** * 删除数据 */ @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } /** * 更新数据 */ @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { return 0; } }
如下为我用游标进行简单模拟的一些操作,主要涉及查询和插入,实际情况中我们可以用sqlite等数据库进行替代。
package com.ftd.test; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** * 内容提供者 * tangxianfeng * 2022.10.29 */ public class MyProvider extends ContentProvider { private static final String AUTHORITY = "com.ftd.test.myprovider"; //用于区分不同的ContentProvider //UriMatch主要为了区配URI,比如应用A提供了数据,但是并不是说有的应用都可以操作这些数据,只有提供了满足应用A的URI,才能访问A的数据,而UriMatch就是做这个区配工作的 private static final UriMatcher sUriMatcher; //用以表明数据类型 public static final int USER_DIR = 0; public static final int USER_ITEM = 1; private MatrixCursor TestCursor; static { sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); //只对这三种uri进行处理 如果匹配上 通过 sUriMatcher.match(uri)可以得到 对应的Code sUriMatcher.addURI(AUTHORITY, "test", USER_ITEM); sUriMatcher.addURI(AUTHORITY, "test1", USER_ITEM); sUriMatcher.addURI(AUTHORITY, "testmulti", USER_DIR); } /** * 进行创建 */ @Override public boolean onCreate() { //这里可以创建数据库,也可以创建其他类型的数据,如下为我用游标创建的一个表 String[] tableCursor = new String[] { "time", "food", "where" }; TestCursor = new MatrixCursor(tableCursor); TestCursor.addRow(new Object[] { "2022", "烤鸭", "北京" }); TestCursor.addRow(new Object[] { "2021", "肉夹馍", "陕西" }); TestCursor.addRow(new Object[] { "2020", "大饼", "山东" }); return true; } /** * 获取数据类型 */ @Nullable @Override public String getType(@NonNull Uri uri) { if(sUriMatcher.match(uri)==USER_DIR) { // 查询多条数据 return "vnd.android.cursor.dir/testmulti"; }else{ // 查询一条数据 return "vnd.android.cursor.item/testsingle"; } } /** * 查询数据 * uri 代表资源位置的uri * projection 代表返回内容(conlumn名) * selection 设置条件 * selectionArgs是selection的内容 * sortOrder是根据column排序 */ @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { switch (sUriMatcher.match(uri)) { case USER_DIR: return TestCursor; case USER_ITEM: //条件查询等情况处理 break; } return null; } /** * 插入数据 */ @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { if (values==null){ return null; } switch (sUriMatcher.match(uri)) { case USER_DIR: break; case USER_ITEM: String time = (String) values.get("time"); String food = (String) values.get("food"); String where = (String) values.get("where"); TestCursor.addRow(new Object[] { time, food, where }); getContext().getContentResolver().notifyChange(uri, null);//通知指定URI数据已改变 break; default: } return null; } /** * 删除数据 * selection 设置条件 * selectionArgs是selection的内容 */ @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { return 0;//此处就不赘述了,拿到条件后根据条件对数据进行删除即可 } /** * 更新数据 * selection 设置条件 * selectionArgs是selection的内容 */ @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { return 0;//此处就不赘述了,拿到条件后根据条件对数据进行更新即可 } }
<provider
android:authorities="com.ftd.test.myprovider"
android:name="com.ftd.test.MyProvider"
android:exported="true"
android:grantUriPermissions="true"
android:permission="ftd.Provider"
android:readPermission="ftd.Provider"
android:writePermission="ftd.Provider"
android:enabled="true"
android:multiprocess="true"/>
前文提到的android:authorities 就是在这里设置的,用作唯一标识。
ContentProvider而言,有很多权限控制,可以AndroidManifest.xml文件中对节点的属性进行配置,一般使用如下一些属性设置:
在进行数据交互操作时,我们可以通过ContentResolver统一对数据进行管理和操做:
// 获取ContentResolver
ContentResolver resolver = getContentResolver();
// 通过ContentResolver 向ContentProvider中查询数据
Cursor cursor = resolver.query( Uri.parse("content://com.ftd.test.myprovider/testmulti"), null, null, null, null);
while (cursor.moveToNext()){
System.out.println("query data:" + cursor.getString(0) +" "+ cursor.getString(1)+" "+ cursor.getString(2));
// 将表中数据全部输出
}
cursor.close();
// 关闭游标
// 获取ContentResolver
ContentResolver resolver = getContentResolver();
// 插入表中数据
ContentValues values = new ContentValues();
values.put("time", "2002");
values.put("food", "瘦肉丸");
values.put("where", "温州");
// 通过ContentResolver 根据URI 向ContentProvider中插入数据
resolver.insert(Uri.parse("content://com.ftd.test.myprovider/test"),values);
// 获取ContentResolver
ContentResolver resolver = getContentResolver();
// 删除表中数据
resolver.delete(Uri.parse("content://com.ftd.test.myprovider/test"),"time=",new String[]{"2022"});
// 获取ContentResolver
ContentResolver resolver = getContentResolver();
// 更新表中数据
ContentValues values = new ContentValues();
values.put("time", "2002");
values.put("food", "瘦肉丸");
values.put("where", "温州");
resolver.update(Uri.parse("content://com.ftd.test.myprovider/test"),values,,"time=",new String[]{"2022"});
到此一个ContentProvider的大致使用操作就结束了。
1、MIME类型
MIME:全称Multipurpose Internet Mail Extensions,多功能Internet邮件扩充服务。它是一种多用途网际邮件扩充协议,在1992年最早应用于电子邮件系统,但后来也应用到浏览器。MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
简单来说,MIME类型就是用来标识当前的Activity所能打开的文件类型。
举个栗子:
<activity
android:name=".TESTActivity"
android:label="@string/test">
<intent-filter>
<action android:name="com.ftd.test"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/bmp"/>
</intent-filter>
</activity>
这里指定了data域的MimeType值是"image/bmp",即在利用隐式Intent匹配时,只有指定MimeType是"image/bmp"时,才能启用这个Activity,也就是说,这个Activity只能打开image/bmp类型的文件。
典型的调用方式如下:
Intent intent =new Intent();
intent.setAction(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
Intent.ACTION_DIAL这个activity会通过tel:10086获取自己用的通讯录的contentprovider的type. 如果与intentfilter中指定的mimeType一致, 那么证明传过来的这个参数uri是可用的, 那么就打开拨号这个界面。
而在getType中,我们可以看到getType这个函数会根据传进来的URI,相应生成一个代表MimeType的字符串;而此字符串的生成也有规则:
MIME前面的一部分我们按照Google的要求来写,后面一部分就可以根据我们自己的实际需要来写。
2、ontentProvider的底层是采用 Android中的Binder机制来实现的,如果想具体研究该原理,可先去了解Binder机制
1、提供 ContentProvider的应用未唤醒能拿到数据吗
不能,Android中A应用向B应用去通过ContentProvider去获取数据时,是需要先唤醒B应用,在通过B的ContentProvider去获取资源数据。如果B的进程没有被唤醒,就拿不到数据了,此时后台会发现Log:ActivityThread: Failed to find providerinfo for xxx.
2、ContentProvider的getType的作用?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。