Android广播机制

邮差的信 提交于 2019-12-02 12:10:19

 

一、简介

       Android中的每个app都可以对自己感兴趣的广播进行注册,这样程序就只会接收到自己关心的广播内容,这些广播可能是来自系统的,也可能来自其他应用程序。发送广播要用到 Intent,接收广播要用到 BroadcastReceiver

 

二、Android中广播的类型

1、标准广播

       标准广播是一种 异步执行 的广播,在广播发出之后,所有BroadcastReceiver几乎会在同一时刻接收到这条广播消息,因此它们之间没有先后顺序可言。
                优点:效率高   缺点:无法截断

                                              

2、有序广播

         有序广播是一种 同步执行 的广播,在广播发出之后,同一时刻只会有一个BroadcastReceiver接收到这条消息,当这个BroadcastReceiver执行完毕之后,广播才会继续传递。
         因此这种方式下,BroadcastReceiver是有先后顺序的,优先级高的BroadcastReceiver就能先收到广播消息,并且前面的BroadcastReceiver还可以截断广播,让后面的接收器无法收到。

                           

三、如何接收广播

         Android中有很多系统级别的广播,比如手机开机之后发出一条广播、电池电量变化会发出一条广播,我们可以在app中通过监听这些广播来得到系统的状态信息,要想接收这些广播,就得使用 广播接收器

         广播接收器可以自由的对自己感兴趣的广播进行注册,当有相应的广播发出时,广播接收器就能收到该广播,并在内部处理。注册广播有动态注册静态注册,前者是在代码中注册,后者是在AndroidManifest.xml中注册

1、动态注册

       下面是一个通过动态注册广播,来监听网络变化的程序:

首先,添加获取网络状态的权限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

 

然后编写代码:

public class CastActivity extends AppCompatActivity {

    private IntentFilter mIntentFilter;
    private NetworkChangeReceiver broadcastReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_cast);

        //创建IntentFilter
        mIntentFilter = new IntentFilter();
        //添加行为
        mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");

        //创建一个BroadcastReceiver
        broadcastReceiver = new NetworkChangeReceiver();
        
        //用IntentFilter注册BroadcastReceiver
        registerReceiver(broadcastReceiver,mIntentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除注册BroadcastReceiver
        unregisterReceiver(broadcastReceiver);
    }

    /*
    * 自定义的广播接收器类
    * */
    class NetworkChangeReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            //利用 ConnectivityManager 和 NetworkInfo 去监听网络状态
            ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

            if (networkInfo != null && networkInfo.isConnected()){
                Toast.makeText(context,"network is available",Toast.LENGTH_SHORT).show();
            }else {
                Toast.makeText(context,"network is unavailable",Toast.LENGTH_SHORT).show();
            }
        }
    }
}

         上面的IntentFilter是用来注册BroadcastReceiver的

         它添加了一个action (android.net.conn.CONNECTIVITY_CHANGE),IntentFilter的作用是可以过滤Intent,利用这个IntetFilter注册了我们的BroadcastReceiver之后,我们的BroadcastReceiver就具备了接收"android.net.conn.CONNECTIVITY_CHAGE"这个系统广播的能力。

附:Intent和IntentFilter?

           Android中提供了 Intent 机制来协助应用间的交互与通讯,Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。Intent不仅可用于应用程序之间,也可用于应用程序内部的Activity/Service之间的交互。因此,Intent在这里起着一个媒体中介的作用,专门提供组件互相调用的相关信息,实现调用者与被调用者之间的解耦。   

          Intent本身是一个被动的数据结构,保存着要执行操作的抽象描述。例如,你有一个活动,需要打开邮件客户端并通过 Android 设备来发送邮件。为了这个目的,你的活动需要发送一个带有合适选择器的 ACTION_SEND 到 Android 意图处理。指定的选择器给定合适的界面来让用户决定如何发送他的邮件数据,这将用到IntentFilter。

