探索安卓内容提供者:构建、访问和管理数据【复习】

文章目录

  • 一 ContentProvider
    • 1.1 数据模型- **ContentProvider 使用基于数据库模型的简单表格来提供需要共享的数据**,在该表格中,每一表示一条记录,而每一列代表特定类型和含义的数据,并且其中每一条数据记录都包含一个名为“_ID”的字段类标识每条数据。
    • 1.2 Uri(统一资源标识符)
    • 1.3 创建内容提供者
    • 1.4 清单文件
    • 1.5 访问其他程序的数据
      • 1.5.1 访问提供者【了解】
      • 1.5.2 通过ContentProvider查询其他程序数据
      • 1.5.3 query方法
      • 1.5.4 多学一招:UriMatcher类【了解】
  • 二 ContentObserver
    • 2.1 创建ContentObserver
    • 2.2 补充:注册ContetnObserver的时机
  • 三 题目总结

一 ContentProvider

  • 内容提供者(ContentProvider)是Android系统四大组件之一,它是不同应用程序之间进行数据共享的标准API,通过ContentResolver类可以访问ContentProvider中共享的数据。
  • ContentProvider的工作原理如下:
    探索安卓内容提供者:构建、访问和管理数据【复习】_第1张图片
    • A程序使用ContetntProvider暴露数据,才能被其他程序操作。B程序通过ContetnResolver操作A程序暴露出来的数据,A程序将操作的结果返回给ContentResoler,然后ContetnResolver再将操作的结果返回给B程序。

1.1 数据模型- ContentProvider 使用基于数据库模型的简单表格来提供需要共享的数据,在该表格中,每一表示一条记录,而每一列代表特定类型和含义的数据,并且其中每一条数据记录都包含一个名为“_ID”的字段类标识每条数据。

探索安卓内容提供者:构建、访问和管理数据【复习】_第2张图片

1.2 Uri(统一资源标识符)

  • 统一资源标识符(Uniform Resource Identifier,或URI)是一个用于标识某一资源名称的字符串。 该标识允许用户对任何(包括本地和互联网)的资源通过特定的协议进行交互操作。
  • ContentResolver提供一系列增删改查的方法对数据进行操作,并且这些方法以Uri的形式对外提供数据。
  • Uri为内容提供者中的数据建立了唯一标识符。它主要由三部分组成,scheme、authorities和path
    探索安卓内容提供者:构建、访问和管理数据【复习】_第3张图片

1.3 创建内容提供者

内容提供者创建步骤:

  1. 在程序包名处右击选择【New】->【Other】->【Content Provider】选项
  2. 输入内容提供者的Class Name(类名称)和URI Authorities(唯一标识,通常使用包名)
  3. 点击【Finish】按钮创建完成
private class MyObserver extends ContentObserver{
        public MyObserver(Handler handler) {
            super(handler);
        }
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
          }
        //还需要重写增删改查的方法
        //...
    }

1.4 清单文件

  • 内容提供者创建完成后,Android Studio会自动在AndroidManifest.xml中对内容提供者进行注册。
    <application ......>
        <provider
            android:name=".MyContentProvider"
            android:authorities="cn.itcast.mycontentprovider"
            android:enabled="true"
            android:exported="true" >
       provider>
    application>
  • provider中的android:name代表是继承于ContentProvider类的的全路径名称。
  • android:authorities:标识MyContentProvider提供的数据,该值可以是一个或者多个URI authority,authorities之间用分号隔开。
  • android:enabled:标识MyContentProvider提供的数据能否被系统实例化
  • android:exported:用于指示该服务是否能被其他程序应用组件调用或跟他交互; 取值为(true | false),如果设置成true,则能够被调用或交互,否则不能;设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。

1.5 访问其他程序的数据

  • 在不同的应用程序之间交换数据时,应用程序会通过ContentProvider暴露自身数据,便通过ContentResolver对程序暴露的数据进行操作,因此 CntentProvider充当一个中介的角色。由于在使用ContentProvider暴露数据时,提供了相应的 Uri,因此在访问现有的CntentProvider时,要指定相应的 Uri,然后再通过ContentResolver对象来实现对数据的操作。

