ContentResolver只是提供数据的造访接口

Android四大组件之ContentProvider

为了在应用程序之间沟通数据,Android提供了ContentProvider,它是例外应用程序之间举行数据交换的正统API,当三个应用程序需求把本人的数目暴露给其余程序行使时,该应用程序就可经过提供ContentProvider来实现;其余应用程序就可透过ContentResolver来操作ContentResolver暴光的数额。

正文参谋文献:《疯狂Android讲义 :
第2版

ContentProvider简介

ContentProvider是不相同应用程序之间进行数据沟通的正经API,ContentProvider以某种Uri的情势对外提供数据,允许别的使用访问或涂改数据;其余应用程序使用COntentResolver依据Uri去做客操作钦点数量。

设若有些应用程序通过COntentProvider揭破了谐和的数额操作接口,那么不管该应用程序是不是运转,别的应用程序都可透过该接口来操作该应用程序的里边数据,包罗增加数据、删除数据、修改数据、查询数据等。

ContentResolver只是提供数据的访问接口,并不是像网址一样对外提供全体的页面。开荒一个ContentProvider的步调如下所示:

  1. 概念自身的ContentProvider类,该类要求承袭Android提供的ContentProvider基类。
  2. 向Android系统登记ContentProvider,即在AndroidManifest.xml文件中登记那几个ContentProvider,就想注册Activity同样。注册ContentProvider时须求为它绑定3个Uri。

除开,自个儿定义的ContentProvider类还亟需提供如下多少个格局:

  • public boolean
    onCreate():
    该方法在ContentProvider创立后会被调用,当其他应用程序第3回访问ContentProvider时,该ContentProvider会被创立出来,并立刻回调该onCreate()方法。
  • public Uri insert(Uri uri ,ContentValues
    values):
    依赖该Uri插入values对应的多少。
  • public int delete(Uri uri ,String selection ,String[]
    selectionArgs):
    据书上说Uri删除selection条件所相配的全体笔录。
  • public int update(Uri uri ,ContentValues values ,String selection
    ,String[]
    selectionArgs):
    基于Uri修改selection条件所相称的全部笔录。
  • public Cursor query(Uri uri ,String[] projection ,String
    selectionn ,String[] selectionArgs ,String
    sortOrder):
    基于Uri查询出selection条件所相配的成套记下,在那之中projection就是一个列名列表,评释只选抽取钦定的数据列。
  • public String getType(Uri
    uri):
    该方法用于重临当前Uri所代表的数据的MIME类型。假若该Uri对应的数量也许包含多条记下,那么MIME类型字符串应该以vnd.android.cursor.dir/开始;如若该Uri对应的数目只包蕴一条记下,那么MIME类型字符串应该以vnd.android.cursor.item/早先。

ContentProvider 是见仁见智应用程序之间开始展览数据交流的科班
API,ContentProvider 以某种 Uri
的花样对外提供数据,允许其余应用访问或修改数据;别的应用程序使用
ContentResolver 依照 Uri 去访问操作钦命数量。

Uri简介

ContentProvider要求的Uri与互连网的UOdysseyL类似,举个例子如下Uri:

content://org.crazyit.providers.dictprovider/words

它能够分成如下3部分:

  • content://:其壹有个别是Android的ContentProvider规定的,就像上网的商业事务暗中同意是http://一样。暴露ContentProvider、访问ContentProvider的协议默认是content://。
  • org.crazyit.providers.dictprovider:其一某个正是COntentProvider的authorities。系统便是由那些局地来找到操作哪个ContentProvider的。只要访问钦命的ContentProvider,这么些片段正是牢固的,
  • words:能源部分(恐怕说数据部分)。当访问者需求拜访差异能源时,那几个部分是动态改造的。

为了将一个字符串调换来Uri,Uri工具类提供了parse()静态方法。举例,如下代码就能够将
字符串转换为Uri:

Uri  uri = Uri.parse("content://org.crazyit.providers.dictprovider/word/2")