Intent作用的表现形式为:

           Android 操作系统使用 IntentFilter 来指定一系列活动、服务和广播接收器处理意图,需要借助于意图所指定的动作、类别、数据模式。

 

2、静态注册

        动态注册的广播接收器能自由的注册和注销,比较灵活,但是它的缺点是,必须在程序启动之后才能接收到广播,因为逻辑是在onCreate()方法中的。要想在程序未启动的时候收到广播,就需要使用 静态注册 的方式。

        下面我们实现一个开机广播。

  首先,定义一个广播接收器类

public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"boot compelete!",Toast.LENGTH_SHORT).show();
    }
}

然后在AndroidManifest.xml文件中的<application>标签内注册该receiver

<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"/>

然后这样的话,下次重启手机之后,就会提醒"boot completed!"。

3、自定义广播

          上面的例子都是定义一个广播接收器去接收系统广播,那么如何去发送自定义广播呢?

发送标准广播

         首先,定义一个广播接收器

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String castMsg = intent.getStringExtra("broadcast_tag");
        Toast.makeText(context,"received broadcast: " + castMsg,Toast.LENGTH_SHORT).show();
    }
}

         然后,在AndroidManifest.xml文件中注册这个receiver

        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.yy.MY_BROADCAST" />
            </intent-filter>
        </receiver>

   最后,在一个Activity中写出发送广播的逻辑

        broadcastBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.yy.MY_BROADCAST");
                intent.putExtra("broadcast_tag","this is a broadcast");
                sendBroadcast(intent);
            }
        });

最后,点击按钮就会发送广播消息"this is a broadcast",并且被MyBroadcastReceiver中的onReceive()方法接收。

[注] 这种方式发送的广播会被其他应用程序接收到,只要其他应用程序的<application>闭包内的<receiver>的action也是com.example.yy.MY_BROADCAST。

 

发送有序广播

        首先,在<receiver>闭包的<intent-filter>中可以设置接收器的优先级:

            <intent-filter android:priority="100">
                <action android:name="com.example.yy.MY_BROADCAST" />
            </intent-filter>

这样优先级更高的BroadcastReceiver会先收到广播消息

 

      其次,在MyBroadcastReceiver中的onReceiver()方法中可以调用abortBroadcast()方法,可以将广播消息截断,让后面的接收器无法接收到广播消息。

 

4、本地广播

         前面的广播都是系统全局广播,这样的广播能被其他程序收到,这样安全性就不好。为了解决安全性问题,就得用到本地广播,主要是用 LocalBroadcastManager 来管理。下面的例子是LocalBroadcastManager配合动态注册的方式实现的本地广播。

 

public class LocalCastActivity extends AppCompatActivity {

    private IntentFilter mIntentFilter;
    private LocalReceiver mLocalReceiver;
    private LocalBroadcastManager mLocalBroadcastManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_local_cast);

        //创建LocalBroadcastManager
        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
        Button button = findViewById(R.id.broadcast_btn2);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent("com.example.yy.LOCAL_BROADCAST");
                //发送一个本地广播
                mLocalBroadcastManager.sendBroadcast(intent);
            }
        });

        //创建IntentFilter
        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction("com.example.yy.LOCAL_BROADCAST");
        //创建LocalReceiver
        mLocalReceiver = new LocalReceiver();
        
        //利用BoradcastManager注册的Receiver是本地Receiver
        mLocalBroadcastManager.registerReceiver(mLocalReceiver,mIntentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //注销Receiver
        mLocalBroadcastManager.unregisterReceiver(mLocalReceiver);
    }

    class LocalReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context,"received local broadcast",Toast.LENGTH_SHORT).show();
        }
    }
}

 

使用本地广播的优势:

  • 可以明确地知道正在发送的广播不会离开本程序,因此不必担心数据泄露
  • 其他的程序无法将广播发送到我们程序内部,因此不必担心安全漏洞
  • 发送本地广播比全局广播更高效

 

四、应用技巧

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!