先自定义一个广播类
员工经过长期磨合与沉淀,具备了协作精神,得以通过团队的力量开发出优质的产品。成都创新互联坚持“专注、创新、易用”的产品理念,因为“专注所以专业、创新互联网站所以易用所以简单”。公司专注于为企业提供成都网站设计、网站制作、微信公众号开发、电商网站开发,微信小程序开发,软件按需制作等一站式互联网企业服务。
然后在Application中进行动态注册
因为是动态注册所以无需在AndroidManifest.xml中进行注册
对于Activity的启动流程,我们已经有了几个版本的分析了。这里我们分析一个更容易一些的,四大组件中最简单的Broadcast Receiver。
关于Broadcast,有几点需要了解。首先是广播的类型,然后是广播的发送方法,最后是广播是如何被接收的。这三者相辅相承的,比如普通广播和有序广播只有在详细了解了广播的接收过程了之后,才能真正明白它的含义。
普通的广播是不在意顺序的,最简单的理解是同时可以收到这个广播。如果应用是动态注册这个广播的,且广播发送时这个进程还活着,那么当然可以并发的把广播尽快地传送出去是最好的。
但是,如果是通过AndroidManifest.xml静态注册的情况,也就是说这个广播首先要把一个进程启动起来,这时并发启动很多进程就是个问题了。Android目前的做法是,对这种静态的广播接收者,自动按有序广播的方式来串行处理。但是这对应用是透明的,应用不能假设系统已经把静态的无序广播当成有序广播来处理。
这个时候讲粘性广播有福了,因为从Android 5.0(API 21)开始,因为安全性的问题,官方已经正式废弃了粘性广播。
Context类提供两个方法可以用于发送普通广播:
差别是第二个设置权限。
发给特定的用户:
有序广播因为要处理消息的处理结果,所以要复杂一些。
如果只是想让广播可以按优先级来收取,并不在意处理的结果,可以用下面的版本:
同样,在多用户环境下,也可以选择给哪个用户发广播:
不管是普通的还是有序的广播都对应有粘性的版本:
以上的API都是定义于Context类中:
首先我们先看看发送端是如何发送的。
我们首先先放一个大图,让大家先有一个直观的印象,不管普通广播、有序广播、粘性广播如何组合,最终都汇集到一个大方法中。
我们先看应用发送普通广播的一个简单的例子:
非常简单,调用ContentWrapper的sendBroadcast方法就可以了。
然后我们顺藤摸瓜就好了。
Activity中的sendBroadcast,实际上调用的是:
我们来看frameworks/base/core/java/android/content/ContextWrapper.java中对sendBroadcast的定义:
ContextWrapper只是一个包装,真正的实现在ContextImpl中
我们来看/frameworks/base/core/java/android/app/ContextImpl.java中真正实现sendBroadcast的功能:
它会通过IPC去调用AMS的broadcastIntent。由于我们这个普通的广播的方法参数最少,所以好多都是传null。
加锁,定参数,然后调用真正的逻辑的实现。
我们先把broadcastIntentLocked的真正逻辑放一下,先看看有序广播是如何发送的。
ContextWrapper.sendOrderedBroadcast
Context是abstract方法,调用的是ContextWrapper的实现:
跟普通广播一样,还是会调用到ContextImpl.sendOrderedBroadcast
有序广播调用broadcastIntent的区别在于serialized参数,普通广播为false,有序广播为true.
原型为:
前面讲过带有回调的版本,我们看看它是如何实现的:
当然还是调用ContextImpl.sendOrderedBroadcast
这次变成只是一个封装了,它会调用一个更多参数的版本:
这次是一个全参数调用broadcastIntent的版本了,除了sticky就齐了
我们也不绕圈子了,直接看ContextImpl.sendStickyBroadcast.
以下广播简称Broadcast
是Android四大组件之一,在四大组件的另外两个组件 和 拥有发送和接收广播的能力。Android 是在 进程间通信机制的基础上实现的,内部基于消息发布和订阅的事件驱动模型,广播发送者负责发送消息,广播接收者需要先订阅消息,然后才能收到消息。 进程间通信与 的区别在于:
有三种类型
存在一个注册中心,也可以说是一个调度中心,即 。广播接收者将自己注册到 中,并指定要接收的广播类型;广播发送者发送广播时,发送的广播首先会发送到 , 根据广播的类型找到对应的 ,找到后边将广播发送给其处理。
这里以普通广播为例子, 接收者有两种注册方式,一种是 ,一种是 :
(广播的发送分为 两种,这里针对有序的广播) 中的android:priority=""和 中的IntentFilter.setPriority(int)可以用来设置广播接收者的优先级,默认都是0 , 范围是[-1000, 1000],值越大优先级越高,优先级越高越早收到。
在相同优先级接收同个类型广播时, 的广播接收器比 的广播接收者更快的接收到对应的广播,这个之后会进行分析。
注:以下源码基于rk3399_industry Android7.1.2
的流程可分为 , 和 三个部分,这里依次分析下
在Android系统的 机制中,前面提到, 作为一个注册和调度中心负责注册和转发 。所以 的注册过程就是把它注册到 的过程。
这里我们分析 广播的过程, 和 有一个共同的父类 ,所以它们对应的注册过程其实是调用 ,接下来我们按照流程逐步分析调用流程的源码。
frameworks/base/core/java/android/content/ContextWrapper.java
在之前的 Android应用程序启动入口ActivityThread.main流程分析 分析过,在我们启动 Activity 时会创建一个 对象,然后通过 传给我们启动的 ,其内部就会将该对象赋值给 ; 的 方法也是类似的赋值流程,这里放个简易的源码应该更好理解
可以看到最后都会将生成的 对象赋值给对应的
对象。接下来继续分析 , 即 函数。
/frameworks/base/core/java/android/app/ContextImpl.java
这里我们首先看下如何将广播接收者 封装成一个 接口的 本地对象
/frameworks/base/core/java/android/app/LoadedApk.java
每一个注册过广播接收者的 或 组件在font color='Crimson' LoadedApk /font类中都有个对应的 对象,该对象负责将 与 组件关联起来。这些对象,以关联的 作为关键字保存在一个 中。之后对应的 又以 的 作为关键字保存在 的成员变量 对象中。最后通过 对应的 方法获得其 接口的 本地对象。之后再回到 注册方法内,将 对象发给 进行注册。
/frameworks/base/core/java/android/app/ActivityManagerNative.java
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
在的 或 注册一个 时,并不是将其注册到font color='OrangeRed'AMS/font中,而是将与它关联的font color='OrangeRed'InnerReceiver/font对象注册到font color='OrangeRed'AMS/font中,当font color='OrangeRed'AMS/font接收到广播时,会根据 在内部找到对应的font color='OrangeRed'InnerReceiver/font对象,然后在通过这个对象将这个广播发送给对应的 处理。
注册过程这边画了一个简单的流程图:
font color='OrangeRed'Broadcast/font的发送过程可简单描述为以下几个过程:
frameworks/base/core/java/android/content/ContextWrapper.java
/frameworks/base/core/java/android/app/ContextImpl.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
有两种注册广播方式:
1.常驻型广播
常驻型广播,当应用程序关闭了,如果有广播信息来,写的广播接收器同样的能接收到,它的注册方式就是在应用程序的AndroidManifast.xml 中进行注册,这种注册方式通常又被称作静态注册。这种方式可以理解为通过清单文件注册的广播是交给操作系统去处理的。示例代码如下:
AndroidManifest.xml中配置广播
?xml version="1.0" encoding="utf-8"?
manifest xmlns:android=""
package="spl.broadCastReceiver"
android:versionCode="1"
android:versionName="1.0"
application android:icon="@drawable/icon" android:label="@string/app_name"
activity android:name=".BroadCastReceiverActivity"
android:label="@string/app_name"
intent-filter
action android:name="android.intent.action.MAIN" /
category android:name="android.intent.category.LAUNCHER" /
/intent-filter
/activity
!--广播注册、name里面填写广播类的路径--
receiver android:name=".SmsBroadCastReceiver"
intent-filter android:priority="20"
action android:name="android.provider.Telephony.SMS_RECEIVED"/
/intent-filter
/receiver
/application
uses-sdk android:minSdkVersion="7" /
!-- 权限申请 --
uses-permission android:name="android.permission.RECEIVE_SMS"/uses-permission
/manifest
2.非常驻型广播
非常驻型广播,当应用程序结束了,广播自然就没有了,比如在 Activity 中的 onCreate 或者 onResume 中注册广播接收者,在 onDestory 中注销广播接收者。这样广播接收者就一个非常驻型的了,这种注册方式也叫动态注册。这种方式可以理解为通过代码注册的广播是和注册者关联在一起的。比如写一个监听 SDcard 状态的广播接收者:
package cn.sunzn.mosecurity.activity;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Environment;
public class SDcard extends Activity {
SdcardStateChanageReceiver sdcardStateReceiver;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sdcardStateReceiver = new SdcardStateChanageReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
filter.addAction(Intent.ACTION_MEDIA_EJECT);
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addDataScheme("file");
registerReceiver(sdcardStateReceiver, filter);
}
protected void onDestroy() {
unregisterReceiver(sdcardStateReceiver);
}
class SdcardStateChanageReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
checkSDCard();
}
public void checkSDCard() {
String state = Environment.getExternalStorageState();
System.out.println(state);
if (state.equals(Environment.MEDIA_REMOVED) || state.equals(Environment.MEDIA_UNMOUNTED)) {
System.out.println("SDCard 已卸载!");
}
}
}
}
首先写一个类要继承BroadcastReceiver\x0d\x0a第一种:在清单文件中声明,添加\x0d\x0a\x0d\x0a \x0d\x0a \x0d\x0a\x0d\x0a第二种使用代码进行注册如:\x0d\x0aIntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");\x0d\x0aIncomingSMSReceiver receiver = new IncomgSMSReceiver();\x0d\x0aregisterReceiver(receiver.filter);