广播接收者(BroadcastReceiver)和内容提供者(ContentProvider)学习

1. 广播接收者(BroadcastReceiver)

1.1 广播的介绍

1.1.1 基本介绍

Android广播分为两个方面:广播发送者和广播接收者,通常情况下,BroadcastReceiver指的就是广播接收者(广播接收器)。

  • 在Android中,广播是一种广泛运用的在应用程序之间传输信息的机制,主要用于不同组件间通信
  • 广播接收器没有用户界面
  • 自定义广播接收者,需要继承Broadcast Receiver类,且重写onReceive(),广播接收器收到相应广播后,会自动调用onReceive()方法,一般情况下,onReceive()会涉及与其他组件之间的交互,如 发送Notification,启动service等
  • 默认情况下,广播接收器运行在UI线程,因此,onReceive方法不能执行耗时操作,否则将导致ANR (Application Not responding)
1.1.2 使用场景

广播作为Android组件间的通信方式,可以使用的场景如下:

  • 1.同一app内部的同一组件内的消息通信(单个或多个线程之间);

    同一app内部的同一组件内的消息通信(单个或多个线程之间),实际应用中肯定是不会用到广播机制的(虽然可以用),无论是使用扩展变量作用域、基于接口的回调还是Handler-post/Handler-Message等方式,都可以直接处理此类问题;

  • 2.同一app内部的不同组件之间的消息通信(单个进程);

    同一app内部的不同组件之间的消息通信(单个进程),对于此类需求,在有些教复杂的情况下单纯的依靠基于接口的回调等方式不好处理,此时可以直接使用EventBus等,相对而言,EventBus由于是针对统一进程,用于处理此类需求非常适合,且轻松解耦。

  • 3.同一app具有多个进程的不同组件之间的消息通信;

  • 4.不同app之间的组件之间消息通信;

  • 5.Android系统在特定情况下与App之间的消息通信。

    第三、四、五情形:由于涉及不同进程间的消息通信,此时根据实际业务使用广播机制会显得非常适宜。

1.2 广播的分类

1.2.1 系统广播(System Broadcast)

Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。

如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

静态注册:直接在AndroidManifest.xml文件中进行注册,规则如下:

<receiver android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</receiver> 

<!-- 
需要注意的属性
android:exported ——此broadcastReceiver能否接收其他App的发出的广播,这个属性默认值有点意思,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。
同样的,activity/service中的此属性默认值一样遵循此规则)同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程);

android:name —— 此broadcastReceiver类名;

android:permission ——如果设置,具有相应权限的广播发送方发送的广播才能被此broadcastReceiver所接收;

android:process ——broadcastReceiver运行所处的进程。默认为app的进程。可以指定独立的进程(Android四大基本组件都可以通过此属性指定自己的独立进程)
-->

常见的注册形式有:

<receiver android:name=".MyBroadcastReceiver" >
  <!--intent-filter由于指定此广播接收器将用于接收特定的广播类型 -->
  <intent-filter>
    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
  </intent-filter>
  <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED" />
  </intent-filter>
</receiver> 

接收开机广播示例:

<!-- 在AndroidManifest.xml中注册 -->
<?xml version="1.0" encoding="utf-8"?>
<manifest
    .............
    <!-- 开机启动权限 -->
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    .........
    <application 
        .........
        <!-- 接收开机启动广播 -->
        <receiver
                android:name=".BOOTReceiver"
                android:enabled="true"
                android:exported="true"
                android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
            <intent-filter android:priority="1000"> <!-- 设置优先级-1000~10000,值越大越先接收-->
                <!--.接收启动完成的广播-->
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
    </application>
 
</manifest>
public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
            Log.i(TAG,"接收到开机广播");
        }
    }
}

项目中的应用:App开机自启动,例如RunningTest,TpLcdTest

public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
            Log.i(TAG,"接收到开机广播");
            Intent intent = new Intent(context, TpLcdTestActivity.class);
            // 非常重要缺少声明会启动时会报错
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }
    }
} 

一些常见的系统广播:

事件常量描述
android.intent.action.BATTERY_CHANGED持久的广播,包含电池的充电状态,级别和其他信息。
android.intent.action.BATTERY_LOW标识设备的低电量条件。
android.intent.action.BATTERY_OKAY标识电池在电量低之后,现在已经好了。
android.intent.action.BOOT_COMPLETED在系统完成启动后广播一次。
android.intent.action.BUG_REPORT显示报告bug的活动。
android.intent.action.CALL执行呼叫数据指定的某人。
android.intent.action.CALL_BUTTON用户点击"呼叫"按钮打开拨号器或者其他拨号的合适界面。
android.intent.action.DATE_CHANGED日期发生改变。
android.intent.action.REBOOT设备重启。
1.2.2 普通广播(Normal Broadcast)