完全地开拓二个 ContentProvider 的手续:

ContentResolver操作数据

ContentProvider的效益是揭发可供操作的数量;别的应用程序则经过ContentProvider来操作ContentProvider所暴光的数目,ContentResolver也便是HttpClient。
Context提供了如下方法来获取ContentResolver对象:

  • getContentResolver():获取该行使暗许的ContentResolver对象。

假若在程序中赢得了ContentResolver对象之后,接下去就可调用ContentResolver的如下方法来操作数据了。

  • insert(Uri uri ,ContentValues
    values):
    向Uri对应的ContentProvider中插入values对应的数目。
  • delete(Uri uri ,String where ,String[]
    selectionArgs):
    除去Uri对应的ContentProvider中where提交相称的数额。
  • update(Uri uri ,ContentValues values ,String where ,String[]
    selectionArgs):
    创新Uri对应的ContentProvider中where提交相称的数据。
  • query(Uri uri ,String[] projection ,STring selection ,String[]
    selectionArgs ,String
    sortOrder):
    查询Uri对应的ContentProvider中where提交相配的数目。

貌似的话,ContentProvider是单实例方式的,当多个应用程序通过ContentResolver来操作ContentProvider提供的数据时,ContentResolver调用的数据操作将会委托给同一个ContentProvider管理。

  1. 概念自个儿的 ContentProvider 类,该类需求后续 Android 提供的
    ContentProvider 基类;
  2. 在 AndroidManifest.xml 文件中登记那几个ContentProvider,在注册时绑定贰个 Uri。

ContentProvider与ContentResolver的关系

从ContentResolver、ContentProvider和Uri的关联来看,无论是ContentResolver,依然ContentProvider,它们所提供的增加和删除改查方法的率先个参数都是Uri。也正是说,Uri是ContentProvider和ContentResolver进行数据沟通的标记。ContentResolver对点名Uri实行增加和删除改查的数额操作,但Uri并不是当真的多少核心,由此这几个增加和删除改查操作会委托给该Uri对应的ContentProvider来达成。

ContentProvider、Uri、ContentResolver叁者之间的涉嫌如下图所示:

0.jpg

以钦赐Uri为标记,ContentResolver能够完成“直接调用”ContentProvider的增删改查方法:

  1. 当A应用调用ContentResolver的insert()方法时,实际上也等于调用了该Uri对应的ContentProvider的insert()方法。
  2. 当A应用调用ContentResolver的update()方法时,实际上约等于调用了该Uri对应的ContentProvider的update()方法。
  3. 当A应用调用ContentResolver的delete()方法时,实际上相当于调用了该Uri对应的ContentProvider的delete()方法。
  4. 当A应用调用ContentResolver的query()方法时,实际上约等于调用了该Uri对应的ContentProvider的query()方法。

Uri 简介

Uri 能够分成如下四个部分:

  1. content:// —— 那几个片段是 Android 的 ContentProvider
    规定的,就如上网的情商暗中认可是 http:// 同样。揭示ContentProvider、访问 ContentProvider 的协议私下认可是 content://;
  2. testContentProvider.toby.person —— 这些片段正是 content:// 的
    authority。系统就是由那么些部分来找到操作哪个
    ContentProvider。只要访问哪个内定的
    ContentProvider,那一个局地总是长久的;
  3. words ——
    财富(数据)部分,当访问者须要拜访分裂能源时,这一个片段是动态改造的。

content://testContentProvider.toby.person/word/2

那时候它要访问的财富为 word/2,那意味着访问 word 数据中 ID 为 二 的数目。

content://testContentProvider.toby.person/word/2/word

那会儿它要访问的能源为 word/二,那代表访问 word 数据中 ID 为 2的多寡的word字段。

开发ContentProvider子类

支出ContentProvider只要如下两步:

  1. 支付1个ContentProvider子类,该子类须要贯彻query()、insert()、update()和delete()等办法。
  2. 在AndroidManifest.xml文件中登记该ContentProvider,钦定android:authorities属性。

