Android Broadcast Receiver 基础详解

原创 2016年08月29日 10:15:21

Android中的广播机制十分灵活,因为Android中的每个应用程序都可以对自己感兴趣的广播进行注册。它提供了一套完整的API允许应用程序自由的发送和接受广播。

广播分为两类:1.标准广播,是一种完全异步执行的广播,在广播发出后,所有的接收器几乎在同一时间收到广播消息;2.有序广播,是一种同步执行的广播,在广播发出后,同一时刻只会有一个广播接收器能够收到这条广播消息,当这个广播接收器中的逻辑执行完毕了,广播才会继续传递,并且前面的广播接收器可以截断广播。

注册广播的方式有两种:1.动态注册即在代码中注册;2.静态注册即在AndroidManifest.xml中注册。



1.动态注册监听网络变化。

package company.testbroadcast;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.widget.Toast;

/**
 * Created by samyang on 2016/8/29.
 */
public class MainActivity extends Activity {

    private IntentFilter mIntentFilter;
    private NetworkChangeReceiver mNetworkChangeReceiver;

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

        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        mNetworkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(mNetworkChangeReceiver, mIntentFilter);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mNetworkChangeReceiver);
    }

    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "network changed", Toast.LENGTH_SHORT).show();
        }
    }
}

首先建立一个NetworkChangeReceiver内部类,让它继承自BroadcastReceiver。并重写其中的onReceive方法。然后,声明并实例化mIntentFilter和mNetworkChangeReceiver,在mIntentFilter中添加一个addAction传入"android.net.conn.CONNECTIVITY_CHANGE"(当系统网络发生变化时,发出一条该值的广播),并注册接收器,传入两个参数:要动态注册的广播接收器实例,和添加了Action的IntentFilter实例。最后在onDestroy方法中取消注册

运行程序,然后按home键回到主界面,此时不能按back键,否则会调用onDestroy方法。然后在手机设置中更改网络状态,即会弹出Toast如下图所示。


此时我们只是在onReceive方法中加入了简单的逻辑。我们并不能知晓当前网络状态是变为有网络还是无网络。因此,我们更改代码,使用户准确的知晓当前网络状态。更改onReceive方法中的代码如下:

private class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = connectivityManager.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();
        }
    }
}

借助getSystemService方法获取connectivityManager实例,然后通过调用connectivityManager的getActivityNetworkInfo方法获取networkInfo实例。这样我们就能够用network实例的isAvailable方法判断当前的网络状态,从而加入相应的逻辑。当然,我们还需要在AndroidManifest.xml中获取网络状态的权限。

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

运行程序,再次更改网络状态后如下图所示:




2.静态注册实现开机启动。

此时你可能发现,我们的广播必须在启动了app之后才会执行onCreate方法,这时写在其中的注册逻辑才能得到执行。但有的时候我们需要在开机后而app未启动前就能够收到广播。由于系统启动完成后会自动发出一条值为android.intent.action.BOOT_COMPLETED的广播,我们以此来做示例。

package company.testbroadcast;

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

/**
 * Created by samyang on 2016/8/29.
 */
public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "boot completed", Toast.LENGTH_SHORT).show();
    }
}

新建一个类,而不是内部类。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="company.testbroadcast">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

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

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

</manifest>

在AndroidManifest中静态注册receiver,而不是在activity的onCreate中去动态注册。当然完了之后一样要拿到RECEIVE_BOOT_COMPLETED的权限。

运行程序,然后重启系统如下图所示:




3.发送标准广播

刚才我们发送的广播都是通过接收器去接收系统的广播,接下来我们打算发送自己的定义的广播然后让指定的接收器接收。

新建一个StandardBroadcastReceiver类。

package company.testbroadcast;

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

/**
 * Created by samyang on 2016/8/29.
 */
public class StandardBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received standard broadcast", Toast.LENGTH_SHORT).show();
    }
}

在布局中添加一个按钮,然后在MainActivity中添加下面一段代码:

Button sendStandard = (Button) findViewById(R.id.send_standard_broadcast);
sendStandard.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent("company.testbroadcast.MY_STANDARD_BROADCAST");
        sendBroadcast(intent);
    }
});

在AndroidManifest.xml中添加注册:

<receiver android:name=".StandardBroadcastReceiver">
    <intent-filter>
        <action android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>
    </intent-filter>
</receiver>

这样当我们点击按钮时,就会发送一个传入了intent的广播,而StandardBroadcastReceiver这个接收器已经静态注册了接收值跟intent一样的广播。因此就会弹出toast提示received standard broadcast。




4.发送有序广播

在新建一个AnotherBroadcast类,在onReceive方法中写入弹出toast提示received another broadcast的逻辑。

package company.testbroadcast;

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

/**
 * Created by samyang on 2016/8/29.
 */
public class AnotherBroadcast extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received another broadcast", Toast.LENGTH_SHORT).show();
    }
}

同样为其进行动态注册。

<receiver android:name=".AnotherBroadcast">
    <intent-filter>
        <action android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>
    </intent-filter>
</receiver>

并将StandardBroadcastReceiver的优先级改为100。

