四大组件之Broadcast简介

来源:互联网 发布:java内部类实例化 编辑:程序博客网 时间:2024/06/05 01:10

广播机制简介

Android 中的广播主要可以分为两种类型,标准广播(无序广播)有序广播广播是一种可以跨进程的通信方式,通过intent传递信息,没有构造函数

  • 标准广播(Normal broadcasts是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。标准广播的工作流程如图所示。


  • 有序广播(Ordered broadcasts则是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以截断正在传递的广播,这样后面的广播接收器就无法收到广播消息了。有序广播的工作流程如图所示



有序广播通过 Context.sendOrderedBroadcast() 方法来发送,所有广播接收器按照优先级依次执行

通过 receiver intent-filter 中的 android:priority 属性给广播接收器设置了优先级,数值越大,优先级越高
在 onReceive() 方法中调用了 abortBroadcast() 方法,就表示将这条广播截断
当广播接收器收到广播后,可以使用 setResult()函数将结果传递给下一个广播接收器,然后通过 getResult() 函数来取得上一个广播接收器返回的结果。

然后修改 MainActivity 中的代码,如下所示:
public class MainActivity extends Activity {……@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button button = (Button) findViewById(R.id.button);button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.broadcasttest. MY_BROADCAST");sendOrderedBroadcast(intent, null);}});……}……}

即将 sendBroadcast(方法改成 sendOrderedBroadcast() 方法sendOrderedBroadcast() 方法接收两个参数,第一个参数仍然是 Intent,第二个参数是一个与权限相关的字符串,这里传入null就行了。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.broadcasttest"    android:versionCode="1"    android:versionName="1.0" >   ……    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        ……        <receiver android:name=".MyBroadcastReceiver">            <intent-filter android:priority="100" >                <action android:name="com.example.broadcasttest.MY_BROADCAST"/>            </intent-filter>        </receiver>    </application></manifest>


通过 android:priority 属性给广播接收器设置了优先级,优先级比较高的广播接收器就可以先收到广播。这里将 MyBroadcastReceiver 的优先级设成了100,以保证它一定会在 AnotherBroadcastReceiver 之前收到广播。

既然已经获得了接收广播的优先权,那么 MyBroadcastReceiver 就可以选择是否允许广播继续传递了。修改 MyBroadcastReceiver 中的代码,如下所示:


public class MyBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "received in MyBroadcastReceive", Toast.LENGTH_SHORT).show();abortBroadcast();}}

如果在 onReceive() 方法中调用了 abortBroadcast() 方法,就表示将这条广播截断,后面的广播接收器将无法再接收到这条广播。

  • 本地广播为了能够简单地解决广播的安全性问题,实现限于应用内的)
  • 应用场景:比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。

Android引入了一套本地广播机制(Support v4 包中新增的),使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,这样所有的安全性问题就都不存在了。


主要就是使用了一个 LocalBroadcastManager 来对广播进行管理,并提供了发送广播和注册广播接收器的方法。

public class MainActivity extends Activity {private IntentFilter intentFilter;private LocalReceiver localReceiver;private LocalBroadcastManager localBroadcastManager;@Overrideprotected 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 OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent("com.example.broadcasttest. LOCAL_BROADCAST");localBroadcastManager.sendBroadcast(intent); // 发送本地广播}});intentFilter = new IntentFilter();intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");localReceiver = new LocalReceiver();localBroadcastManager.registerReceiver(localReceiver, intentFilter); // 注册本地广播监听器}@Overrideprotected void onDestroy() {super.onDestroy();localBroadcastManager.unregisterReceiver(localReceiver);}class LocalReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show();}}}

首先是通过 LocalBroadcastManager g etInstance() 方法得到了它的一个实例,然后在注册广播接收器的时候调用的是 LocalBroadcastManager 的 registerReceiver() 方法,在发送广播的时候调用的是 LocalBroadcastManager 的 sendBroadcast() 方法,仅此而已。

本地广播是无法通过静态注册的方式来接收的。因为静态注册主要就是为了让程序在未启动的情况下也能收到广播,而发送本地广播时,我们的程序肯定是已经启动了,因此也完全不需要使用静态注册的功能。

使用本地广播的几点优势:

1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄漏的问题。

2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐患。

3.发送本地广播比起发送系统全局广播将会更加高效。