ContentProvider、ContentResolver、Uri 的关系

图片 1

图形源于 http-_www.jianshu.com_p_c6c52c3ba66e

从图中得以看看,ContentResolver 能够兑现”直接调用“ ContentProvider 的
CRUD 方法:

  1. 当 A 应用调用 ContentResolver 的 insert()
    方法时,实际上相当于调用了该 Uri 对应的 ContentProvider 的 insert()
    方法;
  2. 当 A 应用调用 ContentResolver 的 update()
    方法时,实际上也等于调用了该 Uri 对应的 ContentProvider 的 update()
    方法;
  3. 当 A 应用调用 ContentResolver 的 delete()
    方法时,实际上也就是调用了该 Uri 对应的 ContentProvider 的 delete()
    方法;
  4. 当A应用调用 ContentResolver 的 query() 方法时,实际上也正是调用了该
    Uri 对应的 ContentProvider 的 query() 方法。

配置ContentProvider

只要为<applicaton…/>成分增加了<provider…/>子元素就可以配置ContentProvider。举个例子如下的配备部分:

<provider
        android:name=".FirstProvider"
        android:authorities="org.crazyit.providers.firstprovider"
        android:exported="true"/>

安顿ContentProvider时平时钦点如下属性:

  • name:点名该ContentProvider的落到实处类的类名。
  • authorities:内定该ContentProvider对应的Uri(相当于为该ContentProvider分配贰个域名。)
  • android:exported:钦点该ContentProvider是不是同意任何应用调用。假若将该属性设为false,那么该ContentProvider将不容许任何应用调用。

为了分明ContentProvider实际能管理的Uri,以及明确每种方法中Uri参数所操作的数据,Android系统提供了UriMatcher工具类,主要提供了之类八个办法:

  • void addURI(String authority ,String path ,int
    code):
    该方法用于向UriMatcher对象注册Uri。当中authority和path组合成1个Uri,而code则象征该Uri对应的标志码。
  • int match(Uri
    uri):
    依赖前边注册的Uri来决断钦命Uri对应的标记码。假诺找不到极其的标识码,就能够回到-一。

Android还提供了一个ContentUris工具类,它是一个操作Uri字符串的工具类,提供了之类三个工具方法:

  • withAppendedId(uri , id):用于为路线加上ID部分。
  • parseId(uri):用于从钦定Uri中深入分析出所涵盖的ID值。

开发 ContentProvider 子类

开垦 ContentProvider 只要如下两步:

  1. 付出2个 ContentProvider 子类,该子类供给达成query()、insert()、update() 和 delete() 等格局;
  2. 在AndroidManifest.xml 文件中登记该 ContentProvider,内定android:authorities 属性。

操作系统的ContentProvider

Android系统本身提供了大批量的ContentProvider,使用ContentResolver操作系统的ContentProvider数据的步骤也是两步:

  1. 调用Context的getContentResolver()获取ContentResolver对象;
  2. 据悉要求调用ContentResolver的insert()、delete()、update()和query()方法操作数据。

Android系统用于管理关系人的ContentProvider的多少个Uri如下:

  • ContactsContract.Contacts.CONTENT_URI:管住关系人的Uri。
  • ContactsContract.CommonDataKinds.Phone.CONTENT_URI:管理关系人的电话的Uri。
  • ContactsContract.CommonDataKinds.Email.CONTENT_URI:管住关系人的E-mail的Uri。

Android为多媒体提供的ContentProvider的Uri如下所示:

  • MediaStore.Audio.Media.EXTERNAL_CONTENT_URI:存款和储蓄在外部存储其上的音频文件内容的ContentProvider的Uri。
  • MediaStore.Audio.Media.INTERNAL_CONTENT_URI:仓库储存在堂弟大内存上的音频文件内容的ContentProvider的Uri。
  • MediaStore.Images.Media.EXTERNAL_CONTENT_URI:仓库储存在表面存款和储蓄器上的图片文件内容的ContentProvider的Uri。
  • MediaStore.Images.Audio.Media.INTERNAL_CONTENT_URI:积累在小弟大内存上的图片文件内容的ContentProvider的Uri。
  • MediaStore.Video.Media.EXTERNAL_CONTENT_URI:积攒在外表存款和储蓄器上的录制文件内容的ContentProvider的Uri。
  • MediaStore.Video.Audio.Media.INTERNAL_CONTENT_URI:积累在手提式有线电话机里面存款和储蓄器上的摄像文件内容的ContentProvider的Uri。

