Android06--Android广播接收器

标签: Android


undefined

BroadcastReceiver 是Android 系统中的四大组件(Activity、Service、BroadcastReceiver、ContentProvider)之一。

Android广播机制就是在Android中,有一些操作完成以后,会发送广播。系统在电量低、开机、锁屏等一些操作都会发送一个广播,我们可以在APP里注册这些广播,然后实现自定义功能,如开机自启。当然,我们也可以自定义发送广播。

广播由广播发送者和广播接收者组成。

发送广播:
1)可以使用sendBroadcast()发送普通广播;
2) 可以使用sendOrderedBroadcast()发送有序广播。

接收广播:

  1. 先静态或者动态注册广播接收器;
    2)然后在相应的接收器里实现自定义功能。

终止广播:

  1. 可以使用abortBroadcast()终止有序广播。

发送和接收自定义广播 #

新建一个类:MyReceiver.java,该类需要继承BroadcastReceiver,我们需要编写回调方法onReceive(),以处理接收的广播消息:

package com.a52fhy.learnbroadcastreciver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyReceiver extends BroadcastReceiver {
    public MyReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        System.out.println("接收到了消息");
    }
}

上面代码我们实现自定义广播接收的功能。下面我们来实现发送广播的功能。

我们在MainActivity里使用sendBroadcast()发送广播:

sendBroadcast(new Intent(MainActivity.this, MyReceiver.class));

这个和启动Activity是很类似的,需要传入一个Intent。这里使用了显式IntentBroadcastReceiver也支持隐式Intent

然后在AndroidManifest.xml注册广播接收器:

<receiver
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true"></receiver>

启动APP,会打印:

com.a52fhy.learnbroadcastreciver I/System.out: 接收到了消息

BroadcastReceiver也支持隐式Intent,通常推荐这种用法:

<receiver
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true">
        <intent-filter >
            <action android:name="com.a52fhy.learnbroadcastreciver.intent.action.MyReceiver"></action>
        </intent-filter>
</receiver>
</application>

intent-filter里我们添加了action属性。发送广播的时候我们改成这样就可以了:

sendBroadcast(new Intent("com.a52fhy.learnbroadcastreciver.intent.action.MyReceiver"));

自定义广播名称的规范:包名 + .intent.action + .广播名称

动态注册和注销广播接收器 #

除了在AndroidManifest.xml直接注册(静态注册)接收器,还可以在程序里动态注册和注销BroadcastReceiver

注册和注销使用到registerReceiver()unregisterReceiver()两个方法。

由于是动态注册,我们会在注册的时候给这个service加个intent-filter,发送广播的时候传的Intent是这个:

//Intent i = new Intent(MainActivity.this, MyReceiver.class); //该写法将无法发送广播

Intent i = new Intent(MyReceiver.ACTION);

MyReciver.java

package com.a52fhy.learnbroadcastreciver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class MyReceiver extends BroadcastReceiver {

    public final static String ACTION = "com.a52fhy.learnbroadcastreciver.intent.action.MyReceiver";

    public MyReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {

        System.out.println("收到的信息是:" + intent.getStringExtra("msg").toString());

    }
}

该文件里声明了个ACTION常量,方便后续使用。命名方式与Activity一样。onReceive里接收来自Activity传过来的参数。

我们在界面添加三个按钮,分别是发送消息注册广播注销广播

MainActivity里代码:

package com.a52fhy.learnbroadcastreciver;

import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private MyReceiver receiver = null;

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

        //sendBroadcast(new Intent(MainActivity.this, MyReceiver.class));

        findViewById(R.id.btnSendMsg).setOnClickListener(this);
        findViewById(R.id.btnReg).setOnClickListener(this);
        findViewById(R.id.btnUnreg).setOnClickListener(this);
    }

    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btnSendMsg:
                Intent i = new Intent(MyReceiver.ACTION);
                i.putExtra("msg","新消息!");
                sendBroadcast(i);
                break;

            case R.id.btnReg:
                if(receiver == null){
                    receiver = new MyReceiver();
                    registerReceiver(receiver, new IntentFilter(MyReceiver.ACTION));
                }
                break;

            case R.id.btnUnreg:
                if(receiver != null){
                    unregisterReceiver(receiver);
                    receiver = null;
                }
                break;
        }
    }
}

