Android入门笔记(广播)

Android 广播是一种可 跨进程 的通信方式,系统中的每个程序都可以对自己感兴趣的广播进行注册,注册过后的程序只会接受到自己所关心的广播内容,这些广播可以来自系统,也可以来自系统里的各个程序。
Android 的广播可以分为两类,分别是

  • 标准广播
  • 有序广播

标准广播 是一种完全异步执行的广播,所有广播接收器都几乎同一时刻接收到该广播,这种广播的效率最高,但同时也就意味着它无法被拦截,如下图:
在这里插入图片描述
有序广播 是一种同步操作的广播,同一时刻只有一个广播接收器接收到广播,当这个广播接收器的处理逻辑执行完之后,广播才会继续传递,即此时的所有的广播接收器接收广播是有先后顺序的,优先级高的广播接收器可以优先接收到广播,同时也可以把广播拦截下来,如下图:
在这里插入图片描述

1 系统广播

Android 系统内置了很多系统级别的广播,应用程序可以通过监听这些广播来得到系统的状态信息。(比如系统开机会发送一条广播,断网会发送一条广播)

1.1 广播接收器

广播接收器可以自由地对自己感兴趣的广播进行注册,这样当相应的广播发送时,广播接收器就能够接收到该广播,并在内部处理相应的逻辑。
广播的注册方式有两种:

  • 动态注册:在代码中进行注册
  • 静态注册:在 AndroidMainfest.xml 文件中注册

而相应的逻辑处理就需要创建一个继承自 BroadCastReceiver,具体的逻辑就是在重写父类的 onReceive 方法里面。

注意:在 onReceive 方法中不要添加过多的逻辑或耗时的操作,因为接收器中不允许开启线程,当 onReceive 方法运行较长时间而没有结束时,程序就会报错。
因此,广播接收器更多的是在扮演一种打开程序其他组件的角色

1.1.1 动态注册

下面是动态注册的代码,其中接收器的逻辑也作为内部类声明了:

  • 首先创建 IntentFilter 实例,并调用 addAction 方法选择哪个广播注册
  • 创建对应接收器的实例
  • 通过 registerReceiver 方法进行正式的注册
  • 并且在活动生命周期的注销回调函数中,对广播进行注销

下面代码主要是对网络连接状态的改变进行注册广播:

public class MainActivity extends AppCompatActivity {

    private IntentFilter intentFilter;

    private NewWorkChangeReceiver newWorkChangeReceiver;

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

        // 创建 IntentFilter,作用是筛选某个广播
        intentFilter = new IntentFilter();
        // 添加 网络状态变化 广播
        intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        // 注册广播
        newWorkChangeReceiver = new NewWorkChangeReceiver();
        registerReceiver(newWorkChangeReceiver, intentFilter);
    }

    /**
     * 活动销毁时要注销广播接收器
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(newWorkChangeReceiver);
    }
 
	/**
     * 网络状态改变广播接收器
     * 接收器类
	 * 继承 BroadcastReceiver 类,并重写 OnReceive 方法
     */
    class NewWorkChangeReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
    
    		
            ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    
            // 判断安卓版本
            // 新版使用 getAllNetworks
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                Network[] networks = connectivityManager.getAllNetworks();
                NetworkInfo networkInfo;
                for (Network mNetwork : networks) {
                    networkInfo = connectivityManager.getNetworkInfo(mNetwork);
                    if (networkInfo.getState().equals(NetworkInfo.State.CONNECTED)) {
                        Toast.makeText(context, "网络可用", Toast.LENGTH_SHORT).show();
                        return;
                    }
                }
            } else {
                //否则调用旧版本方法
                if (connectivityManager != null) {
                    NetworkInfo[] info = connectivityManager.getAllNetworkInfo();
                    if (info != null) {
                        for (NetworkInfo anInfo : info) {
                            if (anInfo.getState() == NetworkInfo.State.CONNECTED) {
                                Toast.makeText(context, "网络可用", Toast.LENGTH_SHORT).show();
                                return;
                            }
                        }
                    }
                }
            }
            Toast.makeText(context, "网络不可用", Toast.LENGTH_SHORT).show();
        }
    }
}

同时,Android 系统为了用户设备的安全和隐私,有严格的规定,如果程序需要进行一些对用户来说比较敏感的操作,就必须在配置文件中声明权限,如:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="top.seiei.aboutbroadcast">
	
	<!-- 用户权限 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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>
    </application>

</manifest>

注:如果只需保证只有处于栈顶的活动才能接收到广播,而非栈顶的活动不接收到广播,那么做法就可以是创建一个活动基类,在该活动基类中的 onResumeonPause 方法中添加注册和注销广播的逻辑

1.1.2 静态注册