配置 ContentProvider

只要为 <applicaton…/> 成分加多了 <provider…/>
子元素就能够配置 ContentProvider。举例如下的配置部分:

<provider
        android:name=".FirstProvider"
        android:authorities="com.toby.personal.testlistview.FirstProvider"
        android:exported="true"/>

上边是3个归纳的施用示例,示例0一:
示例01,主布局文件的源委:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/container"
    android:background="@color/colorGray"
    android:orientation="vertical"
    >

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/query"
        android:onClick="query"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/insert"
        android:onClick="insert"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/update"
        android:onClick="update"
        />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/delete"
        android:onClick="delete"
        />

</LinearLayout>

示例01 的 FirstProvider.java 文件的开始和结果:

package com.toby.personal.testlistview;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Created by toby on 2017/4/23.
 */

public class FirstProvider extends ContentProvider {

    final private static String TAG = "Toby_Provider";

    @Override
    public boolean onCreate() {
        Log.d(TAG, "========= onCreate is called =========");
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        Log.d(TAG, "========= query is called selection =========> " + selection);
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Log.d(TAG, "========= insert is called values =========> " + values);
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.d(TAG, "========= delete is called selection =========> " + selection);
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.d(TAG, "========= update is called selection =========> " + selection);
        return 0;
    }
}

咱俩要求在 AndroidManifest.xml 中的 application 节点下进入如下代码:

<provider
            android:authorities="com.toby.personal.testlistview.FirstProvider"
            android:name=".FirstProvider"
            android:exported="true"
            />

说起底是主程序文件的代码:

package com.toby.personal.testlistview;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    final private static String TAG = "Toby_Test";

    ContentResolver contentResolver;
    Uri uri = Uri.parse("content://com.toby.personal.testlistview.FirstProvider/");

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

    public void query(View source) {
        Cursor c = contentResolver.query(uri, null, "query_where", null, null);
        Toast.makeText(this, "远端 ContentProvider 返回的 Cursor 为:" + c,
                Toast.LENGTH_LONG).show();
    }

    public void insert(View source) {
        ContentValues values = new ContentValues();
        values.put("name", "jsdhfkjsjh");
        Uri newUri = contentResolver.insert(uri, values);
        Toast.makeText(this, "远端 ContentProvider 新插入记录的 Uri 为:" + newUri,
                Toast.LENGTH_LONG).show();
    }

    public void update(View source) {
        ContentValues values = new ContentValues();
        values.put("name", "jsdhfkjsjh");
        int count = contentResolver.update(uri, values, "update_where", null);
        Toast.makeText(this, "远端 ContentProvider 更新记录数为:" + count,
                Toast.LENGTH_LONG).show();
    }

    public void delete(View source) {
        int count = contentResolver.delete(uri, "update_where", null);
        Toast.makeText(this, "远端 ContentProvider 删除记录数为:" + count,
                Toast.LENGTH_LONG).show();
    }

}

该示例运维之后,调节台的 log 监察和控制效用:

图片 2

展现效果

布署ContentProvider时常见内定如下属性:

  1. name:钦定该ContentProvider的完结类的类名。
  2. authorities:钦定该ContentProvider对应的Uri(也等于为该ContentProvider分配3个域名。)
  3. android:exported:内定该ContentProvider是或不是同意任何应用调用。假若将该属性设为false,那么该ContentProvider将不容许其他使用调用。

