安卓-四大组件-ContentProvider

ContentProvider

android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。这些数据可以存储在文件系统中、在一个SQLite数据库、或以任何其他合理的方式,其他应用可以通过ContentResolver类(见ContentProviderAccessApp例子)从该内容提供者中获取或存入数据.(相当于在应用外包了一层壳),只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中

它的好处:统一数据访问方式

android系统自带的内容提供者(顶级的表示数据库名,非顶级的都是表名)这些内容提供者在SDK文档的android.provider Java包中都有介绍。见:http://developer.android.com/reference/android/provider/package-summary.html

├────Browser
├────CallLog
├────Contacts
│ ├────Groups
│ ├────People
│ ├────Phones
│ └────Photos
├────Images
│ └────Thumbnails
├────MediaStore
│ ├────Albums
│ ├────Artists
│ ├────Audio
│ ├────Genres
│ └────Playlists
├────Settings
└────Video

CallLog:地址和接收到的电话信息

Contact.People.Phones:存储电话号码

Setting.System:系统设置和偏好设置

使用Content Provider对外共享数据的步骤

  1. 继承ContentProvider类并根据需求重写以下方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    //处理初始化操作
    public boolean onCreate();

    /**
    * 插入数据到内容提供者(允许其他应用向你的应用中插入数据时重写)
    * @param uri
    * @param initialValues 插入的数据
    * @return
    */
    public Uri insert(Uri uri, ContentValues initialValues);

    /**
    * 从内容提供者中删除数据(允许其他应用删除你应用的数据时重写)
    * @param uri
    * @param selection 条件语句
    * @param selectionArgs 参数
    * @return
    */
    public int delete(Uri uri, String selection, String[] selectionArgs);

    /**
    * 更新内容提供者已存在的数据(允许其他应用更新你应用的数据时重写)
    * @param uri
    * @param values 更新的数据
    * @param selection 条件语句
    * @param selectionArgs 参数
    * @return
    */
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs);

    /**
    * 返回数据给调用者(允许其他应用从你的应用中获取数据时重写)
    * @param uri
    * @param projection 列名
    * @param selection 条件语句
    * @param selectionArgs 参数
    * @param sortOrder 排序
    * @return
    */
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) ;

    /**
    * 用于返回当前Uri所代表数据的MIME类型
    * 如果操作的数据为集合类型(多条数据),那么返回的类型字符串应该为vnd.android.cursor.dir/开头
    * 例如要得到所有person记录的Uri为content://com.bravestarr.provider.personprovider/person,
    * 那么返回的MIME类型字符串应该为"vnd.android.cursor.dir/person"
    * 如果操作的数据为单一数据,那么返回的类型字符串应该为vnd.android.cursor.item/开头
    * 例如要得到id为10的person记录的Uri为content://com.bravestarr.provider.personprovider/person/10,
    *   那么返回的MIME类型字符串应该为"vnd.android.cursor.item/person"
    * @param uri
    */
    public String getType(Uri uri)

    这些方法中的Uri参数,得到后需要进行解析然后做对应处理,Uri表示要操作的数据,包含两部分信息:

    1. 需要操作的contentprovider

    2. contentprovider中的什么数据进行操作,一个Uri格式:结构头://authorities(域名)/路径(要操作的数据,根据业务而定)

      content://com.bravestarr.provider.personprovider/person/10

      说明:contentprovider的结构头已经由android规定为content://authorities用于唯一标识这个contentprovider程序,外部调用者可以根据这个找到他路径表示我们要操作的数据,路径的构建根据业务而定.路径格式如下:

      要操作person表行号为10的记录,可以这样构建/person/10

      要操作person表的所有记录,可以这样构建/person

  2. AndroidManifest.xml中使用<provider>ContentProvider进行配置注册(内容提供者注册它自己就像网站注册域名),ContentProvider采用authoritie(原意授权,可理解为域名)作为唯一标识,方便其他应用能找到

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <!-- authorities属性命名建议:公司名.provider.SomeProvider-->
    <provider
    android:name=".PersonProvider"
    ndroid:authorities="com.bravestarr.provider.personprovider"/>
    ...
    </application>

ContentProvider 管理对结构化数据集的访问。它们封装数据,并提供用于定义数据安全性的机制。 内容提供程序是连接一个进程中的数据与另一个进程中运行的代码的标准界面。