<receiver android:name=".StandardBroadcastReceiver">
    <intent-filter android:priority="100">
        <action android:name="company.testbroadcast.MY_STANDARD_BROADCAST"/>
    </intent-filter>
</receiver>

此时再次点击按钮时,就会先后弹出toast:"received standard broadcast", "received another broadcast"。说明我们通过sendOrderedBroadcast实现了发送有序广播。里面传入两个值:第一个为intent,第二个参数是一个与权限相关的字符串,这里传入null即可。



5.本地广播

此时又出现了一个新的问题,到目前为止,我们发送的都是系统性的全局广播。发送的广播不仅可以被其他任何程序收到造成混乱,还有可能被其他应用程序拦截下来从而造成安全问题。Android为了解决广播的安全性问题,引入了一套本地广播机制。主要就是通过LocalBroadcastManager来对广播进行管理

接下来我们就要通过一个例子来学习本地广播的使用。在这个例子中我们首先想要发送一条"company.testbroadcast.LOCAL_BROADCAST"的广播,在testbroadcast项目中去接收到这条广播并弹出toast,同时在新建立的testforlocalbroadcast项目中也能接收到这条广播并弹出toast。这样表示我们发出的广播是能被其他应用程序接收到的。然后更改testbroadcast中代码使用本地广播,此时再观察testforlocalbroadcast是否能收到。

首先新建了testforlocalbroadcast项目,在其中实现如下代码。

package company.testlocalbroadcast;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private LocalBroadcastReceiver mLocalBroadcastReceiver;
    private IntentFilter mIntentFilter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction("company.testbroadcast.LOCAL_BROADCAST");
        mLocalBroadcastReceiver = new LocalBroadcastReceiver();
        registerReceiver(mLocalBroadcastReceiver, mIntentFilter);
    }

    private class LocalBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received local broadcast in 'company.testlocalbroadcast'", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mLocalBroadcastReceiver);
    }
}

然后修改testbroadcast项目,添加的代码后面都打了注释,我不想删除之前的功能示例可能导致代码有点乱,麻烦仔细看下定能理清。

package company.testbroadcast;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

/**
 * Created by samyang on 2016/8/29.
 */
public class MainActivity extends Activity {

    private IntentFilter mIntentFilter;
    private NetworkChangeReceiver mNetworkChangeReceiver;
    private IntentFilter mlocalIntentFilter;                              //重新声明IntentFilter和LocalBroadcastReceiver
    private LocalBroadcastReceiver mLocalBroadcastReceiver;

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

        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        mNetworkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(mNetworkChangeReceiver, mIntentFilter);

        mlocalIntentFilter = new IntentFilter();                         //添加发送"company.testbroadcast.LOCAL_BROADCAST"的Action并动态注册接收器
        mlocalIntentFilter.addAction("company.testbroadcast.LOCAL_BROADCAST");
        mLocalBroadcastReceiver = new LocalBroadcastReceiver();
        registerReceiver(mLocalBroadcastReceiver, mlocalIntentFilter);


        Button sendStandard = (Button) findViewById(R.id.send_standard_broadcast);
        sendStandard.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("company.testbroadcast.MY_STANDARD_BROADCAST");
                sendOrderedBroadcast(intent, null);
            }
        });

        Button sendLocal = (Button) findViewById(R.id.send_local_broadcast);        //添加按钮,实现点击逻辑
        sendLocal.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("company.testbroadcast.LOCAL_BROADCAST");
                sendBroadcast(intent);
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mNetworkChangeReceiver);
        unregisterReceiver(mLocalBroadcastReceiver);
    }

    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectivityManager.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();
            }
        }



    }

    private class LocalBroadcastReceiver extends BroadcastReceiver {         //新建LocalBroadcastReceiver内部类

        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received local broadcast in 'company.testbroadcast'", Toast.LENGTH_SHORT).show();
        }
    }
}

先后运行两个项目,然后切换到testbroadcast项目点击SEND LOCAL BROADCAST按钮可以得到如下图结果。


此时已经证明了我们发送的广播是能够被其他应用程序接收到的。然后我们修改testbroadcast项目,使用本地广播机制,来检验testlocalbroadcast是否还能接收到广播。

package company.testbroadcast;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

/**
 * Created by samyang on 2016/8/29.
 */
public class MainActivity extends Activity {

    private IntentFilter mIntentFilter;
    private NetworkChangeReceiver mNetworkChangeReceiver;
    private IntentFilter mlocalIntentFilter;
    private LocalBroadcastReceiver mLocalBroadcastReceiver;
    private LocalBroadcastManager localBroadcastManager;                //声明localBroadcastManager


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

        localBroadcastManager = LocalBroadcastManager.getInstance(this); //获取实例

        mIntentFilter = new IntentFilter();
        mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        mNetworkChangeReceiver = new NetworkChangeReceiver();
        registerReceiver(mNetworkChangeReceiver, mIntentFilter);


