Android广播详解

Android广播详解

一、广播的分类

1、标准广播

标准广播是完全异步执行(异步同步详情见百度,概念很好李姐)同时不可被截断,也就是说当前发出的一号广播,会被ABC三个接收器同时接收到。如下图
在这里插入图片描述

2、有序广播

顾名思义,一种有序的,同步的广播,它是按照次序来传递,先被优先级高的接收器接收到,响应完成后再传递给接收器B然后C,同时可以进行截断,比如在A处截断,后续B和C就无法接收到广播。如下图
在这里插入图片描述

1.按照接受者的优先顺序来接受广播,优先级别在intent-filter中的priority中声明,-1000~1000之间,值越大优先级越高.
2.可以终止广播的继续传播,接受者可以修改intent的内容
3.同级别接受顺序是随机的,级别低的后收到
4.能截断广播的继续传播,高级别的广播接收器接受广播后能决定是否截断.能处理广播
5.同级别动态注册高于静态注册

3、粘性广播(不建议使用)

粘性广播也属于标准广播,在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理,如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态,粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据。
注意:普通广播和粘性消息不同被截获,而有序广播是可以被截获的。

在Android系统粘性广播一般用来确保重要的状态改变后的信息被持久保存,并且能随时广播给新的广播接收器,比如电源的改变,因为耗电需要一个过程,前一个过程必须提前得到,否则可能遇到下次刚好接收到的广播后系统自动关机了,随之而来的是kill行为,所以对某些未处理完的任务来说,后果很严重。
需要声明权限

<uses-permission android:name="android.permission.BROADCAST_STICKY" />

发送粘性广播

Intent intent = new Intent();
intent.setAction(StickyBroadcastReceiver.Action);
intent.putExtra("test", "sticky broadcast has been receiver");
sendStickyBroadcast(intent);

调用下面方法移除粘性广播

removeStickyBroadcast(intent);

4、系统广播

Android 内置了很多系统级别的广播,我们可以在应用中通过监听这些广播来得到各种系统的状态信息。比如手机开机后会发送一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播等等。如果想要接受这些广播,就需要使用广播接收器。
相应系统广播的Action可以通过文档查询,以开机广播为例
首先需要声明权限

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

高版本的环境中可能需要用户手动开启应用的自启动权限,而且这个广播只有应用安装过并且运行过才能收到。
接收和标准广播差不多,在manifest中静态注册

 <receiver android:name=".boardcastReceiver.TestBroadcastReceiver">
        <intent-filter android:priority="1000">
             <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>

常用系统广播如下:
在这里插入图片描述

5、本地广播
广播的底层用到了Binder,我们知道Binder是Android用于跨进程通讯的。也就是说一个APP发送的广播其他APP也是可以接收的。这就存在了一个安全的问题,比如我知道了某个APP广播接收者的action,那么我就可以恶意发送广播给这个APP,为了解决这个问题就有了本地广播这个概念。也就是说发送本地广播只有本应用的receiver能收到。

Intent intent = new Intent("test.broadcast");
   //发送本地广播
localBroadcastManager.sendBroadcast(intent);

注册接收者

localBroadcastManager = LocalBroadcastManager.getInstance(this);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("test.broadcast");
LocalReceiver localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver, intentFilter);

本地广播无法通过静态注册接收,但是在静态注册的时候receiver标签下有android:exported="true"属性,设置为true就是允许接收其他进程发送的广播,false则只接收本地的。和本地广播的功能很相似。

6、带权限的广播

您可以通过权限将广播限定到拥有特定权限的一组应用。您可以对广播的发送器或接收器施加限制。

带权限的发送

当您调用 sendBroadcast(Intent, String)sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) 时,可以指定权限参数。接收器若要接收此广播,则必须通过其清单中的 标记请求该权限(如果存在危险,则会被授予该权限)。例如,以下代码会发送广播:

sendBroadcast(new Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS);

要接收此广播,接收方应用必须请求如下权限:

<uses-permission android:name="android.permission.SEND_SMS"/>

您可以指定现有的系统权限(如 SEND_SMS),也可以使用 `` 元素定义自定义权限。有关权限和安全性的一般信息,请参阅系统权限

**注意:**自定义权限将在安装应用时注册。定义自定义权限的应用必须在使用自定义权限的应用之前安装。

带权限的接收

如果您在注册广播接收器时指定了权限参数(通过 registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) 或清单中的 ](https://developer.android.google.cn/guide/topics/manifest/receiver-element?hl=zh-cn) 标记指定),则广播方必须通过其清单中的 [ 标记请求该权限(如果存在危险,则会被授予该权限),才能向该接收器发送 Intent。

例如,假设您的接收方应用具有如下所示的清单声明的接收器:

<receiver android:name=".MyBroadcastReceiver"
              android:permission="android.permission.SEND_SMS">
        <intent-filter>
            <action android:name="android.intent.action.AIRPLANE_MODE"/>
        </intent-filter>
</receiver>

或者您的接收方应用具有如下所示的上下文注册的接收器:

IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );

那么,发送方应用必须请求如下权限,才能向这些接收器发送广播:

<uses-permission android:name="android.permission.SEND_SMS"/>

引用参考官方文档https://developer.android.google.cn/guide/components/broadcasts?hl=zh-cn

二、广播的注册

1、静态注册

静态注册广播接收器是通过在AndroidManifest.xml文件中进行声明和配置的。使用静态注册的方式,广播接收器会在应用启动时自动注册,并在整个应用生命周期内有效。

下面是一个静态注册的广播接收器的示例代码:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 处理接收到的广播消息
    }
}

