学而不思则罔,思而不学则殆
【Android】BroadcastReceiver广播机制简单了解
广播分类
广播类型 | 说明 |
---|---|
标准广播(normal broadcasts) | 是一条完全异步执行的广播,广播发出后所有的接受者几乎是在同一时刻受到广播,他们中间没有先后顺序,不能被截断 |
有序广播 (ordered broadcasts) | 一种同步执行的广播,广播发出后,同一时间只能有一个接受者受到广播,高优先级的接受者可以截断广播的发送 |
标准广播图示
有序广播图示
注册广播
新建一个广播接收器
新建一个类,继承BroadcastReceiver ,并重写onReveive方法
public class MyFirstReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("zhangyu1102",MyFirstReceiver.class.getSimpleName() + " action:" + action);
//截断广播
//abortBroadcast();
}
}
动态注册
public class MainActivity extends AppCompatActivity {
private MyFirstReceiver myFirstReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_TIME_TICK);
myFirstReceiver = new MyFirstReceiver();
//动态注册广播
registerReceiver(myFirstReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册
unregisterReceiver(myFirstReceiver);
}
}
运行程序:
$ adb logcat -s zhangyu1102
--------- beginning of main
--------- beginning of system
11-02 00:43:00.034 17443 17443 D zhangyu1102: MyFirstReceiver action:android.intent.action.TIME_TICK
11-02 00:44:00.019 17443 17443 D zhangyu1102: MyFirstReceiver action:android.intent.action.TIME_TICK
11-02 00:45:00.031 17443 17443 D zhangyu1102: MyFirstReceiver action:android.intent.action.TIME_TICK
该广播每隔一分钟就发送一次。
想了解更多系统广播可以到如下的路径下面去查看:
<AndroidSDK>\platforms\<andorid版本>\data\broadcast_actions.txt
Sdk\platforms\android-27\data\broadcast_actions.txt
静态注册
动态注册广播可以自由的控制注册和注销,在灵性方面有很大的优势和选择。但是也存在缺点,就是必须需要程序启动之后才能接收广播。那么有没有什么办法可以让程序未启动的情况下也能接收广播呢?
实际上在过去的Android版本中,很多广播都可静态注册接收,但是这样存在很多恶意程序注册,从而被唤醒,严重的影响了手机的性能和CPU,因此Google每年都在消减或者限制静态注册广播的功能
在Android 8过后,所有的隐式广播都不允许静态注册的方式来接收了。隐式广播是指那些没有指定具体发送给哪个应用程序的广播。大多数系统广播都是隐式广播,但是少数的特殊的广播目前还是可以允许使用静态注册的方式来接收。具体广播可以查看:https://developer.android.google.cn/guide/components/broadcast-exceptions.html
比如:android.intent.action.BOOT_COMPLETED 开机启动广播
用该广播实现开机启动功能。
静态注册实现开机启动
广播接收器:
public class TestBootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("zhangyu1103", TestBootReceiver.class.getSimpleName() + " action:" + action);
}
}
静态注册:
<receiver
android:name=".broadcast.TestBootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
注册权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
系统为了保护用户的安全和隐私,做了严格的规定:如果程序需要进行一些对用户来说比较敏感的操作,必须在AndroidManifest.xml中进行权限声明。
重启手机(模拟器)过后,你通过命令可以发现进程已经被拉起来了:
这里就简单实现了一个开机自启动功能的逻辑。
静态注册的广播和Application接收的顺序
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
Log.d("zhangyu1112", "App onCreate:" + this);
}
}
重启手机:
86183@DESKTOP-MKB5ERF MINGW64 ~/Desktop
$ adb logcat -s zhangyu1112
- waiting for device -
--------- beginning of main
--------- beginning of system
11-12 06:19:30.650 3753 3753 D zhangyu1112: App onCreate:com.example.broadcastdemo.App@a371b5f
11-12 06:19:30.651 3753 3753 D zhangyu1112: MyBootReceiver action:android.intent.action.BOOT_COMPLETED
可以看到,是Application 的onCreate方法先走,然后才是onReceive。
发送广播
发送普通广播
自定义两个广播:
public class MyFirstReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("zhangyu1103", MyFirstReceiver.class.getSimpleName() + " action:" + action);
//截断广播
//abortBroadcast();
}
}
public class MySecondReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("zhangyu1103", MySecondReceiver.class.getSimpleName() + " action:" + action);
//截断广播
//abortBroadcast();
}
}
注册两个广播:
<receiver
android:name=".broadcast.MySecondReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcastdemo.ZY_TEST1103" />
</intent-filter>
</receiver>
<receiver
android:name=".broadcast.MyFirstReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcastdemo.ZY_TEST1103" />
</intent-filter>
</receiver>
发送普通广播:
public void sendNormal(View view) {
Intent intent = new Intent("com.example.broadcastdemo.ZY_TEST1103");
sendBroadcast(intent);
}
但是点击却怎么也收不到广播。前面已经说了,Android 8过后静态注册的广播是无法接受隐式广播的,而默认的情况下,我们发送的广播都是隐式广播。因此这里我们需要通过setPackage()方法,指定这条广播是发送到那个应用的,从而让他变成一条显示广播,否则静态注册的广播将无法接收到这条广播。
public void sendNormal(View view) {
Intent intent = new Intent("com.example.broadcastdemo.ZY_TEST1103");
intent.setPackage("com.example.broadcastdemo");
sendBroadcast(intent);
}
LOG如下,发现两个广播接收器几乎同时接收到广播:
$ adb logcat -s zhangyu1103
--------- beginning of main
--------- beginning of system
--------- beginning of crash
11-02 16:28:06.240 5097 5097 D zhangyu1103: MySecondReceiver action:com.example.broadcastdemo.ZY_TEST1103
11-02 16:28:06.244 5097 5097 D zhangyu1103: MyFirstReceiver action:com.example.broadcastdemo.ZY_TEST1103
测试静态和动态同时注册的情况下
我们先改造一下广播,添加一个变量表示是否是动态注册还是静态注册:
public class MyFirstReceiver extends BroadcastReceiver {
boolean flag = false; //false表示静态注册 true 表示动态注册
public MyFirstReceiver() {
this(false);
}
public MyFirstReceiver(boolean flag) {
this.flag = flag;
Log.d("zhangyu1103", "MyFirstReceiver init flag:" + flag+" "+this);
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("zhangyu1103", MyFirstReceiver.class.getSimpleName() + " action:" + action);
Log.d("zhangyu1103", "flag:" + flag+" "+this);
}
}
页面启动的时候动态注册广播:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter intentFilter = new IntentFilter("com.example.broadcastdemo.ZY_TEST1103");
registerReceiver(new MyFirstReceiver(true), intentFilter);
}
点击发送普通广播两次,结果如下:
11-02 16:32:50.249 5333 5333 D zhangyu1103: MyFirstReceiver init flag:true com.example.broadcastdemo.broadcast.MyFirstReceiver@8466fcc
11-02 16:32:59.197 5333 5333 D zhangyu1103: MyFirstReceiver action:com.example.broadcastdemo.ZY_TEST1103
11-02 16:32:59.197 5333 5333 D zhangyu1103: flag:true com.example.broadcastdemo.broadcast.MyFirstReceiver@8466fcc
11-02 16:32:59.221 5333 5333 D zhangyu1103: MyFirstReceiver init flag:false com.example.broadcastdemo.broadcast.MyFirstReceiver@52ae90b
11-02 16:32:59.221 5333 5333 D zhangyu1103: MyFirstReceiver action:com.example.broadcastdemo.ZY_TEST1103
11-02 16:32:59.222 5333 5333 D zhangyu1103: flag:false com.example.broadcastdemo.broadcast.MyFirstReceiver@52ae90b
11-02 16:33:23.771 5333 5333 D zhangyu1103: MyFirstReceiver action:com.example.broadcastdemo.ZY_TEST1103
11-02 16:33:23.771 5333 5333 D zhangyu1103: flag:true com.example.broadcastdemo.broadcast.MyFirstReceiver@8466fcc
11-02 16:33:23.773 5333 5333 D zhangyu1103: MyFirstReceiver init flag:false com.example.broadcastdemo.broadcast.MyFirstReceiver@fee2800
11-02 16:33:23.773 5333 5333 D zhangyu1103: MyFirstReceiver action:com.example.broadcastdemo.ZY_TEST1103
11-02 16:33:23.773 5333 5333 D zhangyu1103: flag:false com.example.broadcastdemo.broadcast.MyFirstReceiver@fee2800
根据log可以看出:动态注册的广播和静态注册的广播都接收到了。但是动态注册的广播实例对象只有一个{8466fcc},静态注册的广播每次收到广播都会新建一个实例对象,比如{52ae90b}{fee2800}。
发送有序广播
第二个参数是跟权限有关的,这里传入null就可以。
public void sendOrder(View view) {
Intent intent = new Intent("com.example.broadcastdemo.ZY_TEST1103");
intent.setPackage("com.example.broadcastdemo");
sendOrderedBroadcast(intent, null);
}
改变MyFirstReceiver的优先级。
<receiver
android:name=".broadcast.MyFirstReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.example.broadcastdemo.ZY_TEST1103" />
</intent-filter>
</receiver>
在第一个接受的广播中休眠一秒:
public class MyFirstReceiver extends BroadcastReceiver {
boolean flag = false; //false表示静态注册 true 表示动态注册
public MyFirstReceiver() {
this(false);
}
public MyFirstReceiver(boolean flag) {
this.flag = flag;
Log.d("zhangyu1103", "MyFirstReceiver init flag:" + flag+" "+this);
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("zhangyu1103", MyFirstReceiver.class.getSimpleName() + " action:" + action);
Log.d("zhangyu1103", "flag:" + flag+" "+this);
// FIXME: 2020/11/3 做一点耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试结果:
11-02 16:45:47.736 5610 5610 D zhangyu1103: MyFirstReceiver init flag:false com.example.broadcastdemo.broadcast.MyFirstReceiver@2b63fc
11-02 16:45:47.736 5610 5610 D zhangyu1103: MyFirstReceiver action:com.example.broadcastdemo.ZY_TEST1103
11-02 16:45:47.737 5610 5610 D zhangyu1103: flag:false com.example.broadcastdemo.broadcast.MyFirstReceiver@2b63fc
11-02 16:45:48.764 5610 5610 D zhangyu1103: MySecondReceiver init flag:false com.example.broadcastdemo.broadcast.MySecondReceiver@bd88a85
11-02 16:45:48.765 5610 5610 D zhangyu1103: MySecondReceiver action:com.example.broadcastdemo.ZY_TEST1103
11-02 16:45:48.765 5610 5610 D zhangyu1103: flag:false com.example.broadcastdemo.broadcast.MySecondReceiver@bd88a85
查看时间戳,MyFirstReceiver 先收到广播,MySecondReceiver 是在1秒后才收到广播。
截断广播发送
如果想截断广播的话,可以如下操作:
public class MyFirstReceiver extends BroadcastReceiver {
boolean flag = false; //false表示静态注册 true 表示动态注册
public MyFirstReceiver() {
this(false);
}
public MyFirstReceiver(boolean flag) {
this.flag = flag;
Log.d("zhangyu1103", "MyFirstReceiver init flag:" + flag+" "+this);
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("zhangyu1103", MyFirstReceiver.class.getSimpleName() + " action:" + action);
Log.d("zhangyu1103", "flag:" + flag+" "+this);
// FIXME: 2020/11/3 做一点耗时操作
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
abortBroadcast(); //截断广播
}
}