开发者自己定义的intent,以context.sendBroadcast_”AsUser”(intent, …)形式。具体可以使用的方法有:

sendBroadcast(intent)/sendBroadcast(intent, receiverPermission)

sendBroadcastAsUser(intent, userHandler)/sendBroadcastAsUser(intent, userHandler,receiverPermission)。

普通广播会被注册了的相应的感兴趣(intent-filter匹配)接收,且顺序是无序的。如果发送广播时有相应的权限要求,

BroadCastReceiver如果想要接收此广播,也需要有相应的权限。

动态注册: 无须在AndroidManifest中注册组件。直接在代码中通过调用Context的registerReceiver函数,可以在程序中动态注册BroadcastReceiver。registerReceiver的定义形式如下:

registerReceiver(BroadcastReceiver receiver, IntentFilter filter)

registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)

简单示例如下:

public class MainActivity extends Activity {
  public static final String BROADCAST_ACTION = "com.example.corn";
  private BroadcastReceiver mBroadcastReceiver;

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

    mBroadcastReceiver = new MyBroadcastReceiver();
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BROADCAST_ACTION);
    registerReceiver(mBroadcastReceiver, intentFilter);
  }
  
  @Override
  protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(mBroadcastReceiver);
  }
}
// 注:Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister。因此,上例在onDestroy()回到中需要unregisterReceiver(mBroadcastReceiver)。

// 当此Activity实例化时,会动态将MyBroadcastReceiver注册到系统中。当此Activity销毁时,动态注册的MyBroadcastReceiver将不再接收到相应的广播。
1.2.3 有序广播(Ordered broadcast)

有序广播的有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播无异,只是其的主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, …)。

有序广播示例:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    <application
        ......
        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="100">	//设置优先级-1000~10000,值越大越先接收
                <action android:name="com.android.MYBROADCAST"/>
            </intent-filter>
        </receiver>

        <activity android:name=".MainActivity">
		.......
        </activity>
    </application>

</manifest>
public class MainActivity extends AppCompatActivity {

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

        //发送自定义的有序广播
        Intent intent = new Intent("com.android.MYBROADCAST");
        sendOrderedBroadcast(intent,null);//第二个参数是receivePermission
    }
}

对于有序广播,其主要特点总结如下:

1>多个具当前已经注册且有效的BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序,对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。

2>先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,使后面的BroadcastReceiver接收到广播后解析得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,尤其是针对系统中的有序广播。

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e("MyBroadcastReceiver", "onReceive: 接收到自定义广播");
        abortBroadcast();//截断广播
    }
}
1.2.4 粘性广播( Sticky Broadcast)

在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated

1.2.5 局部广播( Local Broadcast)

为什么会出现局部广播?平常使用的Broadcast都属于系统全局广播,也就是发出的广播可以被其它应用程序接收到,并且我们也可以接受来自于其他任何应用程序的广播。这样一来会造成一些问题,一是消耗性能,二是容易引起安全性的问题

1.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;

2.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。

解决方案

1.对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;

2.在广播发送和接收时,都增加上相应的permission,用于权限验证;

3.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

Android引入了一套广播本地广播机制,使用该机制发出的广播只能够在本应用内部进行传递,并且广播接收器也只能接收来自本应用发出的广播。

代码片段:

//registerReceiver(mBroadcastReceiver, intentFilter);
//注册应用内广播接收器
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
      
//unregisterReceiver(mBroadcastReceiver);
//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);

Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.putExtra("name", "qqyumidi");
//sendBroadcast(intent);
//发送应用内广播
localBroadcastManager.sendBroadcast(intent);

