Android广播(Broadcast)基本使用

一. 什么是广播
广播(Broadcast)机制用于进程/线程间通信,广播最大的特点就是发送方并不关心接收方是否接到数据,也不关心接收方是如何处理数据的,它只负责“说”而不管你“听不听”,广播可以来之系统,也可以来自于其他应用程序.

  • 按发送方式分类:

标准广播:是一种 完全异步执行 的广播,没有任何先后顺序,所有的广播接收器几乎同一时刻接收到这条广播消息,效率高,无法被截断。
有序广播:是一种 同步执行 的广播,有先后顺序,同一时刻只有一个接收器可以接收这个广播消息,优先级高的广播接收器可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法接收广播消息了。

  • 按注册方式分类

静态广播:不管应用程序是否处于活动状态,都会进行监听
动态广播:在代码中进行注册,注意动态注册的广播一定要取消注册才行,通常是在onDestroy()方法中调用unregisterReceiver()方法来实现

静态广播:每次触发都会建立新的Receiver对象。
动态广播:从开始创建直到其被解除注册会使用同一个Receiver,无论这个广播被触发几次;
  • 按定义方式分类

系统广播: Android系统中内置了多个系统广播,每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,由系统自动发出。
自定义广播: 由应用程序开发者自己定义的广播


二. 为什么使用广播(什么时候使用广播)

  • 比如在收到推送的消息时,当前页面的消息展示数改变,此时可以用到广播;
  • 比如qq等断开网络或网络️断开到连接正常时,监测网络状态变化,此时可以用到广播

其实需要用到广播的地方无处不在,只是有的被你用第三方库代替了,比如rxbus、EventBus等


三. 广播的使用

  1. 静态广播
    AndroidManifest.xmlapplication里面定义receiver并设置要接收的action,MyReceiver为继承BroadcastReceiver的类,重写了onReceiver方法,并在onReceiver方法中对广播进行处理。标签设置过滤器,接收指定action广播。
  • 注册
        //指定广播接收器
            //设置关闭优先级
                //确定需要接收的广播频道(自定义名字)
            
        

特点:不管该应用程序是否处于活动状态,都会进行监听

  • 发送
        Intent intent = new Intent("XXX");//xxx即为action
        intent.putExtra("key", "要传的value");
        sendBroadcast(intent);
  • 接收
public class MyReceiver extends BroadcastReceiver {
    //不能在onReceiver做好事操作
    @Override
    public void onReceive(Context context, Intent intent) {
        String value = intent.getStringExtra("test");
        Toast.makeText(context, value, Toast.LENGTH_LONG).show();
    }
}
  1. 动态广播
  • 注册
   MyReceiver myReceiver= new MyReceiver();
   IntentFilter filter = new IntentFilter();
   filter.addAction("XXX");   //加入Action条件
//filter.setPriority(Integer.MAX_VALUE);  //在有序广播里可以设置他的优先级别
   registerReceiver(myReceiver, filter);  //注册接收器的方法:1.接收器的实体类 2.action频道的信息
  • 发送:
   Intent intent = new Intent();
   intent.setAction("XXX");
   intent.putExtra("key", "传递的value");
   sendBroadcast(intent);
  • 接收
public class MyReceiver2 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String value = intent.getStringExtra("value");
        Toast.makeText(context, value, Toast.LENGTH_LONG).show();
    }
}
  • 注销:
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(myReceiver);//注销接收器
    }
  1. 有序广播
  • 注册
        
        
        
            
                
            
        
         
            
                
            
        
  • 发送
  Intent intent2 = new Intent();
   intent2.setAction("Test");
   intent2.putExtra("key1", "发射了");
   sendOrderedBroadcast(intent2, null);//(null不匹配权限)
  • 接收