1.5.1 访问提供者【了解】

  • 从Uri访问 ContentProvider 的常用模式是使用 CursorLoader 在后台运行异步查询。UI中的 Activity 或 Fragment 会调用查询的 CursorLoader,其转而使用 ContentResolver 获取 ContentProvider。如此一来,用户便可在查询运行时继续使用UI。
  • ContentResolver对象在客户 app的进程中,ContentProvider对象在app中,它们之间自动处理IPC。ContentProvider也是数据存储同数据的外在表现的一个抽象层。
    探索安卓内容提供者:构建、访问和管理数据【复习】_第4张图片
  • 注意:为了存取provider,app通常在它的manifest文件中请求特定的权限。
  • 例如,为了从User Dictionary Provider中得到一个单词表,你需要调用ContentResolver.query()。query()方法实际调用User Dictionary Provider中的ContentProvider.query(),如下:
// 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
  • 下表显示了query的参数同SQL SELECT语句的对应关系:
    探索安卓内容提供者:构建、访问和管理数据【复习】_第5张图片

1.5.2 通过ContentProvider查询其他程序数据

  • 步骤:
    1. 通过parse()方法解析Uri
    2. 通过query()方法查询数据
    3. 通过while()遍历查询到的数据
	//获取相应操作的Uri,Uri.parse()方法是将字符串转化成Uri对象。
    Uri uri = Uri.parse("content://cn.itcast.mycontentprovider/person"); 
    //获取ContentResolver对象
    ContentResolver resolver = context.getContentResolver();
    //通过ContentResolver对象查询数据
    Cursor cursor = resolver.query(Uri uri, String[] projection, String selection,
                                     String[] selectionArgs, String sortOrder);
    //通过while()循环将Cursor对象中的数据遍历出来
    while (cursor.moveToNext()) {
        String address = cursor.getString(0); 
        long date = cursor.getLong(1);
        int type = cursor.getInt(2);
   }
    cursor.close();

1.5.3 query方法

ContentResolver的query方法的函数接口如下:

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

参数说明:

  • uri:要查询的数据的地址。
  • projection:要查询的列,即需要返回的数据的字段。
  • selection:查询条件,即需要满足的约束条件。
  • selectionArgs:查询条件的参数,用于替换selection中的占位符。
  • sortOrder:查询结果的排序方式。
  • cancellationSignal:用于取消查询的信号。

返回值:

  • Cursor:查询结果的游标对象。

注意事项:

  • 如果查询不需要使用selection和selectionArgs,可以将它们设置为null。
  • 如果不需要排序,可以将sortOrder设置为null。
  • 如果不需要取消查询,可以将cancellationSignal设置为null。

示例代码:

Uri uri = Uri.parse("content://com.example.provider/user");
String[] projection = {"name", "age"};
String selection = "age > ?";
String[] selectionArgs = {"18"};
String sortOrder = "name ASC";
CancellationSignal cancellationSignal = null;
Cursor cursor = getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder, cancellationSignal);

1.5.4 多学一招:UriMatcher类【了解】

  1. 初始化UriMatcher
    UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    
  2. 将Uri注册到UriMatcher中
    matcher.addURI("cn.itcast.contentprovider", "people", PEOPLE);  
    matcher.addURI("cn.itcast.contentprovider", "person/#", PEOPLE_ID);
    
  3. 与已经注册的Uri进行匹配
    Uri uri = Uri.parse("content://" + "cn.itcast.contentprovider" + "/people");
    int match = matcher.match(uri);
    switch (match){
          case PEOPLE:
           //匹配成功后做的相关操作
           case PEOPLE_ID:
           //匹配成功后做的相关操作
    default:
        return null;
   }   

二 ContentObserver

  1. 内容观察者(ContentObserver)用于观察指定Uri所代表的数据的变化,当ContentObserver观察到指定Uri代表的数据发生变化时,就会触发onChange()方法,此时在onChange()方法中使用ContentResovler可以查询到变化的数据。
  2. 要使用ContentObserver观察数据变化,就必须在ContentProvider中调用ContentResolver的notifyChange()方法。
  • ContentObserver的工作原理如下:
    探索安卓内容提供者:构建、访问和管理数据【复习】_第6张图片
    • 使用contentObserver观察 A程序的数据时,首先要在 A程序的ContentProvider中调用ContentResolver的notifyChange()方法,调用此方法后,当 B操作程序作 A程序中的数据时, A程序会向消息中心发送数据变化的消息。此时C程序会观察到消息中心的数据有变化,会触发 ContentObserver的onChange()方法。
      创建ContentObserver的具体步骤如下:

