如果外部应用想要操作数据库暴露的数据时,仅
分类:美高梅网上注册平台

ContentProvider是Android 的四大组件之一。主要用于对外共享数据,也就是说通过将ContentProvider将数据中的资源,分享给其他应用访问。其他应用可以通过ContentProvider对指定应用中的数据进行操作。ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。交互关系如图所示:

以下内容整理自互联网,仅用于个人学习

转载请注明出处:http://www.jianshu.com/p/f0f65683403a

本文出自 LeoYan 的 博客

本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 LeoYan 即可关注。

图片 1ContentProvider的数据交互


ContentProvider 是什么?

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

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

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

看了这个会觉得很奇怪,为什么不直接和数据库交互呢?还要通过ContentProvider进行交互。主要是Android的安全性问题决定的,它的数据库是私有的,所以外部数据无法直接访问这个数据库。所以提供了这个ContentProvider 内容提供者,将数据库的内容提供给外部应用,同时将外部应用的数据存储到数据库中。如果外部应用想要操作数据库暴露的数据时,需要ContentResolver来操作ContentProvider暴露的数据。 一旦某个应用通过ContentProvider暴露了数据,那么不管该应用程序是否启动,其他的应用都能通过该接口操作暴露的数据,对数据进行增删查改的操作。

1. ContentProvider介绍

ContentProvider(内容提供者)是Android的四大组件之一,管理android以结构化方式存放的数据,以相对安全的方式封装数据(表)并且提供简易的处理机制和统一的访问接口供其他程序调用。

URI

URI(统一资源标识符)代表要操作的数据,可以用来标识每个 ContentProvider,这样你就可以通过指定的URI找到想要的 ContentProvider, 从中获取或修改数据。

在 Android 中 URI 的格式如下所示:

content://com.leo.peopleprovider/people/7

它可以分为如下三部分:

  • content://

这个部分是 Android 的 ContentProvider 规定的,就像是上网的协议默认是 http:// 一样。暴露 ContentProvider、访问 ContentProvider 的协议默认是 content:// 。

  • com.leo.peopleprovider

这个部分就是 ContentProvider 的 authorities (主机名)。是唯一标识符,用来定位 ContentProvider。系统就是由这个部分来找到操作哪个 ContentProvider 的。

  • /people

资源部分(或者说数据部分)。指向一个对象集合,一般用表的名字。当访问者需要访问不同资源时,这个部分是动态改变的。

  • /7

指向特定的记录,这里表示操作 people 表 id 为 7 的记录。如果要操作 people 表中 id 为 7 的记录的 name 字段,这部分应为 /7/name 即可。

其中 /people 部分和 /7 部分:是每个 ContentProvider 内部的路径部分

URI 模式匹配通配符

*  匹配的任意长度的任何有效字符的字符串。

# 匹配的任意长度的数字字符的字符串。

如:

content://com.leo.peopleprovider/*   匹配 provider 的任何内容 url

content://com.leo.peopleprovider/people/#   匹配 people 表中的所有行

1、ContentProvider:

2. URI

URI(统一资源标识符)代表要操作的数据,可以用来标识每个ContentProvider,这样你就可以通过指定的URI找到想要的ContentProvider,从中获取或修改数据。

Android中URI的格式:
content://com.example.app.provider/table/1

  • content://: schema,已经由Android所规定为:content://
  • com.example.app.provider:权限(Authority),是URI的授权部分,是唯一标识符,用来定位ContentProvider。
  • table:指向一个对象集合,一般用表的名字,如果没有指定后面“1”部分,则返回全部记录。
  • 1:指向特定的记录。

通配符:

*:匹配的任意长度的任何有效字符的字符串。
#:匹配的任意长度的数字字符的字符串。

content://com.example.app.provider/* 匹配provider的任何内容uri
content://com.example.app.provider/table1/# 匹配table3的所有行

MIME

MIME,全称 Multipurpose Internet Mail Extensions,多功能 Internet 邮件扩充服务。MIME 类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。Android 中的工作方式也是类似的,在 ContentProvider 的 getType(Uri) 方法中,可以显示的返回一个 MIME 类型,该方法返回一个字符串,可以是任意的字符串,当我们显示的返回该 MIME 类型的时候,相当于通过该方法的验证,Provider 可以识别自身其他方法返回的 Cursor 的内容,不需要在进行更多的验证;如果返回其他的字符串 (非 android 能够识别的 MIME 类型,例如直接返回当前的包名),则 Provider 在执行其他方法后,返回 Cursor 类型的时候,需要再次进行验证。MIME 类型一般包含两部分,如:

text/html

text/css

text/xml

application/pdf

分为类型和子类型,Android 遵循类似的约定来定义 MIME 类型,每个内容类型的 Android MIME 类型有两种形式:多条记录(集合)和单条记录。

集合记录:

vnd.android.cursor.dir/自定义

单条记录:

vnd.android.cursor.item/自定义

vnd 表示这些类型和子类型具有非标准的、供应商特定的形式。Android 中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型可以按照格式自己填写。

