Broadcast Receiver 介绍

474 查看

广播 接收、效率很低、只有先注册了广播才能发送广播。

使用 Intent 来广播一个事件,让你和第三方开发人员对事件作出反应,可以通过监听 Broadcast Intent 来对设备状态变化和第三方应用程序事件作出反应。

Android 中使用了非常多的 Broadcast Intent 来广播系统事件,例如 网络变化、电池电量、来电。


使用 Intent 来广播事件

构建一个 Intent,可以对 Intent 的动作字符串、数据、分类进行设置,从而使 Broadcat Intent 能够更加精准的定位广播范围。

  • 动作字符串 可以用来标识要广播的事件,需要是唯一的。习惯上使用 Java 包名的方式构建
    public static final String NEW_LIFEFORM_DETECTED = "com.example.intent.NEW_LIFTFORM";
  • 数据 可以使用 data属性指定一个 URI,也可以使用 extras 来添加额外的基本值。
  • 分类


使用 Broadcast Receiver 来监听广播

Broadcast Receiver 通常称为接收器,用来监听 Broadcast Intent。要使 Broadcast Receiver 能够接收广播,就需要对其进行注册。可以通过代码的形势注册,也可以在 manifest 文件中配置。无论怎么注册都需要使用 Intent Filter 来指定他要监听哪些 Intent 和数据。

Manifest 文件注册的接收器应用程序,在 Intent 被广播出去的时候,应用程序不一定非要处于运行状态,当匹配的 Intent 被广播出去的时候,他们会被自动地启动。

要创建一个 Broadcast Receiver 需要扩展 BroadcastReceiver 类,并重写 onReceive 方法。

public class MyBroadcastReceiver extends BroadcastReceiver {
    @override
    public void onReceive(Context context, Intent intent) {
        // ....
    }
}

当接收到一个与在注册接收器时使用的 Intent Filter 相匹配的 Broadcast Intent 的时候,就会执行 onReceive 方法,该方法必须在5秒内处理完成,否者会显示 Force Close 对话框。
一般情况下,Broadcast Receiver 将会更新内容、启动 Service、更新 Activity UI,或者使用 Notification Manager 来通知用户。

public class LifeformDetectedReceiver extends BroadcastReceiver {

    public static final String EXTRA_LIFEFORM_NAME = "EXTRA_LIFEFORM_NAME";
    public static final String EXTRA_LATITUDE = "EXTRA_LATITUDE";
    public static final String EXTRA_LONGITUDE = "EXTRA_LONGITUDE";

    public static final String ACTION_BURN = "com.example.intent_demo.receiver.action.BURN_IF_WITH_FIRE";

    public static final String NEW_LIFEFORM = "com.example.intent_demo.receiver.action.NEW_LIFEFORM";

    @Override
    public void onReceive(Context context, Intent intent) {
        // 从 Intent 获得 lifeform 的细节
        Uri data = intent.getData();

        String type = intent.getStringExtra(EXTRA_LIFEFORM_NAME);
        double lat = intent.getDoubleExtra(EXTRA_LATITUDE, 0);
        double lng = intent.getDoubleExtra(EXTRA_LONGITUDE, 0);

        Location l = new Location("gps");
        l.setLatitude(lat);
        l.setLongitude(lng);

        if (type.equals("facehugger")) {
            Intent startIntent = new Intent(ACTION_BURN, data);
            startIntent.putExtra(EXTRA_LATITUDE, lat);
            startIntent.putExtra(EXTRA_LONGITUDE, lng);

            context.startService(startIntent);
        }
    }

}

影响特定 UI 的 Broadcast Receiver 通常使用代码注册,代码注册的接收器只会在应用程序运行的时候响应 Broadcast Intent。在用来更新一个 Activity 中的 UI 时非常有用。在 onResume 中注册接收器,在 onPause 中注销它。


使用代码注册 Broadcast Receiver

public class ThreeActivity extends ActionBarActivity {

    private IntentFilter filter = new IntentFilter(LifeformDetectedReceiver.NEW_LIFEFORM);