2.1 创建ContentObserver

  1. 创建一个类继承自ContentObserver,并重写onChange方法。
    public class MyContentObserver extends ContentObserver {
    	//Handler对象是主线程中的Handler对象,也可以是其他线程中的Handler对象
        public MyContentObserver(Handler handler) {
            super(handler);
        }
    	//当MyContentObserver观察到Uri代表的数据发生变化时,程序回调用onChange()方法,并在该方法中处理相关的逻辑
        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
            // 处理数据变化的逻辑
        }
    }
    
  2. 注册内容观察者
    ContentResolver resolver = getContentResolver();
    Uri uri = Uri.parse("content://aaa.bbb.ccc");
    resolver.registerContentObserver(uri, true, new MyContentObserver(new Handler());
    

参数说明:

  • uri:要监听的数据的地址。
  • notifyForDescendants:是否监听uri的所有子路径。
  1. 取消注册内容观察者
    getContentResolver().unregisterContentObserver(contentObserver);
    

通过以上步骤,就可以创建并使用ContentObserver来监听数据的变化。

注意事项:

  • ContentObserver只能监听ContentProvider中数据的变化,而不能监听其他的数据源。
  • ContentObserver的onChange方法在主线程中执行,如果需要执行耗时操作,建议在onChange中开启新的线程来处理。
  • ContentObserver的注册和取消注册应该在合适的时机进行,避免不必要的资源消耗。

  • 下面是一个完整的示例代码,演示如何创建和使用ContentObserver来监听数据的变化:

    public class MainActivity extends AppCompatActivity {
    
        private MyContentObserver contentObserver;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // 创建ContentObserver对象
            contentObserver = new MyContentObserver(new Handler());
            
            // 注册ContentObserver监听数据变化
            Uri uri = Uri.parse("content://com.example.provider/user");
            getContentResolver().registerContentObserver(uri, true, contentObserver);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            // 取消注册ContentObserver
            getContentResolver().unregisterContentObserver(contentObserver);
        }
    
        private class MyContentObserver extends ContentObserver {
            public MyContentObserver(Handler handler) {
                super(handler);
            }
    
            @Override
            public void onChange(boolean selfChange, Uri uri) {
                super.onChange(selfChange, uri);
                // 处理数据变化的逻辑
                Log.d("ContentObserver", "Data changed: " + uri.toString());
                // 可以在这里更新UI或执行其他操作
            }
        }
    }
    
    • 在上述示例中,在MainActivity的onCreate方法中创建了一个ContentObserver对象,并在onDestroy方法中取消注册。在MyContentObserver的onChange方法中,我们可以处理数据变化的逻辑,例如打印日志或更新UI。

2.2 补充:注册ContetnObserver的时机

  • 通常是在Activity、Fragment或Service等组件中的某个方法中注册ContentObserver来监听数据的变化。具体来说,可以在以下方法中注册ContentObserver:
    • 在Activity中,可以在onCreate方法中注册,然后在onDestroy方法中取消注册。
    • 在Fragment中,可以在onCreateView方法中注册,然后在onDestroyView方法中取消注册。
    • 在Service中,可以在onCreate方法中注册,然后在onDestroy方法中取消注册。
  • 具体选择,取决于业务需求和组件的生命周期。
  • 示例代码:
public class MainActivity extends AppCompatActivity {

    private MyContentObserver contentObserver;

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

        // 创建ContentObserver对象
        contentObserver = new MyContentObserver(new Handler());

        // 注册ContentObserver监听数据变化
        Uri uri = Uri.parse("content://com.example.provider/user");
        getContentResolver().registerContentObserver(uri, true, contentObserver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // 取消注册ContentObserver
        getContentResolver().unregisterContentObserver(contentObserver);
    }

    private class MyContentObserver extends ContentObserver {

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

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
            // 处理数据变化的逻辑
            Log.d("ContentObserver", "Data changed: " + uri.toString());
            // 可以在这里更新UI或执行其他操作
        }
    }
}