在使用 Intent 时,会用到 MIME,根据 Mimetype 打开符合条件的活动。

下面分别介绍 Android 系统提供了两个用于操作 Uri 的工具类:ContentUris 和 UriMatcher。

● 一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据暴露出去;● 外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以修改程序的数据。

MIME

MIME是指定某个扩展名的文件用一种应用程序来打开,就像你用浏览器查看PDF格式的文件,浏览器会选择合适的应用来打开一样。Android中的工作方式跟HTTP类似,ContentProvider会根据URI来返回MIME类型,ContentProvider会返回一个包含两部分的字符串。

  • text/html
  • text/css
  • text/xml
  • application/pdf

一个内容URI所对应的MIME字符串主要由三个部分组成,Android对这三个部分做了格式规范:

  1. 必须以vnd开头
  1. 如果内容URI以路径结尾,则后接android.cursor.dir/,如果内容URI以id结尾,则接android.cursor.item/
  2. 最后接上vnd.<authority>.<path>

每个内容类型的Android MIME类型有两种形式:多条记录(集合)和单条记录。

//集合记录 
vnd.android.cursor.dir/自定义 

//单条记录  
vnd.android.cursor.item/自定义

vnd表示这些类型和子类型具有非标准的、供应商特定的形式。Android中类型已经固定好了,不能更改,只能区别是集合还是单条具体记录,子类型可以按照格式自己填写。

ContentUris

ContetnUris 包含一个便利的函数 withAppendedId() 来向 URI 追加一个 id。

Uri uri = Uri.parse("content://com.leo.peopleprovider/people")
Uri resultUri = ContentUris.withAppendedId(uri, 7); 

// 生成后的Uri为:content://com.leo.peopleprovider/people/7

同时提供 parseId(uri) 方法用于从 URL 中获取 ID:

Uri uri = Uri.parse("content://com.leo.peopleprovider/people/7")
long personid = ContentUris.parseId(uri);
// 获取的结果为:7

2、ContentResolver:(操作A应用所暴露的数据)

Android系统提供了两个用于操作Uri的工具类:ContentUris和UriMatcher。

UriMatcher

UriMatcher 本质上是一个文本过滤器,用在 ContentProvider 中帮助我们过滤,分辨出查询者想要查询哪个数据表。

举例说明:

  • 第一步,初始化:
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
// 常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
  • 第二步,注册需要的Uri:
// MULTIPLE_PEOPLE 和 SINGLE_PEOPLE 是两个 int 型数据
matcher.addURI("com.leo.peopleprovider", "people", MULTIPLE_PEOPLE);
matcher.addURI("com.leo.peopleprovider", "people/#", SINGLE_PEOPLE);
// 如果 match() 方法匹配 content://com.leo.peopleprovider/people 路径,返回匹配码为 MULTIPLE_PEOPLE
  • 第三部,与已经注册的Uri进行匹配:
/* 
 * 如果操作集合,则必须以 vnd.android.cursor.dir 开头 
 * 如果操作非集合,则必须以 vnd.android.cursor.item 开头 
 */  
@Override  
public String getType(Uri uri) {  
Uri uri = Uri.parse("content://" + "com.leo.peopleprovider" + "/people");  
    switch(matcher.match(uri)){  
    case MULTIPLE_PEOPLE:  
        return "vnd.android.cursor.dir/people";  
    case SINGLE_PEOPLE:  
        return "vnd.android.cursor.item/people";  
    }  
}

● 外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据;● ContentResolver 可以理解成是HttpClient的作用。

ContentUris

ContetnUris包含一个便利的函数withAppendedId()来向URI追加一个id。

Uri uri = Uri.parse("content://cn.scu.myprovider/user") 
Uri resultUri = ContentUris.withAppendedId(uri, 7);  
//生成后的Uri为:content://cn.scu.myprovider/user/7

同时提供parseId(uri)方法用于从URI中获取ID

Uri uri = Uri.parse("content://cn.scu.myprovider/user/7") 
long personid = ContentUris.parseId(uri); 
//获取的结果为:7

ContentProvider 的主要方法

public boolean onCreate()

ContentProvider 创建后或打开系统后其它应用第一次访问该 ContentProvider 时调用。

public Uri insert(Uri uri, ContentValues values)

外部应用向 ContentProvider 中添加数据。

public int delete(Uri uri, String selection, String[] selectionArgs)

外部应用从 ContentProvider 删除数据。

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):

外部应用更新 ContentProvider 中的数据。

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)

供外部应用从 ContentProvider 中获取数据。

public String getType(Uri uri)

该方法用于返回当前 Url 所代表数据的 MIME 类型。

3、 Uri:Uri是ContentResolver和ContentProvider进行数据交换的标识。

UriMatcher

UriMatcher本质上是一个文本过滤器,用在contentProvider中帮助我们过滤,分辨出查询者想要查询哪个数据表。

使用UriMatcher主要步骤:

  1. 初始化
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码
  1. 注册Uri