ContentProvider 无法被用户感知,对于一个 ContentProvider 组件来说,它的内部需要实现增删该查这四种操作,它的内部维持着一份数据集合,这个数据集合既可以是数据库实现,也可以是其他任何类型,如 ListMap,内部的 insertdeleteupdatequery 方法需要处理好线程同步,因为这几个方法是在 Binder 线程池中被调用的。

ContentProvider 通过 Binder 向其他组件乃至其他应用提供数据。当 ContentProvider 所在的进程启动时,ContentProvider 会同时启动并发布到 AMS 中,需要注意的是,这个时候 ContentProvideronCreate 要先于 ApplicationonCreate 而执行。

基本使用

1
2
3
4
5
6
7
// Queries the user dictionary and returns results
mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
mProjection, // The columns to return for each row
mSelectionClause // Selection criteria
mSelectionArgs, // Selection criteria
mSortOrder); // The sort order for the returned rows
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Installer extends ContentProvider {

@Override
public boolean onCreate() {
return true;
}

@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 String getType(@NonNull Uri uri) {
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;
}
}
  1. 自己创建一个ContentProvider或往已存在的Provider中写数据,供其他应用访问。
  2. 访问ContentProvider时,可通过ActivityContext的方法getContentResolver来访问
  3. 每个ContentProvider都有个唯一的URI来识别
  4. ContentProvider的生命周期不可人为控制,在进程创建时创建,随进程消亡而消亡。

四大基本组件的总结

四大组件的注册

  • 四大基本组件都需要注册才能使用,每个ActivityserviceContent Provider内容提供者都需要在AndroidManifest文件中进行配置。

  • AndroidManifest文件中未进行声明的activity服务以及内容提供者将不为系统所见,从而也就不可用,而BroadcastReceive广播接收者的注册分静态注册(在AndroidManifest文件中进行配置)和通过代码动态创建并以调用Context.registerReceiver()的方式注册至系统。需要注意的是在AndroidManifest文件中进行配置的广播接收者会随系统的启动而一直处于活跃状态,只要接收到感兴趣的广播就会触发(即使程序未运行)

  • AndroidManifest文件中进行注册格式如下:

    • <activity>元素的name 属性指定了实现了这个activityActivity 的子类。iconlabel 属性指向了包含展示给用户的此activity 的图标和标签的资源文件。
    • <service> 元素用于声明服务
    • <receiver> 元素用于声明广播接收器
    • <provider> 元素用于声明内容提供者

四大组件的激活

  • 容提供者的激活:当接收到ContentResolver 发出的请求后,内容提供者被激活。而其它三种组件──activity服务广播接收器被一种叫做intent 的异步消息所激活

  • Activity的激活通过传递一个Intent 对象至Context.startActivity()Activity.startActivityForResult()以载入(或指定新工作给)一个activity。相应的activity 可以通过调用getIntent() 方法来查看激活它的intent。如果它期望它所启动的那个activity 返回一个结果,它会以调用startActivityForResult()来取代startActivity()。比如说,如果它启动了另外一个Activity 以使用户挑选一张照片,它也许想知道哪张照片被选中了。结果将会被封装在一个Intent 对象中,并传递给发出调用的activityonActivityResult() 方法。

  • 服务的激活可以通过传递一个Intent 对象至Context.startService()Context.bindService()前者Android 调用服务的onStart()方法并将Intent 对象传递给它,后者Android 调用服务的onBind()方法将这个Intent 对象传递给它

  • 发送广播可以通过传递一个Intent 对象至给Context.sendBroadcast()Context.sendOrderedBroadcast()Context.sendStickyBroadcast() Android 会调用所有对此广播有兴趣的广播接收器的onReceive()方法,将intent 传递给它们

四大组件的关闭

  • 内容提供者仅在响应ContentResolver 提出请求的时候激活。

  • 而一个广播接收器仅在响应广播信息的时候激活。所以,没有必要去显式的关闭这些组件。

  • Activity关闭:可以通过调用它的finish()方法来关闭一个activity

  • 服务关闭:对于通过startService()方法启动的服务要调用Context.stopService()方法关闭服务,使用bindService()方法启动的服务要调用Contex.unbindService()方法关闭服务