动态注册的广播接收器可以自由地控制注册和注销,但 必须在程序启动后才能接收到广播,想要程序还没启动就能接收到广播就需要 静态注册 了。
此时可以借助 Android Studio 的快捷方式,实现快速的静态注册,右键左侧项目放置对应接收器的包名,点击 New -> Other -> Broadcast Receiver,此时会出现下面的对话框:
在这里插入图片描述

当中有两个勾选了的属性,分别是:

  • Exported:是否允许该接收器接收 本程序以外 的广播
  • Enabled:是否启动该接收器

通过上述方法创建后,再打开 AndroidManifest.xml,会发现 application 标签下多了以下标签:

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

此时还要说明该接收器用于接收哪些广播,所以修改后如下:

<receiver
    android:name=".receivers.BootCompleteReceiver"
    android:enabled="true"
    android:exported="true">
    <!-- 接收哪些广播 -->
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"></action>
    </intent-filter>
</receiver>

以上的代码表示该广播接收器接收设备启动时的广播,注意,监听系统开机广播也是需要声明权限的,所以还需要在 AndroidManifest.xml 文件中添加以下代码:

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

2 自定义广播

上面介绍的都是如何接收系统广播,而接收自定义广播的操作也是类似的,那么自定义广播是如何创建和发送的。
这次使用到的还是 Intent 对象,步骤如下:

  • 首先特有的广播名传入 Intent 类初始化 Intent,此时可以添加一些数据进行传递
  • 调用 ContextsendBroadcast 方法进行发送,此时所有注册了该广播的接收器都可以接收到该广播
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent("top.seiei.MyFirstBroadcast");
        intent.putExtra("txt", "传递过来的数据");
        sendBroadcast(intent);
    }
});

2.1 有序广播

上面开头就有讲到广播类型可以分为两种,标准广播和有序广播。而使用 sendBroadcast 方式发送的广播就是标准广播,而想要发送有序广播也很简单,就是使用 sendOrderedBroadcast 方法来代替 sendBroadcast 方法发送广播,sendOrderedBroadcast 方法接收两个参数,第一个参数是 Intent,而第二个参数就是一个与权限相关的字符串,可以传入 null,如:

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Intent intent = new Intent("top.seiei.MyFirstBroadcast");
        intent.putExtra("txt", "传递过来的数据");
        sendOrderedBroadcast(intent, null);
    }
});

在静态注册中,可以使用 android:priority 属性给广播接收器设置优先级,优先级比较高的广播接收器就先收到广播,如下代码将 TestReceiver 接收器的优先级设置为 100

<receiver
    android:name=".receivers.TestReceiver"
    android:enabled="true"
    android:exported="true">
    <!-- 接收哪些广播 -->
    <intent-filter android:priority="100">
        <action android:name="top.seiei.TestBroadcast"></action>
    </intent-filter>
</receiver>

而在接收器逻辑中使用 abortBroadcast 方法可以 截断 这个广播的继续传递。

3 本地广播

上述所讲的广播都属于 系统全局广播,即发出的广播可以被其他任何程序接收到,这容易产生安全性问题,比如广播可能被其他程序截获,其他程序不停地发送垃圾广播等。
本地广播 就是用来解决这个问题,使用这个机制发出的广播只能在应用程序内部中进行传递,并且广播接收器也只能接收来自本应用程序发出的广播,同时发送本地广播比发送系统全局广播更高效。使用本地广播主要就是使用 LocalBroadcastManager 来对广播进行管理,例子代码如下:

public class MainActivity extends AppCompatActivity {

    private IntentFilter intentFilter;

	private LocalBroadcastManager localBroadcastManager;

    private LocalReceiver localReceiver;

    @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 View.OnClickListener() {
			@Override
			public void onClick(View v) {
				Intent intent = new Intent("top.seiei.broadcasttest.LOCAL_BROADCAST");
				// 本地广播管理实例发送广播
				localBroadcastManager.sendBroadcast(intent);
			}
		});

        // 创建 IntentFilter,作用是筛选某个广播
        intentFilter = new IntentFilter();
        intentFilter.addAction("top.seiei.broadcasttest.LOCAL_BROADCAST");
        // 注册本地广播接收器
        localReceiver= new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);
    }

    /**
     * 活动销毁时要注销广播接收器
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销广播接收器
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
 
	/**
     * 接收器类
	 * 继承 BroadcastReceiver 类,并重写 OnReceive 方法
     */
    class LocalBroadcastManager extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
    		Toast.makeText(context, "接收到本地广播", Toast.LENGTH_SHORT).show();
        }
    }
}

注:本地广播无法通过静态注册的方式来接收,这也很好理解,因为 静态注册主要就是为了让程序在未启动的情况下也能接收到广播,而发送本地广播的时候,该程序肯定是已经启动了,所以也就无需使用静态注册的功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值