public class MyReceiver1 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, intent.getStringExtra("key1"), Toast.LENGTH_LONG).show();
        setResultData("我也要发射了");//给后续的接收器
    }
}
public class MyReceiver2 extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, getResultData(), Toast.LENGTH_SHORT).show();
        //abortBroadcast();//中断后续所有接收器
    }
}
  1. 广播安全性问题

    1. 设置intent-filterexport=false(接受的只是本应用程序的广播,外部app的广播不接受)
    2. 在广播发送和接收时,都增加上相应的permission,用于权限验证;
    3. 发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定,这样 此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中;
    4. 采用LocalBroadcastManager.
  2. 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);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.broadcasttest.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent); // 发送本地广播
            }
        });
        localBroadcastManager = LocalBroadcastManager.getInstance(this); // 获取实例
        localReceiver = new LocalReceiver();
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
        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 local broadcast", Toast.LENGTH_SHORT).show();
        }
    }
}
  1. 粘性广播Sticky Broadcast
    5.0之后Sticky Broadcast被抛弃了,所以现在也没必要讲
  2. 自定义广播权限
  • 静态注册
             
                  
                      
                  
              
  • 动态注册
        MyBroadcastReceiver receiver = new MyBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("action.name");
        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
        //注册receiver时,直接指定发送者应该具有的权限。不然外部应用依旧可以触及到receiver
        registerReceiver(receiver, intentFilter, "com.lazyxu.permission.test", null);

在注册的时候,最关键的一点是用registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)进行注册,而不是平常用的是registerReceiver(BroadcastReceiver, IntentFilter)。相较于后者,前者在注册的时候要求了发送者必须具有的权限。如果发送者没有该权限,那么发送者发送的广播即使经过IntentFilter的过滤,也不会被receiver接收。此时如果再自定义一个权限,并且将权限的protectionLevel设置为signature,那么外部应用便无法使用该权限,也就无法触及到该receiver。

  • 发送广播
        Intent intent = new Intent("action.name");
        sendBroadcast(intent,"com.lazyxu.permission.test");
    另外需要在manifest文件中定义权限并声明
       
        
        

在自定义权限时,通常会指定protectionLevel属性,常用的如下:

normal: 默认的,应用安装前用户可以看到相应的权限,但无需用户主动授权。
dangerous:normal安全级别控制以外的任何危险操作。需要dangerous级别权限时,Android会明确要求用户进行授权。常见的如:网络使用权限,相机使用权限及联系人信息使用权限等。
signature:它要求权限声明应用和权限使用应用使用相同的keystore进行签名。如果使用同一keystore,则该权限由系统授予,否则系统会拒绝。并且权限授予时,不会通知用户。它常用于应用内部。把protectionLevel声明为signature,如果别的应用使用的不是同一个签名文件,就没办法使用该权限,从而保护了自己的接收者。


注意

  1. 如果BroadcastReceiveronReceiver()方法不能在10秒内执行完成,Android会认为该程序无响应。所以不要在onReceiver()里执行耗时的操作,否则会弹出ANR(Application No Response)的对话框。如果需要根据Broadcast来完成一项比较耗时的操作,则可以考虑通过Intent启动一个Service来完成该操作。不赢考虑使用新线程去完成耗时操作,因为BroadcastReceiver本身生命周期很短,可能出现子线程还没有结束,BroadcastReceiver就已经退出。
  1. BroadcastReceiver本身不是Context,其内部也不含有Context,但在onReceive(Context context, Intent intent)中有context参数。这个context随着receiver的注册方式的不同而不同:
    静态注册:context为ReceiverRestrictedContext
    动态注册:context为Activity的context
    LocalBroadcastManager的动态注册:context为Application的context
  2. 静态注册的广播接收器即使app退出,只要有相应的广播发出,仍然可以接收到,但自Android 3.1开始有可能不再成立
  • 对于系统广播,由于是系统内部直接发出,无法更改此intent flag值,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,因此,3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到广播。
  • 对于自定义广播,可通过复写flag为FLAG_INCLUDE_STOPPED_PACKAGES,使得静态注册的BroadcastReceiver,即使App退出,也能能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的。
    实现代码为:
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(intent);

你可能感兴趣的:(Android广播(Broadcast)基本使用)