//USER 和 USER_ID是两个int型数据 
matcher.addURI("cn.scu.myprovider", "user", USER); 
matcher.addURI("cn.scu.myprovider", "user/#",USER_ID); 
//如果match()方法匹配content://cn.scu.myprovider/user路径,返回匹配码为USER
  1. 匹配已经注册的Uri
/*  
 * 如果操作集合,则必须以vnd.android.cursor.dir开头  
 * 如果操作非集合,则必须以vnd.android.cursor.item开头  
 * */   
@Override   
public String getType(Uri uri) {   
Uri uri = Uri.parse("content://" + "cn.scu.myprovider" + "/user");   
    switch(matcher.match(uri)){   
    case USER:   
        return "vnd.android.cursor.dir/user";   
    case USER_ID:   
        return "vnd.android.cursor.item/user";   
    }   
}

ContentResolver

ContentResolver 通过 URI 来查询 ContentProvider 中提供的数据。除了 URI 以外,还必须知道需要获取的数据段的名称,以及此数据段的数据类型。如果你需要获取一个特定的记录,你就必须知道当前记录的 ID,也就是 URI 中 /7 那部分。

ContentResolver 类提供了与 ContentProvider 类相同签名的四个方法:

public Uri insert(Uri uri, ContentValues values) // 添加

public int delete(Uri uri, String selection, String[] selectionArgs) // 删除

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) // 更新

public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) // 获取

实例代码:

ContentResolver resolver =  getContentResolver();
Uri uri = Uri.parse("content://com.leo.peopleprovider/people");

// 添加一条记录
ContentValues values = new ContentValues();
values.put("name", "fanrunqi");
values.put("age", 24);
resolver.insert(uri, values);  

// 获取 user 表中所有记录
Cursor cursor = resolver.query(uri, null, null, null, "userid desc");
while(cursor.moveToNext()){
   // 操作
}

// 把 id 为 1 的记录的 name 字段值更改新为 finch
ContentValues updateValues = new ContentValues();
updateValues.put("name", "finch");
Uri updateIdUri = ContentUris.withAppendedId(uri, 1);
resolver.update(updateIdUri, updateValues, null, null);

// 删除 id 为 2 的记录
Uri deleteIdUri = ContentUris.withAppendedId(uri, 2);
resolver.delete(deleteIdUri, null, null);

● 每个ContentProvider提供公共的URI来唯一标识其数据集。管理多个数据集的的 ContentProvider 为每个数据集提供了单独的URI。

3. ContentProvider的主要方法

//ContentProvider创建后或打开系统后其它应用第一次访问该ContentProvider时调用。 
public boolean onCreate() 

//外部应用向ContentProvider中添加数据。 
public Uri insert(Uri uri, ContentValues values) 
   
//外部应用从ContentProvider删除数据。 
public int delete(Uri uri, String selection, String[] selectionArgs) 

//外部应用更新ContentProvider中的数据。 
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs): 

//供外部应用从ContentProvider中获取数据。 
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,  
                    String sortOrder)  
   
//该方法用于返回当前Url所代表数据的MIME类型。 
public String getType(Uri uri)

ContentObserver

ContentObserver (内容观察者),目的是观察特定 Uri 引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当 ContentObserver 所观察的 Uri 发生变化时,便会触发它.

下面是使用内容观察者监听短信的例子:

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 注册观察者Observser    
        this.getContentResolver().registerContentObserver(Uri.parse("content://sms"),true,new SMSObserver(new Handler()));

    }

    private final class SMSObserver extends ContentObserver {

        public SMSObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {

            Cursor cursor = MainActivity.this.getContentResolver().query(Uri.parse("content://sms/inbox"), null, null, null, null);

            while (cursor.moveToNext()) {
                StringBuilder sb = new StringBuilder();

                sb.append("address=").append(
                        cursor.getString(cursor.getColumnIndex("address")));

                sb.append(";subject=").append(
                        cursor.getString(cursor.getColumnIndex("subject")));

                sb.append(";body=").append(
                        cursor.getString(cursor.getColumnIndex("body")));

                sb.append(";time=").append(
                        cursor.getLong(cursor.getColumnIndex("date")));

                System.out.println("--------has Receivered SMS::" + sb.toString());
            }
        }
    }
}

同时可以在 ContentProvider 发生数据变化时调用
getContentResolver().notifyChange(uri, null) 来通知注册在此 URI 上的访问者。

public class UserContentProvider extends ContentProvider {
   public Uri insert(Uri uri, ContentValues values) {
      db.insert("user", "userid", values);
      getContext().getContentResolver().notifyChange(uri, null);
   }
}

关注我的微信公众号,会有优质技术文章推送。

微信扫一扫下方二维码即可关注:

公众号

本文由美高梅网上注册平台发布于美高梅网上注册平台,转载请注明出处:如果外部应用想要操作数据库暴露的数据时,仅

上一篇:下面使用对称性加密来加密配置,为证书密钥需 下一篇:没有了
猜你喜欢
热门排行
精彩图文