1.1广播机制简介
为了便于进行系统级别的广播通知,Android引入了一套广播消息机制,Android的每个应用程序都可以对自己感兴趣的广播进行注册,这样该程序只会接收到自己所关心的广播内容,这些广播可能是来自系统的,也可能是来自其他应用的,Android提供了一套完整的API,允许应用程序自由地接受和发送广播。接受广播的方法引入一个新概念——广播接收器(Broadcast Receiver)。
广播主要分为两种类型,标准广播,有序广播。
- 标准广播:是一种完全异步执行的广播,在广播发出后,所有的广播几乎在同一时刻接受这条广播消息,没有顺序,效率比较高,同时意味着无法截断。
- 有序广播:一种同步执行的广播,在广播发出后,同一时刻只会有一个广播接收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递,此时的广播接收器是有先后顺序的,优先级高的广播就可以先接收到消息,并且前面的广播接收器还可以截断正在传递的广播,后面的就无法接收消息了。
1.2 接受系统广播
1.2.1 动态注册监听网络变化
注册广播:1.代码注册,动态注册。2.在AndroidManifest.xml中注册,静态注册。
该如何创建一个广播接收器,只需要新建一个类,让它继承自BroadcastReceiver,并重写父类的onReceive()方法。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑可以在这个方法中处理
先写一个通过动态注册的方式监听网络变化的程序,新建一个项目
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver,intentFilter);
}
@Override
protected void onDestroy(){
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
class NetworkChangeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent){
Toast.makeText(context,"network changes",Toast.LENGTH_SHORT).show();
}
}
}
在MainActivity中定义了一个内部类NetworkChangeReceiver,继承BroadcastReceiver,并重写了父类的onReceive()方法,当网络状态发生变化时,onReceive()方法就会得到执行,Toast提示了一段文本。OnCreate()方法,首先创建了IntentFilter的实例,并给他添加了android.net.conn.CONNECTIVITY_CHANGE的action,因为当网络状态发生变化时,系统发出的正是一条值为android.net.conn.CONNECTIVITY_CHANGE的广播。所以我们的广播接收器想要监听什么广播,就在这里添加相应的action。接下来创建一个NetworkChangeReceiver实例,然后调用registerReceiver()方法进行注册,将NetworkChangeReceiver的实例和IntentFilter的实例都传进去。这样NetworkChangeReceiver就会收到值为android.net.conn.CONNECTIVITY_CHANGE的广播,也就实现了监听网络的功能。
最后,动态注册的广播接收器一定要取消注册,这里在onDestroy()方法中通过调用**unregisterReceiver()**方法来实现。
对以上代码进行优化,提醒当前是有网络还是没网络
class NetworkChangeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context ,Intent intent){
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if(networkInfo!=null&&networkInfo.isAvailable()){
Toast.makeText(context,"network is available",Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(context,"network is unavailable",Toast.LENGTH_SHORT).show();
}
}
}
在onReceive()方法中,通过getSystemService()方法得到了ConnectivityManager的实例,这是一个系统服务类,专门用于管理网络连接,然后调用他的getActivityNetworkInfo()方法可以得到NetworkInfo实例,接着调用NetworkInfo的isAvailable()方法就可以判断当前是否有网络,最后通过Toast提示。
要在配置文件里声明权限,在AndroidManifest.xml中加入
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
1.2.2 静态注册实现开机启动
动态注册的广播可以自由地控制注册与注销,灵活,但有一个缺点,必须要在程序启动后才能接收广播,因为注册的逻辑是写在onCreate()方法里,想要让程序在未启动的情况下就能接受广播,这就需要静态注册。
让程序接受一条开机广播,当收到这条广播就可以在onReceive()方法里执行相应的逻辑,从而实现开机启动功能。可以使用Android Studio提供的快捷方式来创建一个广播接收器,New,Other,Broadcast Receiver,将广播接收器命名为BootCompleteReceiver,Exported属性表示是否允许这个广播接收器接受本程序以外的广播,Enabled属性表示是否启用这个广播接收器。修改BootCompleteReceiver中的代码
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"Boot Complete",Toast.LENGTH_SHORT).show();
}
}
静态的广播器要在AndroidManifest.xml中注册,修改其中的代码
......
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
......
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
......
在< aoolication >标签内出现了一个新标签< receiver > ,所有静态的广播接收器都是可以在这里进行注册,用法和< activity >标签非常相似,通过android:name来指定具体注册哪一个广播接收器。由于Android系统启动后会发出一条值为android.intent.action.BOOT_COMPLETED的广播,所以在< intent-filter >标签里添加相应的action。另外监听系统开机广播需要声明权限,在< uses-permission >标签里加入了一条android.permission.RECEIVE_BOOT_COMPLETED权限。
Android 8.0或更高版本,无法使用清单为大多数隐式广播声明接收器,开机时无法接收到广播,解决方法:
Intent it = new Intent("com.example.broadcast.BootCompleteReceiver");
it.setComponent(new ComponentName("com.example.broadcast", "com.example.broadcast.BootCompleteReceiver"));
sendBroadcast(it);
第一个参数是包名,第二个参数是广播器名,然后开机,效果如图
在真正项目中的时候,可以在onReceive()中写自己的逻辑,但是不要在其中添加过多的逻辑或进行任何耗时的操作,因为在广播接收器里是不允许开启线程的。所以广播接收器更多的扮演一种打开程序其他组件的角色,比如创建一条状态通知栏,或者启动一个服务。
1.3 发送自定义广播
1.3.1发送标准广播
发送广播之前先定义一个广播接收器来准备接受此广播,新建MyBroadcastReceiver,
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"received in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
}
}
修改AndroidManifest.xml中的代码
...
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcast.MY_BROADCAST"/>
</intent-filter>
</receiver>
...
让MyBroadcastReceiver接受一条值为com.example.broadcast.MY_BROADCAST的广播,因此待会儿发送广播的时候,就需要发出这样一条广播。
修改activity_main.xml中的代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/button"
android:text="Send Broadcast"/>
</LinearLayout>
然后修改MainActivity中的onCreate()的代码
...
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcast.MY_BROADCAST");
intent.setComponent(new ComponentName("com.example.broadcast", "com.example.broadcast.MyBroadcastReceiver"));
sendBroadcast(intent);
}
});
...
在按钮的点击事件里加入了发送自定义的广播逻辑,首先构建出了一个Intent对象,并把要发送的广播的值传入,然后调用了Context的sendBroadcast()方法将广播发送出去,这样所有监听com.example.broadcast.MY_BROADCAST这条广播的广播接收器就会收到这条消息,此时发出去的广播是一条标准广播。
点击按钮,效果如下
1.3.2发送有序广播
广播是一种可以跨进程的通信方式,因此在我们应用内发出的广播,其他应用程序也可以收到。再创建一个新项目BroadcastTest2,在这个项目下定义一个广播接收器,用于接收上一节中的自定义广播,新建AnotherBroadcastReceiver
public class AnotherBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"received in AnotherBroadcast",Toast.LENGTH_SHORT).show();
}
}
修改AndroidManifest.xml中的代码
...
<receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest1.MY_BROADCAST"/>
</intent-filter>
</receiver>
...
AnotherBroadcastReceiver同样接收的是com.example.broadcasttest1.MY_BROADCAST这条广播,先运行BroadcastTest2安装到模拟器上,然后修改Broadcast的MainActivity中的代码
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcast.MY_BROADCAST");
intent.setComponent(new ComponentName("com.example.broadcast", "com.example.broadcast.MyBroadcastReceiver"));
sendBroadcast(intent);
Intent intent2 = new Intent("com.example.broadcast.MY_BROADCAST");
intent2.setComponent(new ComponentName("com.example.broadcasttest2","com.example.broadcasttest2.AnotherBroadcastReceiver"));
sendBroadcast(intent2);
}
});
运行,点击按钮就会分别弹出两次提示信息。这样证明了,我们应用程序发出的广播可以被其他应用程序接收到。
1.4 使用本地广播
本地广播机制,发出的广播只能在程序的内部进行传递,并且广播接收器也只能接受来自本应用程序发出的广播。
主要使用LocalBroadcastManager来对广播进行管理。
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this); //得到实例
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent("com.example.broadcasttest1.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent); //发送本地广播
}
});
intentFilter = new IntentFilter();
intentFilter.addAction("com.example.broadcasttest1.LOCAL_BROADCAST"); //注册本地广播监听器
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver,intentFilter);
}
@Override
protected void onDestroy(){
super.onDestroy();
localBroadcastManager.unregisterReceiver(localReceiver);
}
class LocalReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context,Intent intent){
Toast.makeText(context,"received in local broadcast",Toast.LENGTH_SHORT).show();
}
}
本地广播无法通过静态注册的方式来接收。
优势:1.可以明确知道正在发送的广播不会离开我们的程序,因此不必担心机密数据泄露。2.其他程序无法将广播发送黄金我们程序的内部,因此不需要担心会有安全泄露。3.发送本地广播会比发送系统全局广播更高效。