PS:跳过了第四章(探究碎片),是平板的相关内容
1.广播类型
类型 | 区别 | 备注 |
---|---|---|
标准广播 | 完全异步执行,所有广播接收器同时收到广播消息 | 效率较高但无法被截断 |
有序广播 | 同步执行,按广播接收器的优先级传递消息 | 可截断 |
2.接受系统广播
动态注册——在代码中
(必须在程序启动后才能接收广播)
举例:监听网络状态变化
public class EightActivity extends AppCompatActivity {
private IntentFilter intentFilter ;
private NetworkChangeReceiver networkChangeReceiver ;
int times = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eight);
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
//网络发生变化时会发出一条值为android.net.conn.CONNECTIVITY_CHANGE的广播
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver,intentFilter);
//调用registerReceiver进行注册
}
@Override
protected void onDestroy(){
//动态注册的广播接收器一定要取消注册,所以在活动销毁时调用unregisterReceiver
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
class NetworkChangeReceiver extends BroadcastReceiver{
//每当网络发生变化时,onReceive会得到执行
@Override
public void onReceive(Context context, Intent intent){
TextView textView = (TextView) findViewById(R.id.Eight_TextView);
Toast.makeText(context,"Network changes",Toast.LENGTH_SHORT).show();
textView.setText("网络改变了"+ ++times +"次");
}
}
}
效果:
说明一下,最开始显示是1次因为打开监听是从无网络到有网络,所以是一次,然后打开WiFi显示改变了3次,我用真机测试了一下发现是因为打开WiFi是先断开数据连接(1次),再连接WiFi(1次),所以是1+1+1=3次
改写一下onReceive使得可以提示网络是断开还是连接
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.isConnected()){
Toast.makeText(context,"网络已连接",Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(context,"网络已断开",Toast.LENGTH_SHORT).show();
}
TextView textView = (TextView) findViewById(R.id.Eight_TextView);
textView.setText("网络改变了"+ ++times +"次");
}
}
效果:
(由于用的虚拟机是Android9.0,真的卡)
此外《第一行代码》中提示说,访问系统的网络状态需要声明权限,在AndroidManifest.xml中加入:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
静态注册实现开机启动——在AndroidManifest.xml中
(Android 9.0我实现不了,于是下了7.0来测试,搜索了一波发现说是因为8.0版本开始已经不支持大部分的静态注册)
①右键 com.example.activitytest -->New -->Other -->Broadcast Receiver
命名为BootCompleteReceiver
加一条信息提示用于测试:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context, "开机启动", Toast.LENGTH_SHORT).show();
//throw new UnsupportedOperationException("Not yet implemented");
}
}
②AndroidManifest.xml
<receiver
android:name=".BootCompleteReceiver"
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"/>
④效果
3.发送自定义广播
发送标准广播
经过测试发现收不到广播,于是搜索了一波发现
8.0版本开始已经不支持大部分的静态注册的广播(隐式发送的)了
解决方法:
- 换成7.0以下版本的虚拟机
- 用显示方法发送广播
①先定义广播接收器
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
//throw new UnsupportedOperationException("Not yet implemented");
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.ActivityTest.MY_BROADCAST" />
</intent-filter>
</receiver>
③添加按钮负责发广播消息
如果是显示发送的广播,可以不设置action,即使设置了action也会被忽略
public class EightActivity extends AppCompatActivity {
private IntentFilter intentFilter ;
private MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eight);
Button button = (Button) findViewById(R.id.Send_Broadcast);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.ActivityTest.MY_BROADCAST");
//Intent intent = new Intent();
//intent.setComponent(new ComponentName(EightActivity.this,MyBroadcastReceiver.class));
sendBroadcast(intent);
}
});
}
@Override
protected void onDestroy(){
super.onDestroy();
unregisterReceiver(myBroadcastReceiver);
unregisterReceiver(anotherBroadcastReceiver);
}
④效果:
发送有序广播
即按Receiver的优先级接收广播,优先级高的先接收,可以中间截断
在发送标准广播的基础上修改:
①Activity中:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eight);
Button button = (Button) findViewById(R.id.Send_Broadcast);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.ActivityTest.MY_BROADCAST");
sendOrderedBroadcast(intent,null);
//这里的null可以替换成与权限相关的字符串
}
});
}
②设置Receiver的优先级:这里设成MyBroadcastReceiver比AnotherBroadcastReceiver优先级高
<receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.ActivityTest.MY_BROADCAST" />
</intent-filter>
</receiver>
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
//proiority设置优先级,优先级高的Receiver先收到广播
<action android:name="com.example.ActivityTest.MY_BROADCAST" />
</intent-filter>
</receiver>
③修改其中一个Receiver,使其在接收到广播后截断广播
abortBroadcast()即为截断广播的方法
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"received in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
abortBroadcast();
}
}
④不执行截断跟执行截断的效果:
4.发送本地广播
举例
用LocalBroadcastManager解决
修改活动中的代码即可:
public class EightActivity extends AppCompatActivity {
private IntentFilter intentFilter ;
private LocalReceiver localReceiver;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_eight);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
Button button = (Button) findViewById(R.id.Send_Broadcast);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent("com.example.ActivityTest.LOCAL_BROADCAST");
localBroadcastManager.sendBroadcast(intent);
}
});
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.example.ActivityTest.LOCAL_BROADCAST");
localReceiver = new LocalReceiver();
localBroadcastManager.registerReceiver(localReceiver,intentFilter);
}
@Override
protected void onDestroy(){
super.onDestroy();
unregisterReceiver(localReceiver)
}
class LocalReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context,Intent intent){
Toast.makeText(context,"收到本地广播",Toast.LENGTH_SHORT).show();
}
}
效果:
(其实代码与动态注册全局广播很相似,只是本地广播用了LocalBroadcastManager)
总结
书中给出了三点本地广播的优势:
可以明确的知道正在发生的广播不会离开程序,无需担心机密数据泄露
其他程序无法发送广播到程序内部,无需担心安全漏洞的隐患
发送本地广播比全局广播更高效