<!--AndroidManifest.xml文件中声明广播接收器 -->
<receiver
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.MY_ACTION" />
    </intent-filter>
</receiver>

以上代码中,我们首先创建了一个自定义的广播接收器MyReceiver,并重写了onReceive()方法来处理接收到的广播消息。然后,在AndroidManifest.xml文件中声明了该广播接收器,并使用元素指定了需要接收的广播动作。

静态注册广播接收器的优点是可以在应用启动时自动注册,并且在整个应用生命周期内有效。但是需要注意,静态注册的广播接收器无法在运行时动态注册和注销,且可能受到系统广播的影响。

不能静态注册的广播
这一点作了解,在出现问题之后至少能反应过来是不是我们静态注册了不能够静态注册的广播。
静态注册的特点是什么:一直监听,从负面角度来看,这样做会一直消耗系统资源去监听,如果被监听的广播是系统运行的基本事件,比如电量变化,屏幕亮灭,就会极大程度的消耗系统资源。这是此类广播不能静态注册的原因之一,现在我们只作了解。

//屏幕亮起
android.intent.action.SCREEN_ON
//屏幕熄灭
android.intent.action.SCREEN_OFF
//电量变化
android.intent.action.BATTERY_CHANGED
//屏幕方向,设备信息发生改变
android.intent.action.CONFIGURATION_CHANGED
//时间改变(每分钟发送一次)
android.intent.action.TIME_TICK

动态注册广播接收器是在代码中动态创建和注册的。使用动态注册的方式,开发者可以更加灵活地控制广播的生命周期,而不会受到AndroidManifest.xml文件的限制。

下面是一个动态注册的广播接收器的示例代码:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 处理接收到的广播消息
    }
}

// 在Activity或Fragment中注册广播接收器
MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.MY_ACTION");
registerReceiver(receiver, filter);

以上代码中,我们首先创建了一个自定义的广播接收器MyReceiver,并重写了onReceive()方法来处理接收到的广播消息。然后,在Activity或Fragment中使用registerReceiver()方法动态注册了该广播接收器,并指定了需要接收的广播动作。当接收到匹配的广播消息时,onReceive()方法将被调用。

当不需要该广播时需要取消注册

unregisterReceiver(receiver);
动态注册广播接收器的优点是可以在运行时根据需要注册和注销,灵活性较高。但是需要注意,动态注册的广播接收器必须在合适的时机进行注册和注销,避免内存泄漏和不必要的资源占用。

总结:

动态注册的优点是可以实现灵活的广播注册和注销,但缺点就是必须要程序启动后才能接收到广播如果想要在程序未启动时,比如刚开机的情况下接受到系统的开机广播,那就需要使用静态注册,但静态注册长期监听,消耗更多资源,因此大部分情况建议优先使用动态注册解决问题。

三、广播的发送

Android 为应用提供三种方式来发送广播:

  • sendOrderedBroadcast(Intent, String) 方法一次向一个接收器发送广播。当接收器逐个顺序执行时,接收器可以向下传递结果,也可以完全中止广播,使其不再传递给其他接收器。接收器的运行顺序可以通过匹配的 intent-filter 的 android:priority 属性来控制;具有相同优先级的接收器将按随机顺序运行。
  • sendBroadcast(Intent) 方法会按随机的顺序向所有接收器发送广播。这称为常规广播。这种方法效率更高,但也意味着接收器无法从其他接收器读取结果,无法传递从广播中收到的数据,也无法中止广播。
  • LocalBroadcastManager.sendBroadcast 方法会将广播发送给与发送器位于同一应用中的接收器。如果您不需要跨应用发送广播,请使用本地广播。这种实现方法的效率更高(无需进行进程间通信),而且您无需担心其他应用在收发您的广播时带来的任何安全问题。

以下代码段展示了如何通过创建 Intent 并调用 sendBroadcast(Intent) 来发送广播。

Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice me senpai!");
sendBroadcast(intent);

优先级设置 注册广播时可以通过setPriority来设置广播优先级

 intentFilter.setPriority(1000);

优先级高的会先收到广播也可以通过abortBroadcast();来截断广播,阻止广播继续向下传输

参考连接:

https://blog.csdn.net/weixin_44666188/article/details/105455796

https://blog.csdn.net/Yrainy_D/article/details/113562395

https://blog.csdn.net/qq_43667625/article/details/109704482

https://blog.51cto.com/u_16213448/7446497

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
@SuppressLint("NewApi") public class MainActivity extends Activity { SmsReceiver myReceiver; Button btn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { test(); } }); } public void test(){ Cursor cursor = null; String defaultSmsApp = Telephony.Sms.getDefaultSmsPackage(this); Intent intent = new Intent(Sms.Intents.ACTION_CHANGE_DEFAULT); intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, this.getPackageName()); startActivity(intent); try { cursor = getContentResolver().query(Uri.parse("content://sms/inbox"), new String[] { "_id", "address", "read" }, "read = ? ", new String[] {"0" }, "date desc"); if (cursor != null) { ContentValues values = new ContentValues(); values.put("read", "1"); for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { Log.v("cky", "" + cursor.getInt(cursor.getColumnIndex("_id")) + " , " + cursor.getString(cursor.getColumnIndex("address"))); int res = getContentResolver().update(Uri.parse("content://sms/inbox"), values, "_id=?", new String[] { "" + cursor.getInt(cursor.getColumnIndex("_id")) }); Log.i("cky","geng xin = "+res); } } intent = new Intent(Sms.Intents.ACTION_CHANGE_DEFAULT); intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, defaultSmsApp); startActivity(intent); } catch (Exception e) { e.printStackTrace(); } finally { if (cursor != null) { cursor.close(); cursor = null; } } } }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值