判断当实例化的receiver不为空就可以注册,反之可以注销。我们点击发送信息,不会触发发送广播;当点击了注册广播,就可以发送广播了。注意sendBroadcast里我们通过Intent传了参数过去,实例化Intent的时候是:

new Intent(MyReceiver.ACTION);

以上代码中动态注册的时候,如果一次性注册多个,可以使用下面的方式:

receiver = new MyReceiver();

IntentFilter filter = new IntentFilter();  

filter.addAction(MyReceiver.ACTION); //自定义广播
filter.addAction(Intent.ACTION_SCREEN_ON); //屏幕亮
filter.addAction(Intent.ACTION_SCREEN_OFF); //屏幕灭

registerReceiver(receiver, filter);

静态注册和动态注册没什么区别,一个在xml里注册,一个使用程序注册。系统发送的广播里有些是需要静态注册的,有些是需要动态注册的。例如:

//只能静态注册
Intent.ACTION_BOOT_COMPLETED //系统启动完成
Intent.ACTION_MEDIA_MOUNTED //SD卡挂载
Intent.ACTION_MEDIA_UNMOUNTED //SD卡卸载
Intent.ACTION_USER_PRESENT//解除锁屏
ConnectivityManager.CONNECTIVITY_ACTION//网络状态变化

//只能动态注册:程序适应系统变化做操作,程序运行状态才能接收到
Intent.ACTION_SCREEN_ON //屏幕亮
Intent.ACTION_SCREEN_OFF //屏幕灭
Intent.ACTION_TIME_TICK //时间变化  每分钟一次

另外,静态注册的广播在APP没有运行的时候也可以收得到。

BroadcastReceiver 的优先级 #

静态注册的时候,我们还可以设置receiver的优先级。

<receiver
    android:name=".MyReceiver"
    android:enabled="true"
    android:exported="true">
        <intent-filter android:priority="1">
            <action android:name="com.a52fhy.learnbroadcastreciver.intent.action.MyReceiver"></action>
        </intent-filter>
</receiver>
        
 <receiver
    android:name=".MyReceiver1"
    android:enabled="true"
    android:exported="true">
        <intent-filter android:priority="2">
            <action android:name="com.a52fhy.learnbroadcastreciver.intent.action.MyReceiver"></action>
        </intent-filter>
</receiver>       

上面代码里我们使用android:priority定义了接收器的优先级,数值大的优先级高。如果没有定义优先级,后注册的优先级高。两个接收器会按照先后顺序执行。

我们也可以在高优先级的接收器里终止广播,不让其它接收器接收:

System.out.println("收到的信息是:" + intent.getStringExtra("msg").toString());

abortBroadcast();//终止广播

这时候不能使用原来的sendBroadcast(i)发送广播,而应该使用sendOrderedBroadcast(i,null),否则会抛出异常。

接收系统广播 #

本例将说明如何接受当网络变化时系统发送的广播。

广播名称:android.net.conn.CONNECTIVITY_CHANGE

我们新建个应用NetworkChangeBR,并新建个广播接收器NetworkChangeBR

首先,我们需要在AndroidManifest.xml里注册:

<receiver android:name=".NetworkChangeBR">
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE"></action>
    </intent-filter>
</receiver>

由于程序里将访问网络状态,需要申明权限:

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

编写接收器逻辑:
NetworkChangeBR.java

package com.a52fhy.networkchangebr;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;
import android.util.Log;

/**
 * Created by YJC on 2016/9/18 018.
 */