  • sticky广播

    sticky 广播通过 Context.sendStickyBroadcast() 函数来发送,用此函数发送的广播会一直滞留,当有匹配此广播的广播接收器被注册后,该广播接收器就会收到此条广播。使用此函数发送广播时,需要获得 BROADCAST_STICKY 权限:

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

sendStickyBroadcast  只保留最后一条广播,并且一直保留下去,这样即使已经有广播接收器处理了该广播,当再有匹配的广播接收器被注册时,此广播仍会被接收,如果你只想处理一遍该广播时,可以通过 removeStickyBroadcast() 函数实现。

  • 广播的两种注册方式:

注册广播的方式一般有两种,在代码中注册(动态注册)和在AndroidManifest.xml中注册(静态注册).

  • 动态注册:

动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,动态注册的广播接收器即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。

  •  静态注册      

静态注册的方式,可以让程序在未启动的情况下就能接收到广播。

动态注册监听网络变化:

创建一个广播接收器其实只需要新建一个类,让它继承自BroadcastReceiver并重写父类的onReceive()方法。这样当有广播到来时,onReceive()方法就会得到执行,具体的逻辑就可以在这个方法中处理。

public class MainActivity extends Activity {private IntentFilter intentFilter;private NetworkChangeReceiver networkChangeReceiver;@Overrideprotected 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);}@Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(networkChangeReceiver);}class NetworkChangeReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();}}}

然后观察onCreate()方法,首先我们创建了一个IntentFilter的实例(调频),并给它添加了一个值为android.net.conn.CONNECTIVITY_CHANGEaction(频道)接下来创建了一个NetworkChangeReceiver的实例,然后调用registerReceiver()方法进行注册,将NetworkChangeReceiver的实例和IntentFilter的实例都传了进去,这样NetworkChangeReceiver就会收到所有值为android.net.conn.CONNECTIVITY_CHANGE的广播,也就实现了监听网络变化的功能。

动态注册的广播接收器一定都要取消注册才行,这里我们是在onDestroy()方法中通过调用unregisterReceiver()方法来实现的。

最好是能准确地告诉用户当前是有网络还是没有网络,因此我们还需要对上面的代码进行进一步的优化。修改MainActivity中的代码,如下所示:

public class MainActivity extends Activity {……class NetworkChangeReceiver extends BroadcastReceiver { @Overridepublic void onReceive(Context context, Intent intent) {ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo networkInfo = connectionManager.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的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它的getActiveNetworkInfo()方法可以得到NetworkInfo的实例,接着调用NetworkInfoisAvailable()方法,就可以判断出当前是否有网络了,最后我们还是通过Toast的方式对用户进行提示。

另外,这里有非常重要的一点需要说明,Android系统为了保证应用程序的安全性做了规定,如果程序需要访问一些系统的关键性信息,必须在配置文件中声明权限才可以,否则程序将会直接崩溃,比如这里查询系统的网络状态就是需要声明权限的。


静态注册实现开机启动:


新建一个BootCompleteReceiver继承自BroadcastReceiver,代码如下所示:

public class BootCompleteReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();} }

可以看到,这里不再使用内部类的方式来定义广播接收器,因为稍后我们需要在AndroidManifest.xml中将这个广播接收器的类名注册进去。在onReceive()方法中,还是简单地使用Toast弹出一段提示信息。

然后修改AndroidManifest.xml文件,代码如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.broadcasttest"    android:versionCode="1"    android:versionName="1.0" >    ……    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >……        <receiver android:name=".BootCompleteReceiver" >            <intent-filter>                <action android:name="android.intent.action.BOOT_COMPLETED" />            </intent-filter>        </receiver>    </application></manifest>

需要注意的是不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作因为在广播接收器中是不允许开启线程的,当onReceive()方法运行了较长时间而没有结束时,程序就会报错。因此广播接收器更多的是扮演一种打开程序其他组件的角色,比如创建一条状态栏通知,或者启动一个服务等,Android 中广播的生命周期不像 Activity 一样复杂,他的生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报错 。









0 0