[Android随笔]BroadcastReceiver广播机制

来源:互联网 发布:linux spi驱动移植 编辑:程序博客网 时间:2024/05/22 16:06

一,介绍

        android四大组件之一:BroadcastReceiver 翻译成中文:广播接收者。在Android中,Broadcast是一种广泛运用在应用程序之间传输信息的机制。而BroadcastReceiver是对发送出来的Broadcast(广播)进行过滤、接收、响应的一类组件。

        呵呵,作为一个android研发学习者,我们想要学习使用BroadcastReceiver这个组件,当然实践是最好的方式。下面介绍BroadcastReceiver的几种使用方式。

二,使用方式

2.1 静态注册

    静态注册分为两步。

        第一步:创建一个继承BroadcastReceiver的子类,重写onReceive(Context context,Intent intent)方法。

        第二步:在AndroidManifest.xml中用标签声明注册,并在标签内设置过滤器,指定接收哪些action。

源码1:MyBroadcastDemo.java

public class MyBroadcastDemo extends BroadcastReceiver{    @Override    public void onReceive(Context context, Intent intent) {        System.out.println("[MyBroadcastDemo]onReceive()");    }}
源码2:AndroidManifest.xml

<receiver android:name=".recoder.MyBroadcastDemo">// 继承BroadcastReceiver的类    <intent-filter>        <action android:name="cn.com.relaxmeet.GAME_BEGIN"/>//使用过滤器,接收指定Action的广播    </intent-filter></receiver>

通过上面两个步骤,我们已经完成了BroadcastReceiver的静态注册方式。

        接着,我们需要对上述方式进行验证。发送广播的实现代码如下:

源码3: 