public class NetworkChangeBR extends BroadcastReceiver{
    /**
     * @param context The Context in which the receiver is running.
     * @param intent  The Intent being received.
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        System.out.println("网络发生了变化");

        ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

        if(networkInfo != null && networkInfo.isAvailable()){
            System.out.println("网络已连接");
        }else{
            System.out.println("网络已断开");
        }
    }
}

运行程序,我们开关网络,将会看到如下log:

09-18 21:52:37.484 24226-24226/com.a52fhy.networkchangebr I/System.out: 网络发生了变化
09-18 21:52:37.484 24226-24226/com.a52fhy.networkchangebr I/System.out: 网络已连接

09-18 21:52:43.604 24226-24226/com.a52fhy.networkchangebr I/System.out: 网络发生了变化
09-18 21:52:43.604 24226-24226/com.a52fhy.networkchangebr I/System.out: 网络已断开

常见系统广播 #

//关闭或打开飞行模式时的广播
Intent.ACTION_AIRPLANE_M;

//充电状态,或者电池的电量发生变化;//电池的充电状态、电荷级别改变,不能通过组建声;
Intent.ACTION_BATTERY_CH;

//表示电池电量低
Intent.ACTION_BATTERY_LO;

//表示电池电量充足
Intent.ACTION_BATTERY_OK;

//关闭或打开飞行模式时的广播
Intent.ACTION_AIRPLANE_MODE_CHANGED;

//充电状态,或者电池的电量发生变化//电池的充电状态、电荷级别改变,不能通过组建声明接收这个广播,只有通过Context.registerReceiver()注册
Intent.ACTION_BATTERY_CHANGED;

//表示电池电量低
Intent.ACTION_BATTERY_LOW;

//表示电池电量充足,即从电池电量低变化到饱满时会发出广播
Intent.ACTION_BATTERY_OKAY;

//在系统启动完成后,这个动作被广播一次(只有一次)。
Intent.ACTION_BOOT_COMPLETED;

//按下照相时的拍照按键(硬件按键)时发出的广播
Intent.ACTION_CAMERA_BUTTON;

//当屏幕超时进行锁屏时,当用户按下电源按钮,长按或短按(不管有没跳出话框),进行锁屏时,android系统都会广播此Action消息
Intent.ACTION_CLOSE_SYSTEM_DIALOGS;

//设备当前设置被改变时发出的广播(包括的改变:界面语言,设备方向,等,请参考Configuration.java)
Intent.ACTION_CONFIGURATION_CHANGED;

//设备日期发生改变时会发出此广播
Intent.ACTION_DATE_CHANGED;

//设备内存不足时发出的广播,此广播只能由系统使用,其它APP不可用
Intent.ACTION_DEVICE_STORAGE_LOW;

//设备内存从不足到充足时发出的广播,此广播只能由系统使用,其它APP不可用
Intent.ACTION_DEVICE_STORAGE_OK;

//发出此广播的地方frameworks\base\services\java\com\android\server\DockObserver.java
Intent.ACTION_DOCK_EVENT;

//移动APP完成之后,发出的广播(移动是指:APP2SD)
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE;

//正在移动APP时,发出的广播(移动是指:APP2SD)
Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;

//Gtalk已建立连接时发出的广播
Intent.ACTION_GTALK_SERVICE_CONNECTED;

//Gtalk已断开连接时发出的广播
Intent.ACTION_GTALK_SERVICE_DISCONNECTED;

//在耳机口上插入耳机时发出的广播
Intent.ACTION_HEADSET_PLUG;

//改变输入法时发出的广播
Intent.ACTION_INPUT_METHOD_CHANGED;

//设备当前区域设置已更改时发出的广播
Intent.ACTION_LOCALE_CHANGED;

//表示用户和包管理所承认的低内存状态通知应该开始。
Intent.ACTION_MANAGE_PACKAGE_STORAGE;

//未正确移除SD卡(正确移除SD卡的方法:设置--SD卡和设备内存--卸载SD卡),但已把SD卡取出来时发出的广播 ,扩展介质(扩展卡)已经从 SD 卡插槽拔出,但是挂载点 (mount point) 还没解除 (unmount)
Intent.ACTION_MEDIA_BAD_REMOVAL;

//按下"Media Button" 按键时发出的广播,假如有"Media Button" 按键的话(硬件按键)
Intent.ACTION_MEDIA_BUTTON;

//插入外部储存装置,比如SD卡时,系统会检验SD卡,此时发出的广播?
Intent.ACTION_MEDIA_CHECKING;

//已拔掉外部大容量储存设备发出的广播(比如SD卡,或移动硬盘),不管有没有正确卸载都会发出此广播, 用户想要移除扩展介质(拔掉扩展卡)。
Intent.ACTION_MEDIA_EJECT;

//插入SD卡并且已正确安装(识别)时发出的广播, 扩展介质被插入,而且已经被挂载。
Intent.ACTION_MEDIA_MOUNTED;

//拓展介质存在,但使用不兼容FS(或为空)的路径安装点检查介质包含在Intent.mData领域。
Intent.ACTION_MEDIA_NOFS;

//外部储存设备已被移除,不管有没正确卸载,都会发出此广播, 扩展介质被移除。
Intent.ACTION_MEDIA_REMOVED;

//广播:已经扫描完介质的一个目录
Intent.ACTION_MEDIA_SCANNER_FINISHED;

//请求媒体扫描仪扫描文件并将其添加到媒体数据库。
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE;

//广播:开始扫描介质的一个目录
Intent.ACTION_MEDIA_SCANNER_STARTED;

// 广播:扩展介质的挂载被解除 (unmount),因为它已经作为 USB 大容量存储被共享。
Intent.ACTION_MEDIA_SHARED;

Intent.ACTION_MEDIA_UNMOUNTABLE;//

// 广播:扩展介质存在,但是还没有被挂载 (mount)
Intent.ACTION_MEDIA_UNMOUNTED

Intent.ACTION_NEW_OUTGOING_CALL;

//成功的安装APK之后//广播:设备上新安装了一个应用程序包。
//一个新应用包已经安装在设备上,数据包括包名(最新安装的包程序不能接收到这个广播)
Intent.ACTION_PACKAGE_ADDED;

//一个已存在的应用程序包已经改变,包括包名
Intent.ACTION_PACKAGE_CHANGED;

//清除一个应用程序的数据时发出的广播(在设置--应用管理--选中某个应用,之后点清除数据时)
//用户已经清除一个包的数据,包括包名(清除包程序不能接收到这个广播)
Intent.ACTION_PACKAGE_DATA_CLEARED;

//触发一个下载并且完成安装时发出的广播,比如在电子市场里下载应用
Intent.ACTION_PACKAGE_INSTALL;

//成功的删除某个APK之后发出的广播, 一个已存在的应用程序包已经从设备上移除,包括包名(正在被安装的包程序不能接收到这个广播)
Intent.ACTION_PACKAGE_REMOVED;

//替换一个现有的安装包时发出的广播
Intent.ACTION_PACKAGE_REPLACED;

//用户重新开始一个包,包的所有进程将被杀死,所有与其联系的运行时间状态应该被移除,包括包名(重新开始包程序不能接收到这个广播)
Intent.ACTION_PACKAGE_RESTARTED;

//插上外部电源时发出的广播
Intent.ACTION_POWER_CONNECTED;

//已断开外部电源连接时发出的广播
Intent.ACTION_POWER_DISCONNECTED;

Intent.ACTION_PROVIDER_CHANGED;//

//重启设备时的广播
Intent.ACTION_REBOOT;

//屏幕被关闭之后的广播
Intent.ACTION_SCREEN_OFF;

//屏幕被打开之后的广播
Intent.ACTION_SCREEN_ON;

//关闭系统时发出的广播
Intent.ACTION_SHUTDOWN;

//时区发生改变时发出的广播
Intent.ACTION_TIMEZONE_CHANGED;

//时间被设置时发出的广播
Intent.ACTION_TIME_CHANGED;

//广播:当前时间已经变化(正常的时间流逝), 当前时间改变,每分钟都发送,不能通过组件声明来接收
,只有通过Context.registerReceiver()方法来注册
Intent.ACTION_TIME_TICK;

//一个用户ID已经从系统中移除发出的广播
Intent.ACTION_UID_REMOVED;

//设备已进入USB大容量储存状态时发出的广播
Intent.ACTION_UMS_CONNECTED;

//设备已从USB大容量储存状态转为正常状态时发出的广播
Intent.ACTION_UMS_DISCONNECTED;

Intent.ACTION_USER_PRESENT;//

//设备墙纸已改变时发出的广播
Intent.ACTION_WALLPAPER_CHANGED;
Build by Loppo 0.6.6