为了分明ContentProvider实际能管理的Uri,以及鲜明每一个方法中Uri参数所操作的数目,Android系统提供了UriMatcher工具类,首要提供了之类多少个格局:

  1. void addUSportageI(String authority ,String path ,int
    code):该情势用于向UriMatcher对象注册Uri。当中authority和path组合成3个Uri,而code则表示该Uri对应的标记码。
  2. int match(Uri
    uri):依据前面注册的Uri来决断钦定Uri对应的标识码。假设找不到十三分的标记码,就能回去-一。

例如:

UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI("com.toby.personal.testlistview.FirstProvider", "words", 1);
uriMatcher.addURI("com.toby.personal.testlistview.FirstProvider", "word/#", 2);

其中 # 为通配符,那意味如下相称结果:

uriMatcher.match(Uri.parse("content://com.toby.personal.testlistview.FirstProvider/words"));
// 返回标识码 1
uriMatcher.match(Uri.parse("content://com.toby.personal.testlistview.FirstProvider/word/2"));
// 返回标识码 2
uriMatcher.match(Uri.parse("content://com.toby.personal.testlistview.FirstProvider/word/10"));
// 返回标识码 2

关于到底要求为 UriMatcher 注册多少个 Uri,取决于系统的政工供给。
对于
content://com.toby.personal.testlistview.FirstProvider/words
这么些 Uri,它的财富部分为 words,这种财富经常代表了访问具备数据项;对于
content://com.toby.personal.testlistview.FirstProvider/word/2
这几个Uri,它的财富部分平常代表做客钦定数量项,个中最终3个数值往往代表了该多少的
ID。

除了这几个之外,Android 还提供了三个 ContentUris 工具类,它是三个操作 Uri
字符串的工具类,提供了之类七个工具方法:

  1. withAppendedId(uri , id):用于为路线加上ID部分,举个例子:

Uri uri = Uri.parse("content://com.toby.personal.testlistview.FirstProvider/word");
Uri resultUri = ContentUris.withAppendedId(uri, 2);
// 生成后的 Uri 为 "content://com.toby.personal.testlistview.FirstProvider/word/2"
  1. parseId(uri):用于从钦赐Uri中分析出所包罗的ID值,举例:

Uri uri = Uri.parse("content://com.toby.personal.testlistview.FirstProvider/word/2");
long wordId = ContentUris.parseId(uri); // 获取的结果为:2

监听ContentProvider的数码变动

在事先的介绍中,只要导致了ContentProvider数据发生了改变,程序中就调用如下代码:

getContext().getContentResolver(),notifyChange(uri ,null);

为了在应用程序中监听ContentProvider数据的变动,要求动用Android提供的ContentObserver基类。监听ContentProvider数据变动的监听器须要连续ContentObserver类,一视同仁写该基类所定义的onChange(boolean
selfChange)方法–当所监听的ContentProvider数据产生转移时,该onChange()方法将会被触发。

为了监听钦点ContentProvider的数量变化,须要经过ContentResolver向钦定Uri注册ContentObserver监听器。ContentResolver提供了如下方法来注册监听器:

  • registerContentObserver(Uri uri , boolean notifyForDescendents ,
    ContentObserver observer)

其1法子的四个参数分别代表:

  • uri:该监听器所监听的ContentProvider的Uri。
  • notifyForDescendents:万壹该参数设为true,若是注册监听的Uri为content://abc,nameUri为contetn://abc/xyzcontent://abc/xyz/foo的数据变动时也会触发该监听器;假诺设为false,那么只有content://abc的数码发生更改时才会触发该监听器。
  • observer:监听器实例。

操作系统的 ContentProvider

Android系统本人提供了汪洋的ContentProvider,使用ContentResolver操作系统的ContentProvider数据的手续也是两步:

  1. 调用Context的getContentResolver()获取ContentResolver对象;
  2. 基于须要调用ContentResolver的insert()、delete()、update()和query()方法操作数据。