三 题目总结

  1. 简述内容提供者的工作原理
    • 假设B程序需要操作A程序数据库中的数据,一般需要A程序使用ContentProvider
      暴露数据,才能被其他程序操作。B程序通过ContentResolver操作A程序暴露出来的数据,而A程序会将操作结果返回给ContentResolver,然后ContentResolver再将操作结果返回给B程序。
  2. 简述内容观察者的工作原理
    • 使用ContentObserver观察A程序的数据时,首先要在A程序的ContentProvider中调用ContentResolver的notifyChange()方法。调用此方法后,当B程序操作A程序中的数据时,A程序会向“消息中心”发送数据变化的消息,此时C程序会观察到“消息中心”的数据有变化,会触发ContentObserver的onChange()方法。
  3. 在ContentProvider中ContentUris的作用是提供增删改查的方法

  • 在ContentProvider中,ContentUris类提供了一些方法,用于处理操作数据库中的数据的URI。这些方法可以帮助开发者在增删改查数据时,对URI进行解析和操作。
    具体来说,ContentUris的作用如下:
  1. 解析URI:ContentUris类的parseId()方法可以从URI中解析出对应的记录的ID。这个方法通常在查询或删除单个记录时使用。例如,可以使用ContentUris.parseId(uri)方法从URI中解析出记录的ID。
  2. 构建URI:ContentUris类的withAppendedId()方法可以将ID添加到URI中,构建出一个新的URI。这个方法通常在插入或更新记录时使用。例如,可以使用ContentUris.withAppendedId(baseUri, id)方法将ID添加到baseUri中,构建出新的URI。
  3. 获取记录的URI:ContentUris类的withAppendedId()方法也可以用于获取指定记录的URI。这个方法通常在查询或删除单个记录时使用。例如,可以使用ContentUris.withAppendedId(baseUri, id)方法获取指定记录的URI。
    通过使用ContentUris类的方法,开发者可以方便地解析和操作URI,从而实现对数据库中的数据进行增删改查的操作。

  1. 若要实现对系统联系人的增删改查,需要使用的系统ContentProvider的Uri为Contacts.Phones.CONTENT_URI
  2. 联系人信息内容提供者的主机名是com.android.contacts
    • 联系人信息的内容提供者的主机名是com.android.contacts,它是Android系统中默认的联系人信息提供者。通过该内容提供者,应用程序可以访问和操作设备上的联系人信息。
  3. 下面哪些功能需要用ContentProvider来实现()。
    A、读取系统中的短信内容
    B、建立一个数据库
    C、开机后自动启动一个程序
    D、播放一段音乐
  • 解析:

    • A、读取系统中的短信内容:需要使用ContentProvider来实现。Android系统中的短信内容存储在短信提供者中,开发者需要通过ContentResolver来查询短信提供者,从而读取系统中的短信内容。

    • B、建立一个数据库:不需要使用ContentProvider来实现。建立数据库可以直接使用SQLiteOpenHelper或Room等数据库框架来创建和管理数据库。

    • C、开机后自动启动一个程序:不需要使用ContentProvider来实现。开机后自动启动一个程序可以通过BroadcastReceiver和BOOT_COMPLETED广播来实现。

    • D、播放一段音乐:不需要使用ContentProvider来实现。播放音乐可以使用MediaPlayer或其他音频播放框架来实现,不涉及ContentProvider的使用。

    • 因此,只有读取系统中的短信内容需要使用ContentProvider来实现。其他功能不需要使用ContentProvider。

  1. 短信的内容提供者是()
    A、ContactProvider
    B、MessageProvider
    C、SmsProvider
    D、TelephonyProvider
  2. 在下列选项中,联系人信息内容提供者的主机名是()
    A、contact
    B、com.android.contacts
    C、com.android.provider.contact
    D、com.android.provider.contacts
  3. 内容提供者主要功能是实现跨程序共享数据的功能
  4. provider中的android:name代表是继承于ContentProvider类的的全路径名称。
  5. 为了解析Uri对象,Android系统提供了一个辅助工具类UriMatcher用于匹配Uri。
  6. Android中通过ContentResover.query()查询短信数据库的时候,第一个Uri参数如何写 contentprovider
  7. 内容观察者是通过观察消息中心来观察数据库的变化
  8. 消息中心是用来观察指定Uri所代表的数据
  9. 利用内容解析者读取短信数据库内容时,短信数据库SmsInfo表主要用来存储短信信息
  10. resolver.registerContentObserver()方法用于注册内容观察者
  11. 创建UriMatcher对象时调用UriMatcher(int code),参数通常使用UriMatcher.NO_MATCH,表示路径不满足条件返回_-1
  12. 当ContentObserver观察到指定Uri代表的数据发生变化时,就会触发ContentObserver的**onChange()**方法。
  13. 内容提供者把私有的数据给暴露出来,我们通过ContentResolver来进行查询数据
  14. 在Android中,是通过ContentResolver读取联系人信息的。
  15. 注册provider时需要指定两个属性android.name和android:authorities
  16. 短信数据是存放在sms表中的
  17. Android中通过内容提供者来读取联系人信息,data表用来保存联系人信息的.
    • Android中读取联系人信息 有三张关键的表1 data表用来存储联系人信息的表,2 raw_contacts 表用来存储一共有多少个联系人. 3 mimetype表用来区分联系人信息的表
  18. 在短信接收器案例中,注册短信内容观察者时,使用的到Uri是Content://sms/

你可能感兴趣的:(Andriod学习,android,java,开发语言)