示例代码:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private BroadcastReceiver mBroadcastReceiver;

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

        // 实例化一个广播接收器
        mBroadcastReceiver = new BroadcastReceiver () {
            @Override
            public void onReceive(Context context, Intent intent) {

                // TODO 接收到广播时的逻辑
                Log.d (TAG, "onReceive: " + intent.getStringExtra ("name"));
            }
        };

        // 实例化IntentFilter
        IntentFilter intentFilter = new IntentFilter ();
        intentFilter.addAction ("com.broadcastreceivertestdemo");
        intentFilter.addAction ("com.broadcastreceivertestdemo1");

        // 注册本地广播
        LocalBroadcastManager.getInstance (this).registerReceiver (mBroadcastReceiver, intentFilter);

        findViewById (R.id.button).setOnClickListener (new View.OnClickListener () {
            @Override
            public void onClick(View v) {
                // 发送一条广播
                Intent intent = new Intent ();
                intent.setAction ("com.broadcastreceivertestdemo1");
                intent.putExtra ("name", "Jack");
                LocalBroadcastManager.getInstance (MainActivity.this).sendBroadcast (intent);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy ();
        // 注销该广播接收器
        LocalBroadcastManager.getInstance (this).unregisterReceiver (mBroadcastReceiver);
    }
}
1.2.6 广播分类总结

(1)有序广播和无序广播的区别

  • 发送广播时,使用的方法不同

    有序广播使用sendOrderedBroadcast()发送广播,而无序广播使用sendBroadcast()方法发送广播。

  • 广播接收者执行的顺序

    a)有序广播的接收者是顺序执行的。

    有序广播按照广播接收者声明的优先级别被依次接收。当在高级别的广播接收者逻辑执行完毕之后,广播才会继续传递。当优先级相同时,先注册的广播接受者优先执行。

    b)无序广播是完全异步执行的。

    当发送无序广播时,所有监听这个广播的广播接收者都会接收到此广播消息,但接收和执行的顺序不确定。

  • 拦截广播

    有序广播的接收者可拦截广播。如果优先级较高的广播接收者将广播终止,那么广播将不再向后传递。

    而无序广播则不能被拦截。

  • 效率

    有序广播的效率比无序广播低。

(2)不同注册方式的广播接收器回调onReceive(context, intent)中的context具体类型

  • 1).对于静态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是ReceiverRestrictedContext;
  • 2).对于全局广播的动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Activity Context;
  • 3).对于通过LocalBroadcastManager动态注册的ContextReceiver,回调onReceive(context, intent)中的context具体指的是Application Context。
  • 注:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的ContextReceiver才有可能接收到(静态注册或其他方式动态注册的ContextReceiver是接收不到的)。

(3)APP退出后是否能收到广播

Android3.1之前静态注册的广播接收器即使app已经退出,只要有相应的广播发出,依然可以接收到。

自Android3.1开始,系统本身则增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。

Android 3.1开始系统在Intent与广播相关的flag增加了参数,分别是

FLAG_INCLUDE_STOPPED_PACKAGES(包含已经停止的包(停止:即包所在的进程已经退出))

FLAG_EXCLUDE_STOPPED_PACKAGES(不包含已经停止的包)

但是对于自定义的广播,可以通过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即使所在App进程已经退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的。

Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.putExtra(“name”, “wlf”);
sendBroadcast(intent);

注意:对于动态注册类型的BroadcastReceiver,由于此注册和取消注册实在其他组件(如Activity)中进行,因此,不受此改变影响。

1.3 广播的原理

1.3.1 广播的真身

经常说”发送广播“和”接收“,表面上看广播作为Android广播机制中的实体,实际上这一实体本身是并不是以所谓的”广播“对象存在的,而是以”意图“(Intent)去表示。定义广播的定义过程,实际就是相应广播”意图“的定义过程,然后通过广播发送者将此”意图“发送出去。被相应的BroadcastReceiver接收后将会回调onReceive()函数。

// 普通广播的定义过程
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);// 对应于BroadcastReceiver中的intentFilter中的action。
intent.putExtra(“name”, “wlf”);
sendBroadcast(intent);
1.3.2 广播的原理

静态广播的注册:通过PackageManagerService在启动的时候扫描已安装的应用去注册的。

动态广播的注册:我们调用Context.registerReceiver最后会调到ActivityManagerService.registerReceiver。

参考文章:

安卓广播的底层实现原理 - 简书 (jianshu.com)

Android 广播实现原理解析_android广播原理_Android_Gaomh的博客-CSDN博客

总结:

  • 从实现原理看上,Android中的广播使用了观察者模式,基于消息的发布/订阅事件模型。
  • 从实现的角度来看,Android中的广播将广播的发送者和接受者极大程度上解耦,使得系统能够方便集成,更易扩展。

1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;

2.广播发送者通过binder机制向AMS发送广播;

3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;

4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。

由此看来,广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。

广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。

2. 内容提供者(ContentProvider)

2.1 内容提供者简单介绍

2.1.1 为什么会有内容提供者?

ContentProvider应用程序间非常通用的共享数据的一种方式,也是Android官方推荐的方式。Android中许多系统应用都使用该方式实现数据共享,比如通讯录、短信等。可能大家觉得直接读取数据库会更简单方便,那么Android搞一个内容提供者在数据和应用之间,只是为了装高大上,故弄玄虚?非也,ContentProvider有如下几个优点