public void sendBroadcast(){    Intent intent = new Intent();    intent.setAction("cn.com.relaxmeet.GAME_BEGIN"); // 设置广播的Action    sendBroadcast(intent); // 发送广播}
发送广播,指定广播的Action,只有在过滤器中添加了,指定Action的广播接收器,才能通过过滤,接收并响应广播。上述测试代码指定的Action是:cn.com.relaxmeet.GAME_BEGIN。文章看再多,不如亲手试一试,可以写一个Demo测试BroadcastReceiver的静态注册方式。

2.2 动态注册

    动态注册方式与静态注册方式在代码结构上的区别比较少,我们先展示代码:

源码4:

public void registerReceiver(){    IntentFilter intentFilter = new IntentFilter();// 过滤器    intentFilter.addAction("cn.com.relaxmeet.GAME_BEGIN"); // 指定Action    mBroadcastReceiver = new MyBroadcastDemo2();    registerReceiver(mBroadcastReceiver, intentFilter);// 注册广播接收器 }public void unRegisterReceiver(){    unregisterReceiver(mBroadcastReceiver);// 注销广播接收器}
上述两个方法,可以分别在onCreate()和onDestroy(),或者onResume()和onPause()中调用。动态注册广播后,必须适时的unRegister,否则会有内存泄漏。

展示在onCreate()和onDestroy()中调用注册接收者和注销广播接收者。完整的Activity的代码如下

源码5:

public class TranManagerActivity extends Activity{    private MyBroadcastDemo2 mBroadcastReceiver;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        registerReceiver();    }    public void registerReceiver(){        IntentFilter intentFilter = new IntentFilter();        intentFilter.addAction("cn.com.relaxmeet.GAME_BEGIN");        mBroadcastReceiver = new MyBroadcastDemo2();        registerReceiver(mBroadcastReceiver, intentFilter);    }    // 注销广播接收器    public void unRegisterReceiver(){        unregisterReceiver(mBroadcastReceiver);    }    @Override    protected void onDestroy() {        super.onDestroy();        unRegisterReceiver();    }}

2.3 两种使用方式区别

2.3.1 两种注册方式在代码结构上就有差异(源码2和源码4)所以在接收响应上存在差异。

2.3.2 静态注册的广播,在应用没有起动的状态下也能过滤-接收-响应到广播。而动态注册的广播则调用了registerReceiver()方法之后才能接收到广播。根据这点,所以在有需要注册接收系统的广播时,使用静态注册方式。

2.3.3 动态注册方式相比较静态注册方式来说比较灵活。使用动态注册方式使用广播时,registerReceiver()与unRegisterReceiver()成对存在。如果调用了unRegisterReceiver()后,广播接收器就接收不到广播了。

三,生命周期

3.1 生命周期描述

根据官方文档介绍:http://developer.android.com/reference/android/content/BroadcastReceiver.html

A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.

一个BroadcastReceiver对象的可用时间是在onReceive()方法中,一旦从这个方法return,系统就认为这个对象已经执行完成,不再处于活跃状态,也就是说这个对象的生命周期就此结束了。

3.2 生命周期图

BroadcastReceiver最主要的方法是onReceive(),让我看看该方法源码介绍

3.3 源码介绍

源码6:

 /**     * This method is called when the BroadcastReceiver is receiving an Intent     * broadcast.  During this time you can use the other methods on     * BroadcastReceiver to view/modify the current result values.  This method     * is always called within the main thread of its process, unless you     * explicitly asked for it to be scheduled on a different thread using     * {@link android.content.Context#registerReceiver(BroadcastReceiver,     * IntentFilter, String, android.os.Handler)}. When it runs on the main     * thread you should     * never perform long-running operations in it (there is a timeout of     * 10 seconds that the system allows before considering the receiver to     * be blocked and a candidate to be killed). You cannot launch a popup dialog     * in your implementation of onReceive().     *     * <p><b>If this BroadcastReceiver was launched through a <receiver> tag,     * then the object is no longer alive after returning from this     * function.</b>  This means you should not perform any operations that     * return a result to you asynchronously -- in particular, for interacting     * with services, you should use     * {@link Context#startService(Intent)} instead of     * {@link Context#bindService(Intent, ServiceConnection, int)}.  If you wish     * to interact with a service that is already running, you can use     * {@link #peekService}.     *      * <p>The Intent filters used in {@link android.content.Context#registerReceiver}     * and in application manifests are <em>not</em> guaranteed to be exclusive. They     * are hints to the operating system about how to find suitable recipients. It is     * possible for senders to force delivery to specific recipients, bypassing filter     * resolution.  For this reason, {@link #onReceive(Context, Intent) onReceive()}     * implementations should respond only to known actions, ignoring any unexpected     * Intents that they may receive.     *      * @param context The Context in which the receiver is running.     * @param intent The Intent being received.     */    public abstract void onReceive(Context context, Intent intent);
翻译与个人理解:

[1,什么时候该方法被调用] 当BroadcastReceiver接收到一个Intent的广播时,该方法会被调用。[2,可以在该方法中做什么] 我们可以在这个方法中可以使用其他方法显示或者修改数据。[3,不能在该方法中做什么]通常BroadcastReceiver的onReceive()方法是在主线程(UI主线程)中被调用,因为UI主线程的特性,在这个方法中,我们不能进行长时间操作,否则会产生ANR异常(如:网络请求)。官方介绍说,该方法有10s的生命周期时间,所以类似启动一个popup dialog这样的操作,在onReceive()方法中是不允许使用的。[4,其他条件] BroadcastReceiver只接收已知的action。

四,应用场景 (以下内容只用作学习)

4.1 监听短信

步骤1:添加权限

<uses-permission android:name="ANDROID.PERMISSION.RECEIVE_SMS"/>
步骤2:指定BroadcastReceiver添加action

<receiver android:name=".recoder.MyBroadcastDemo">    <intent-filter android:priority="1000">        <action android:name="android.provider.Telephony.SMS_RECEIVED"/>    </intent-filter></receiver>

步骤3:onReceive()方法筛选action,解析intent中携带的数据

public class MyBroadcastDemo2 extends BroadcastReceiver {    static final String SMS_ACTION = "android.provider.Telephony.SMS_RECEIVED";    @Override    public void onReceive(Context context, Intent intent) {        if (SMS_ACTION.equals(intent.getAction())) {            Object [] pdus= (Object[]) intent.getExtras().get("pdus");            for(Object pdu:pdus){                SmsMessage smsMessage = SmsMessage.createFromPdu((byte [])pdu);                String sender = smsMessage.getDisplayOriginatingAddress();                String content = smsMessage.getMessageBody();                long date = smsMessage.getTimestampMillis();                // 拦截短信                if ("123456".equals(sender)) {                    abortBroadcast();                }                //TODO 进行其他操作            }        }    }}

4.2 监听网络变化

步骤1:在AndroidManifest.xml文件中添加权限

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><uses-permission android:name="android.permission.INTERNET" />
步骤2:添加广播接收Action
<receiver android:name=".recoder.MyBroadcastDemo">    <intent-filter>        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />    </intent-filter></receiver>
步骤3:实现onReceive方法

    public void onReceive(Context context, Intent intent) {        final String action = intent.getAction();        // public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";        if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(action){            return;        }        boolean noConnection = intent.getBooleanExtra(                ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);        // TODO    }

附带:网络判断帮助类

1)判断是否网络连接

public boolean isNetworkConnected(Context context) {         if (context != null) {             ConnectivityManager mConnectivityManager = (ConnectivityManager) context                     .getSystemService(Context.CONNECTIVITY_SERVICE);             NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();             if (mNetworkInfo != null) {                 return mNetworkInfo.isAvailable();             }         }         return false;     }  

2)判断是否为WI-FI

   public boolean isWifiConnected(Context context) {         if (context != null) {             ConnectivityManager mConnectivityManager = (ConnectivityManager) context                     .getSystemService(Context.CONNECTIVITY_SERVICE);             NetworkInfo mWiFiNetworkInfo = mConnectivityManager                     .getNetworkInfo(ConnectivityManager.TYPE_WIFI);             if (mWiFiNetworkInfo != null) {                 return mWiFiNetworkInfo.isAvailable();             }         }         return false;     }  

3)判断是否为手机流量

public boolean isMobileConnected(Context context) {         if (context != null) {             ConnectivityManager mConnectivityManager = (ConnectivityManager) context                     .getSystemService(Context.CONNECTIVITY_SERVICE);             NetworkInfo mMobileNetworkInfo = mConnectivityManager                     .getNetworkInfo(ConnectivityManager.TYPE_MOBILE);             if (mMobileNetworkInfo != null) {                 return mMobileNetworkInfo.isAvailable();             }         }         return false;     }  
4)获取当前网络连接类型
 public static int getConnectedType(Context context) {         if (context != null) {             ConnectivityManager mConnectivityManager = (ConnectivityManager) context                     .getSystemService(Context.CONNECTIVITY_SERVICE);             NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();             if (mNetworkInfo != null && mNetworkInfo.isAvailable()) {                 return mNetworkInfo.getType();             }         }         return -1;     }  

4.3 监听手机电量

步骤1: 在AndroidManifest.xml文件中添加权限

    <uses-permission android:name="android.permission.BATTERY_STATS"/>
步骤2:添加广播可接收action

        <receiver android:name=".recoder.MyBroadcastDemo">            <intent-filter>                <action android:name="android.intent.action.BATTERY_LOW"/>                <action android:name="android.intent.action.BATTERY_CHANGED"/>                <action android:name="android.intent.action.BATTERY_OKAY"/>            </intent-filter>        </receiver>
上手上面我添加了接收系统发送电量过低,弹出对话框的广播信号。

步骤3:实现onReceive()方法

public class MyBroadcastDemo extends BroadcastReceiver{    @Override    public void onReceive(Context context, Intent intent) {        if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())){            // 得到系统当前电量            int level = intent.getIntExtra("level", 0);            // 取得系统总电量            int total = intent.getIntExtra("scale", 100);            //TODO 其他处理        }    }}

4.4 监听手机开机

步骤1:在AndroidManifest.xml中添加权限

 <uses-permission android:name="ANDROID.PERMISSION.RECEIVE_BOOT_COMPLETED"/>
步骤2:添加BroadcastReceiver指定ACTION
        <receiver android:name=".recoder.MyBroadcastDemo">            <intent-filter>                <action android:name="android.intent.action.BOOT_COMPLETED"/>            </intent-filter>        </receiver>
步骤3:重写onReceive()方法

public class MyBroadcastDemo extends BroadcastReceiver{    @Override    public void onReceive(Context context, Intent intent) {    // public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {            // 开机启动的Activity              Intent activityIntent = new Intent(context, StartOnBootActivity.class);            activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            // 启动Activity              context.startActivity(activityIntent);            // 开机启动的Service              Intent serviceIntent = new Intent(context, StartOnBootService.class);            // 启动Service              context.startService(serviceIntent);        }    }}
上面几个例子仅用于学习,不要做坏事。

五,安全性

5.1 常见场景方案

      在BroadcastReceiver实际的使用中,我们可能不希望其他应用能够接收到我们应用发出的广播。同样,我们的广播接收器也不希望接收不明广播信息。下面几种场景需要我们考虑。

场景1:当你在应用的manifest中注册一个Receiver时,你会在intent-filters中添加一个action,其他应用可以通过发送一个pass这个filters,发送一下信息给你的广播接收器。这样就会导致不安全。

解决方案可以是:在receiver中添加android:exported="false"

<receiver android:name=".recoder.MyBroadcastDemo"            android:exported="false">    <intent-filter>        <action android:name="cn.com.relaxmeet.GAME_BEGIN"></action>    </intent-filter></receiver>

场景2:当你sendBroadcast(Intent),一般情况其他应用能够接收到这些广播信息,为了防止其他应用能够接收到广播信息,你可能会添加一些权限描述,比方说设置指定的包名Intent.setPackage

    public void sendBroadcast(){        Intent intent = new Intent();        intent.setAction("cn.com.relaxmeet.GAME_BEGIN"); // 设置广播的Action        intent.setPackage("cn.com.relaxmeet");        sendBroadcast(intent); // 发送广播    }

5.2 LocalBroadcastManager

如果使用LocalBroadcastManager,上述5.1的几种场景的问题将不存在。Intent的广播将只在当前进程中传递。

使用该方案的优势:1,数据不会离开当前应用,不要担心数据被其他应用接收。2,这个广播接收器也接收不到其他应用发送的广播信息。3,这比全局广播的效率高

5.2.1 使用方式

public class TranManagerActivity extends Activity{    private MyBroadcastDemo2 mBroadcastReceiver;    private LocalBroadcastManager lbm;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        registerReceiver();    }    public void registerReceiver(){        IntentFilter intentFilter = new IntentFilter();        intentFilter.addAction("cn.com.relaxmeet.GAME_BEGIN");        mBroadcastReceiver = new MyBroadcastDemo2();        lbm = LocalBroadcastManager.getInstance(getApplicationContext());        lbm.registerReceiver(mBroadcastReceiver, intentFilter);    }    public void unRegisterReceiver(){        lbm.unregisterReceiver(mBroadcastReceiver);    }    @Override    protected void onDestroy() {        super.onDestroy();        unRegisterReceiver();    }}

发送广播:

    public void sendBroadcast(){        LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getApplicationContext());        Intent intent = new Intent();        intent.setAction("cn.com.relaxmeet.GAME_BEGIN");        lbm.sendBroadcast(intent);    }

六,扩展

6.1 有序广播

优点:1,按优先级的不同,优先Receiver可对数据进行处理,广播可传递到下一个Receiver

           2,通过abortBroadcast可终止广播的传播

缺点:效率低

系统的短信接收广播是一个有序广播,我们可以监听短信接收广播,可以做垃圾短信拦截。

发送广播的方法:sendOrderedBroadcast()

优先接收到Broadcast的Receiver可通过setResultExtra()将数据存入Broadcast中,下一个Receiver可以通过Bundle bundle = getResultExtras(true)方法获取上一个Receiver传来的数据。

测试结果预测:点击按钮,两个Receiver都能接收到同一条广播,在logcat中打印出数据。Receiver的优先顺序,Receiver2先、Receiver1后。Receiver1接收到Receiver传递来的字符串信息,“来自Receiver2的问候:你好"。

测试代码:

代码7:AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="cn.com.relaxmeet" >        <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:theme="@style/AppTheme" >        <activity            android:name=".MainActivity"            android:label="@string/app_name"            android:configChanges="orientation|keyboardHidden|screenSize">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>        <receiver android:name=".recoder.MyBroadcastDemo">            <intent-filter android:priority="300">                <action android:name="cn.com.relaxmeet.GAME_BEGIN"></action>            </intent-filter>        </receiver>        <receiver android:name=".recoder.MyBroadcastDemo2">            <intent-filter android:priority="1000">                <action android:name="cn.com.relaxmeet.GAME_BEGIN"></action>            </intent-filter>        </receiver>    </application></manifest>
代码8:MainActivity.java
public class MainActivity extends Activity implements View.OnClickListener {    private Button mBtnRead;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mBtnRead = (Button) findViewById(R.id.btn_read);        mBtnRead.setOnClickListener(this);    }    @Override    public void onClick(View v) {        sendBroadcast();    }    public void sendBroadcast(){        Intent intent = new Intent();        intent.setAction("cn.com.relaxmeet.GAME_BEGIN");        Bundle bundle = new Bundle();        bundle.putString("a","aaaaaaa");        intent.putExtras(bundle);        sendOrderedBroadcast(intent,null);    }}
代码9:MyBroadcastDemo.java
public class MyBroadcastDemo extends BroadcastReceiver{    @Override    public void onReceive(Context context, Intent intent) {        System.out.println("[MyBroadcastDemo]");        Bundle bundle = getResultExtras(true);        System.out.println("a=" + bundle.getString("a")+",b=" + bundle.getString("b"));    }}
代码10:MyBroadcastDemo2.java
public class MyBroadcastDemo2 extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {        System.out.println("[MyBroadcastDemo2]");        Bundle bundle = intent.getExtras();        bundle.putString("b","bbbbbbb");        System.out.println("a=" + bundle.getString("a"));        setResultExtras(bundle);        // 切断广播//        abortBroadcast();    }}

测试结果:


0 0
原创粉丝点击