Broadcast Receiver__广播机制详解

Android 中的广播主要可以分为两种类型,标准广播和有序广播。
标准广播(Normal broadcasts)是一种完全异步执行的广播,在广播发出之后,所有的
广播接收器几乎都会在同一时刻接收到这条广播消息,因此它们之间没有任何先后顺序可
言。这种广播的效率会比较高,但同时也意味着它是无法被截断的。

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

广播接收器可以自由地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广
播接收器就能够收到该广播,并在内部处理相应的逻辑。注册广播的方式一般有两种,在代
码中注册和在 AndroidManifest.xml 中注册,其中前者也被称为动态注册,后者也被称为静态
注册。

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

动态注册的方式:

public class MainActivity extends Activity {
    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();//new出一个广播接收者的对象,需要继承BroadcastReceiver
        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();
        }
    }
}

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

动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是
它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在
onCreate()方法中的。那么有没有什么办法可以让程序在未启动的情况下就能接收到广播
呢?这就需要使用静态注册的方式了。

在xml文件中进行以下代码的注册

<receiver android:name=".BootCompleteReceiver" >  
  <intent-filter>
      <action android:name="android.intent.action.BOOT_COMPLETED" />

  </intent-filter>
</receiver>

name代表接收广播的类,该类同样需要继承BroadcastReceiver重写onReceiver方法

intent-filter代表广播的过滤器(也就是接收该过滤器指定的广播),想想也知道,总不能接收所有广播吧,这也就是过滤器的作用

上面都是监听系统广播 接下来谈谈自定义广播

发送自定义广播

在发送广播之前,我们还是需要先定义一个广播接收器来准备接收此广播才行,不然发
出去也是白发。因此新建一个 MyBroadcastReceiver继承自 BroadcastReceiver,代码如下所示:
public class MyBroadcastReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    Toast.makeText(context, "received in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();
  }
}

这里当 MyBroadcastReceiver收到自定义的广播时,就会弹出 receivedinMyBroadcastReceiver
的提示。然后在 AndroidManifest.xml 中对这个广播接收器进行注册:

<receiver android:name=".MyBroadcastReceiver">
  <intent-filter>
    <action android:name="com.example.broadcasttest. MY_BROADCAST"/>
  </intent-filter>
</receiver>

可 以 看 到 , 这 里 让 MyBroadcastReceiver 接 收 一 条 值 为 com.example.broadcasttest.
MY_BROADCAST的广播,因此待会儿在发送广播的时候,我们就需要发出这样的一条广播。

发送广播的方法:Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");

        sendBroadcast(intent);

这样我们就成功完成了发送自定义广播的功能。另外,由于广播是使用 Intent 进行传递
的,因此你还可以在 Intent 中携带一些数据传递给广播接收器。

标准广播(夸程序监听自定义广播)

广播是一种可以跨进程的通信方式,这一点从前面接收系统广播的时候就可以看出来
了。因此在我们应用程序内发出的广播,其他的应用程序应该也是可以收到的。只需要在注册的时候把过滤器中的name的值写为需要监听的自定义广播就可以

有序广播(可以截断阻值广播继续传播)

在一个程序中发送有序广播 并在该程序的xml文件中把接收该广播的优先级设为最高 然后在接收广播后执行的方法中截断广播

Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");

sendOrderedBroadcast(intent, null);//发送有序广播

在xml文件中

<receiver android:name=".MyBroadcastReceiver">
  <intent-filter android:priority="100" > 优先级定位100 最高
    <action android:name="com.example.broadcasttest.MY_BROADCAST"/>
  </intent-filter>
</receiver>

public class MyBroadcastReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    Toast.makeText(context, "received in MyBroadcastReceive",Toast.LENGTH_SHORT).show();
    abortBroadcast();//截断广播的继续传播,使得其他注册了该广播的程序也接收不到该广播
  }
}

使用本地广播

前面我们发送和接收的广播全部都是属于系统全局广播,即发出的广播可以被其他任何
的任何应用程序接收到,并且我们也可以接收来自于其他任何应用程序的广播。这样就很容
易会引起安全性的问题,比如说我们发送的一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。

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

本地广播的用法并不复杂,主要就是使用了一个 LocalBroadcastManager 来对广播进行
管理,并提供了发送广播和注册广播接收器的方法。下面我们就通过具体的实例来尝试一下
它的用法,修改 MainActivity 中的代码,如下所示:

public class MainActivity extends Activity {
    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 OnClickListener() {
            @Override
            public 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();//new出广播接收者对象 localBroadcastManager.registerReceiver(localReceiver, intentFilter); // 注册本地广播监听器 } @Override protected void onDestroy() { super.onDestroy(); } class LocalReceiver extends BroadcastReceiver {//广播接收者类 @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "received local broadcast",Toast.LENGTH_SHORT).show(); } } }

最后我们再来盘点一下使用本地广播的几点优势吧。
1. 可以明确地知道正在发送的广播不会离开我们的程序,因此不需要担心机密数据泄
漏的问题。
2. 其他的程序无法将广播发送到我们程序的内部,因此不需要担心会有安全漏洞的隐
患。
3. 发送本地广播比起发送系统全局广播将会更加高效。

 注意:在广播里跳转activity的时候

Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

Context中有一个startActivity方法,Activity继承自Context,重载了startActivity方法。如果使用Activity的startActivity方法,不会有任何限制,而如果使用Context的startActivity方法的话,就需要开启一个新的task,遇到上面那个异常的,都是因为使用了Context的startActivity方法。解决办法是,加一个flag。

//在广播中实现activity的跳转需要加flags
public class DownLoadResever extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setClass(context,Main2Activity.class);
        context.startActivity(intent);
    }
}

 * 注意!!!
* BroadcastReceiver生命周期只有十秒左右,
* 如果在onReceiver内做超过十秒内的事情,就会报错
* 应该通过发送Intent给service,由service来完成
* 不建议在广播中开启子线程

posted @ 2016-04-19 21:19  ts-android  阅读(296)  评论(0编辑  收藏  举报