1.封装

对数据进行封装,提供统一的接口,使用者完全不必关心这些数据是在DB,XML、Preferences或者网络请求来的。当项目需求要改变数据来源时,使用我们的地方完全不需要修改。

2.提供一种跨进程数据共享的方式。

1.我们想在自己的应用中访问别的应用,或者说一些ContentProvider暴露给我们的一些数据, 比如手机联系人,短信等!我们想对这些数据进行读取或者修改,这就需要用到ContentProvider了!

2.我们自己的应用,想把自己的一些数据暴露出来,给其他的应用进行读取或操作,我们也可以用 到ContentProvider,另外我们可以选择要暴露的数据,就避免了我们隐私数据的的泄露!

2.1.2 什么是内容提供者?

ContentProvider(内容提供者)是android中的四大组件之一,ContentProvider为不同的软件之间数据共享,提供统一的接口。而且ContentProvider是以类似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URI来表示外界需要访问的“数据库”。

如果我们想让其他的应用使用我们自己程序内的数据,就可以使用ContentProvider定义一个对外开放的接口,从而使得其他的应用可以使用我们自己应用中的文件、数据库内存储的信息。当然,自己开发的应用需要给其他应用共享信息的需求可能比较少见,但是在Android系统中,很多数据如:联系人信息、短信信息、图片库、音频库等,这些信息在开发中还是经常用到的,这些信息已经封装好了,我们可以使用谷歌提供的Uri去直接访问这些数据。

使用场景:

  • 获取通讯录中的联系人,申请好友。
  • 获取其他软件搜索记录,大数据计算,进行产品推送。
  • 预约直播,将预约信息写入手机备忘录

参考链接:【Android】安卓四大组件之内容提供者-CSDN博客

2.2 内容提供者的使用

2.2.1 使用系统提供的ContentProvider

URI(统一资源定位符)介绍

要查询内容提供者,你需要以如下格式的URI的形式来指定查询字符串:

<prefix>://<authority>/<data_type>/<id>

以下是URI中各部分的具体说明:

部分说明
prefix前缀:一直被设置为content://
authority授权:指定内容提供者的名称,例如联系人,浏览器等。第三方的内容提供者可以是全名,如:cn.programmer.statusprovider
data_type数据类型:这个表明这个特殊的内容提供者中的数据的类型。例如:你要通过内容提供者Contacts来获取所有的通讯录,数据路径是people,那么URI将是下面这样:content://contacts/people
id这个指定特定的请求记录。例如:你在内容提供者Contacts中查找联系人的ID号为5,那么URI看起来是这样:content://contacts/people/5

参考链接:

Android 内容提供者(Content Provider) | 菜鸟教程 (runoob.com)

Java魔法堂:URI、URL(含URL Protocol Handler)和URN - _肥仔John - 博客园 (cnblogs.com)

简单的读取收件箱信息:

<uses-permission android:name="android.permission.READ_SMS"/>
// 读取短信内容
private void getMsgs(){
    Uri uri = Uri.parse("content://sms/");
    ContentResolver resolver = getContentResolver();
    //获取的是哪些列的信息
    Cursor cursor = resolver.query(uri, new String[]{"address","date","type","body"}, null, null, null);
    while(cursor.moveToNext())
    {
        String address = cursor.getString(0);
        String date = cursor.getString(1);
        String type = cursor.getString(2);
        String body = cursor.getString(3);
        System.out.println("地址:" + address);
        System.out.println("时间:" + date);
        System.out.println("类型:" + type);
        System.out.println("内容:" + body);
        System.out.println("======================");
    }
    cursor.close();
}
// 插入短信内容
private void insertMsg() {
    ContentResolver resolver = getContentResolver();
    Uri uri = Uri.parse("content://sms/");
    ContentValues conValues = new ContentValues();
    conValues.put("address", "123456789");
    conValues.put("type", 1);
    conValues.put("date", System.currentTimeMillis());
    conValues.put("body", "hello world!");
    resolver.insert(uri, conValues);
    Log.e("HeHe", "短信插入完毕~");
}
2.2.2 自定义ContentProvider
public class MyProvider extends ContentProvider{
    @Override
    public boolean onCreate() {
        return false;
    }
    @Override
    public Cursor query(Uri uri, String[] projection, Stirng selection, String[] selectionArgs, String sortOrder){
        return null;
    }
    @Overrride
    public Uri insert(Uri uri , ContentValues values){
        return null;
    }
    @Override
    public int update(Uri uri, ContentValuse values, String selection, String[] selectionArgs){
        return 0;
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs){
        return 0;
    }
    // 根据传人的内容URI来返回相应的MIME类型。
    @Override
    public String getType(Uri uri){
        return null}
}
query()方法参数对应SQL部分描述
uri fromtable_name指定查询某个应用程序下的某个表
projectionselect column1, column2指定查询的列名
selectionwhere column=value指定where约束条件
selectArgs为where中的占位符提供具体的值
orderByorder by column1, column2指定查询结果的排序方式