Android系统用于管理挂钩人的ContentProvider的多少个Uri如下:

  1. ContactsContract.Contacts.CONTENT_U昂CoraI:处理关系人的Uri;
  2. ContactsContract.CommonDataKinds.Phone.CONTENT_UPAJEROI:管理关系人的话机的Uri;
  3. ContactsContract.CommonDataKinds.Email.CONTENT_U本田CR-VI:处理挂钩人的E-mail的Uri。

Android为多媒体提供的ContentProvider的Uri如下所示:

  1. MediaStore.Audio.Media.EXTERNAL_CONTENT_U奥迪Q5I:存款和储蓄在外部存款和储蓄其上的音频文件内容的ContentProvider的Uri;
  2. MediaStore.Audio.Media.INTERNAL_CONTENT_UBMWX五I:存款和储蓄在二哥大内存上的音频文件内容的ContentProvider的Uri;
  3. MediaStore.Images.Media.EXTERNAL_CONTENT_U锐界I:存款和储蓄在表面存款和储蓄器上的图纸文件内容的ContentProvider的Uri;
  4. MediaStore.Images.Audio.Media.INTERNAL_CONTENT_U科雷傲I:存款和储蓄在手提式有线电话机内存上的图纸文件内容的ContentProvider的Uri;
  5. MediaStore.Video.Media.EXTERNAL_CONTENT_U路虎极光I:存款和储蓄在外表存储器上的录像文件内容的ContentProvider的Uri;
  6. MediaStore.Video.Audio.Media.INTERNAL_CONTENT_U科雷傲I:存款和储蓄在大哥伦比亚大学内存上的录制文件内容的ContentProvider的Uri。

提供程序访问的取代情势

提供程序访问的两种取代情势在使用开垦的进度中那么些重中之重:

  • 批量走访:能够经过ContentProviderOperation类中的方法创造一堆访问调用,然后通过ContentResolver.applyBatch()实践它们。
  • 异步查询:相应在独立线程中施行查询。
  • 经过Intent访问数据:就算无法直接向提供程序发送Intent,然而足以向提供程序的应用发送Intent,后者常常具备修改提供程序数据的极品配置。

好的,ContentProvider就介绍那些吗!!招待关切自己的微信公众号!

自个儿的微信公众号.jpg

监听 ContentProvider 的多少变动

在事先的介绍中,只要导致了 ContentProvider
数据发生了退换,程序中就调用如下代码:

getContext().getContentResolver(),notifyChange(uri ,null);

为了在应用程序中监听ContentProvider数据的改换,须要采纳Android提供的ContentObserver基类。监听ContentProvider数据变动的监听器需求持续ContentObserver类,仁同一视写该基类所定义的onChange(boolean
selfChange)方法–当所监听的ContentProvider数据产生转移时,该onChange()方法将会被触发。

为了监听钦赐ContentProvider的数量变化,必要经过ContentResolver向钦定Uri注册ContentObserver监听器。ContentResolver提供了如下方法来注册监听器:

registerContentObserver(Uri uri , boolean notifyForDescendents , ContentObserver observer)

其壹法子的八个参数分别代表:

  1. uri —— 该监听器所监听的ContentProvider的Uri。
  2. notifyForDescendents ——
    假设该参数设为true,假诺注册监听的Uri为content://abc,nameUri为contetn://abc/xyzcontent://abc/xyz/foo的数据变动时也会触发该监听器;借使设为false,那么唯有content://abc的数额发生改造时才会触发该监听器。
  3. observer —— 监听器实例。

提供程序访问的替代方式

提供程序访问的三种替代方式在行使开垦的长河中十分最首要:

  1. 批量造访:能够经过 ContentProviderOperation
    类中的方法创制一群访问调用,然后通过 ContentResolver.applyBatch()
    施行它们。
  2. 异步查询:应该在独立线程中实行查询。
  3. 经过 Intent 访问数据:即便不能够直接向提供程序发送
    Intent,然则能够向提供程序的选拔发送
    Intent,后者经常兼有修改提供程序数据的特级配置。

相关文章