    private LifeformDetectedReceiver receiver = new LifeformDetectedReceiver(); 

    @Override
    protected void onPause() {
        // 注销 broadcast receiver
        unregisterReceiver(receiver);
    }

    @Override
    protected void onResume() {
        // 注册 broadcast receiver
        registerReceiver(receiver, filter);
    }
}


在 Manifest 中注册 Broadcast Receiver

在 manifest 文件中的 application 节点内部添加一个 receiver 节点,指定要注册的 Broadcast Receiver 的类和要监听的动作字符串。

<receiver android:name="com.example.intent_demo.receiver.LifeformDetectedReceiver">
    <intent-filter>
        <action android:name="com.example.intent_demo.receiver.action.NEW_LIFEFORM"/>
    </intent-filter>
</receiver>


发送 Broadcast Intent 的几种

  1. 普通方式 sendBroadcast
  2. 广播有序的 Intent sendOrderedBroadcast
  3. 广播 Sticky Intent sendStickyBroadcast


Local Broadcast Manager(局部广播管理器)

包含在 Android Support Libray 中,用于简化注册 Broadcast Intent,以及在应用程序内的组件之间发送 Broadcast Intent 的工作。

因为局部广播的作用域要小一些,所有使用 Local Broadcast Manager 比发送全局广播更加高效。而且使用 Local Broadcast Manager 也确保应用程序外部的任何组件都收不到你的广播的 Intent,所以不会有私人数据或敏感数据(如位置信息)泄露出去的风险。

// 创建一个 Local Broadcast Intent 实例
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(this);
lbm.registerReceiver(new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub          
    }   
}, new IntentFilter());

lbm.sendBroadcast(new Intent());
lbm.sendBroadcastSync(new Intent());


监听本地 Broadcast Intent

很多的系统 Service 都会广播 Intent 来指示一种变化,例如:时区改变、数据连接状态、短信或者电话呼叫。

下面罗列了 Intent 类中提供的某些以常量表示的本地动作。

  • ACTION_BOOT_COMPLETED
  • ACTION_CAMERA_BUTTON
  • ACTION_DATE_CHANGED ACTION_TIME_CHANGED
  • ACTION_MEDIA_EJECT
  • ACTION_MEDIA_MOUNTED ACTION_MEDIA_UNMOUNTED
  • ACTION_NEW_OUTGOING_CALL
  • ACTION_SCREEN_OFF ACTION_SCREEN_ON
  • ACTION_TIMEZONE_CHANGED

更多的参考 http://developer.android.com/intl/zh-cn/reference/android/content/Intent.html

监听电量变化

包含有当前电池电量信息和充电状态的 Broadcast Intent 是一个 Sticky Intent,因此不需要实现一个 Broadcast Receiver 就可以在任何时间获取到当前的状态。

IntentFilter batIntentFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent battery = registerReceiver(null, batIntentFilter);

int status = battery.getIntExtra(BatteryManager.EXTRA_STATUS, -1);

boolean isCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL);
监听网络连接变化

网络连接变化的广播不是 sticky 的而且也不包含任何和变化相关的信息,想要获得当前连接状态信息,需要使用 Connectivity Manager。

String svcName = Context.CONNECTIVITY_SERVICE;
ConnectivityManager cm = (ConnectivityManager) this.getSystemService(svcName);

NetworkInfo activeNetwork = cm.getActiveNetworkInfo();

boolean isConnected = activeNetwork.isConnectedOrConnecting();
boolean isMobile = activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE;


在运行时管理 Manifest Receiver

想要减少应用程序的开销,最好禁用一些系统常用事件。可以使用 PackageManager 的 setComponentEnabledSetting 方法开启和禁用应用程序的 manifest receiver

ComponentName name = new ComponentName(this, MyProvider.class);
PackageManager pm = getPackageManager();

// 启用一个 manifest receiver
pm.setComponentEnabledSetting(name,
    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
    PackageManager.DONT_KILL_APP);

// 禁用一个 manifest receiver
pm.setComponentEnabledSetting(name,
    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
    PackageManager.DONT_KILL_APP);