onCreate()

初始化内容提供器的时候调用。通常会在这里完成对数据库的创建和升级等操作,返回true
表示内容提供器初始化成功,返回false则表示失败。注意,只有当存在ContentResolver尝试
访问我们程序中的数据时,内容提供器才会被初始化。

查询query()解析

从内容提供器中查询数据。使用uri参数来确定查询哪张表,projection参数用于确定查
询哪些列,selection 和selectionArgs参数用于约束查询哪些行,sortOrder 参数用于对结
果进行排序,查询的结果存放在Cursor对象中返回。

Cursor cursor = getContentResolver().query(
	uri,
	projection,
	selection,
	selectionArgs,
	sortOrder
);
if(cursor != null){
    while(cursor.moveToNext()) {
        String column1 = cursor.getString(cursor.getColumnIndex("column1"));
        int column2 = cursor.getInt(cursor.getColumnIndex("column2"));
    }
    cursor.close();
}

插入insert()

向内容提供器中添加一.条数据。使用uri参数来确定要添加到的表,待添加的数据保存在
values参数中。添加完成后,返回一个用于表示这条新记录的URI。

ContentValues values = new ContentValues();
values.put(“column1”, "text");
values.put("column2", 1);
getContentResolver().insert(uri, values);

更新update()

更新内容提供器中已有的数据。使用uri参数来确定更新哪一张表 中的数据,新数据保存在
values参数中,selection 和selectionArgs参数用于约束更新哪些行,受影响的行数将作
为返回值返回。

ContentValues valuse = new ContentValues();
valuse.put("column1", "");
getContentResolver().update(uri, values, "column1 = ? and column2 = ?", new String[]{"text", 1});

删除delete()

从内容提供器中删除数据。使用uri 参数来确定删除哪一张表中的数据,selection 和
selectionArgs参数用于约束删除哪些行,被删除的行数将作为返回值返回。

getContentResolver().delete(uri , "column2 = ?", new String[]{ "1"});
2.2.3 ContentResolver,ContentObserver和UriMatch介绍

ContentResolver:可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。 当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。

 ContentResolver cr = getContentResolver();

在上面我们提到ContentProvider可以向其他应用程序提供数据,与之对应的ContentResolver则负责获取ContentProvider提供的数据,修改、添加、删除更新数据等,ContentResolver 类也提供了与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中获取数据。

UriMatcher : 主要用于匹配Uri.这里的匹配是发生在ContentProvider中的,假如我们向ContentProvider中插入一条数据,不可能为所欲为,在ContentProvider肯定要做一个判断,只有在符合条件下才会去执行你想要执行的操作,这里的判断就是用UriMatch进行匹配。

// 它的作用就是创建一个UriMatch对象
publicUriMatcher(int code);
// 它的作用是在ContentProvider添加一个用于匹配的Uri,当匹配成功时返回code。Uri可以是精确的字符串,Uri中带有*表示可匹配任意text,#表示只能匹配数字。
public void addURI (String authority,String path, int code);
// 这里的Uri就是传过来的要进行验证.
public int match(Uri uri);

ContentObserver:内容观察者,从其名字我们可以看出它的作用就是观察,观察什么?观察指定的Uri引起的数据库的变化,然后通知主线程,根据需求做我们想要做的处理,既可以监听本应用的,也可以监听其他应用的内容变化。通常有两种用法:

// 用于监听指定uri的内容变化
ContentResolver.registerContentObserver(Uri uri, ContentObserver observer):
// 用于监听指定cursor指向的内容变化
Cursor.registerContentObserver(ContentObserver observer)

参考链接:ContentObserver原理 - 简书 (jianshu.com)

3. Android中Binder机制详解

Android中四大组件底层都是通过Binder机制通信的,Binder相关的学习可以参考以下链接

图解Android中的Binder机制_HankingHu的博客-CSDN博客

一篇文章了解相见恨晚的 Android Binder 进程间通讯机制_jeanboydev的博客-CSDN博客

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

活跃的咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值