        Button sendStandard = (Button) findViewById(R.id.send_standard_broadcast);
        sendStandard.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("company.testbroadcast.MY_STANDARD_BROADCAST");
                sendOrderedBroadcast(intent, null);
            }
        });


        Button sendLocal = (Button) findViewById(R.id.send_local_broadcast);
        sendLocal.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent("company.testbroadcast.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent);            //通过Manager来发送广播

            }
        });

        mlocalIntentFilter = new IntentFilter();
        mlocalIntentFilter.addAction("company.testbroadcast.LOCAL_BROADCAST");
        mLocalBroadcastReceiver = new LocalBroadcastReceiver();
        localBroadcastManager.registerReceiver(mLocalBroadcastReceiver,mlocalIntentFilter);    //通过Manager来注册广播


    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mNetworkChangeReceiver);                                            //通过Manager来取消注册广播
        localBroadcastManager.unregisterReceiver(mLocalBroadcastReceiver);
    }

    private class NetworkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo networkInfo = connectivityManager.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();
            }
        }



    }

    private class LocalBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received local broadcast in 'company.testbroadcast'", Toast.LENGTH_SHORT).show();
        }
    }
}

本地广播主要是通过获取到一个localBroadcastManager实例然后通过该实例来进行发送广播、注册、取消注册等操作。(实现处已在上述代码中打了注释)

注意一点:刚才我在写好代码运行testbroadcast项目后,testlocalbroadcast仍能接收到我在testbroadcast中发出的本地广播。然后我将两个程序都卸载了重装,就正常了。无论如何点击SEND LOCAL BROADCAST按钮,都只会弹出"received local broadcast in 'company.testbroadcast'"这个toast。从而确认了本地广播的使用。



至此,Android四大组件中的Broadcast Receiver的常见用法已经介绍完毕了。


版权声明:本文为博主原创文章,未经博主允许不得转载。

安卓四大控件之BroadcastReceiver详解

BroadcastReceiver详解广播的概念Android:系统在产生某个事件时发送广播,应用程序使用广播接收者接收这个广播,就知道系统产生了什么事件。 Android系统在运行的过程中,会产生很...
  • qq_27280457
  • qq_27280457
  • 2016年07月06日 17:00
  • 18451

BroadcastReceiver使用完全解析

BroadcastReceiver使用完全解析
  • u010375364
  • u010375364
  • 2016年07月09日 10:37
  • 1746

Android静态安全检测 -> Broadcast Receiver组件暴露

Broadcast Receiver组件暴露 - exported属性 1. android:exported 该属性用来标示,当前Broadcast Receiver是否可以从当前应用外部获取Re...
  • u013107656
  • u013107656
  • 2016年07月12日 17:17
  • 2201

BroadcastReceiver生命周期探讨

前言之前做线控耳机连点两下切下一首歌的需求时, 曾经尝试在BroadcastReceiver中用一个成员变量保存最后一次按下的时间, 但后来发现这个值一直是初始值, 对它赋值后, 再次收到Intent...
  • oqqShaw123
  • oqqShaw123
  • 2015年09月10日 14:10
  • 2016

Android四大组件之BroadcastReceiver工作原理

1.广播的注册 As we all know,广播的注册也是分两种:动态注册和静态注册,前者是在Activity生命周期中用java代码注册和解除注册,后者是在AndroidManifest文件中。后...
  • tianmi1988
  • tianmi1988
  • 2016年03月20日 15:10
  • 972

Android开发学习之路--Broadcast Receiver之初体验

学习了Activity组件后,这里再学习下另一个组件Broadcast Receiver组件。这里学习下自定义的Broadcast Receiver。通过按键自己发送广播,然后自己接收广播。新建MyB...
  • eastmoon502136
  • eastmoon502136
  • 2016年02月13日 21:32
  • 3953

BroadcastReceiver使用完全解析

我们都知道Android四大组件,以前刚写博客的时候也写过其它组件,尽管写的不好,当做学习的笔记吧!比如[Android四大组件之Activity](http://blog.csdn.net/mr_d...
  • Mr_dsw
  • Mr_dsw
  • 2016年05月13日 12:59
  • 2654

android四大组件启动流程-BroadcastReceiver启动流程(基于android 6.0)

前面已经写过Activity的启动流程:http://blog.csdn.net/newhope1106/article/details/53355189 和 Service的启动流程:http:/...
  • newhope1106
  • newhope1106
  • 2017年01月07日 17:07
  • 1313

Android 源码系列之<三>从安全的角度深入理解BroadcastReceiver(下)

在上一篇文章中我们结合实验讲解了有关使用BroadcastReceiver存在的安全性问题并且给出了相应的解决方案,如果你还没有看过上篇文章请点击这里,最后一条的解决方案是采用官方v4包中的Local...
  • llew2011
  • llew2011
  • 2016年04月24日 13:21
  • 3741

Android中Intent详解(二)之使用Intent广播事件及Broadcast Receiver简介

通过第一篇的讲解,我们已经看到了如何使用Intent来启动新的应用程序组件,但是实际上他们也可以使用sendBroadcast方法来在组件间匿名的广播消息。 作为一个系统级别的消息传递机制,Inten...
  • u010358168
  • u010358168
  • 2014年10月31日 11:48
  • 2543
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android Broadcast Receiver 基础详解
举报原因:
原因补充:

(最多只允许输入30个字)