一、前言
Android内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。
比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播,等等。如果想要接收到这些广播,就需要使用广播接收器。
二、动态注册监听网络变化
广播接收器可以自由地对自己感兴趣的广播进行注册,这样当有相应的广播发出时,广播接收器就能够收到该广播,并在内部处理相应的逻辑。
注册广播的方式一般有两种,在代码中注册和在AndroidManifest.xml中注册,其中前者也被称为动态注册,后者也被称为静态注册。
2.1、动态注册
新建一个BroadcastTest项目,修改MainActivity代码:
package com.example.broadcasttest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private IntentFilter intentFilter;
private NetworkChangeReceiver networkChangeReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intentFilter = new IntentFilter();
intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver , intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(networkChangeReceiver);
}
class NetworkChangeReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context , "newwork changes" , Toast.LENGTH_SHORT).show();
}
}
}
在MainActivity中定义了一个内部NetworkChangeReceiver,这个类是继承自BroadcastReceiver的,并重写了父类的onReceive()方法。
这样每当网络状态发生变化时,onReceive( )方法就会得到执行,这里只是简单地使用Toast 提示了一段文本信息。
在onCreate()方法中,首先我们创建了一个IntentFilter的实例,并给它添加了一个值为android.net.conn. CONNECTIVITY_ CHANGE 的action,为什么要添加这个值呢?
因为当网络状态发生变化时,系统发出的正是一条值为 android.net.conn.CONNECTIVITY_ CHANGE 的广播,也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的action。
接下来创建了一个NetworkChangeReceiver 的实例,然后调用registerReceiver()方 法进行注册,将.
NetworkChangeReceiver的实例和IntentFilter的实例都传了进去, 这样NetworkChangeReceiver就会收到所有值为android.net.conn.CONNECTIVITY_ CHANGE 的广播,也就实现了监听网络变化的功能。
动态注册的广播接收器一定都要取消注册才行,这里我们是在onDestroy()方法中通过调用unregisterReceiver()方法来实现的。.
2.2、运行程序
首先你会在注册完成的时候收到一条广播,然后按下Home键回到主界面(注意不能按Back键,否则onDestroy()方法会执行),接着手动打开或关闭网络,就会看到有Toast提醒网络发生了变化。
2.3、功能优化
只是提醒网络发生了变化还不够人性化,最好是能准确地告诉用户当前是有网络还是没有网络,因此我们还需要对上面的代码进行进一步的优化。
修改MainActivity中的部分代码,如下所示:
............................
@Override
public void onReceive(Context context, Intent intent) {
//Toast.makeText(context , "network changes" , Toast.LENGTH_SHORT).show();
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();
}
}
..............................
在onReceive()方法中,首先通过getSystemService()方法得到了ConnectivityManager的实例,这是一个系统服务类,专门用于管理网络连接的。然后调用它的getActiveNetworkInfo()方法可以得到NetworkInfo的实例,接着调用NetworkInfo的isAvailable()方法,就可以判断出当前是否有网络了,最后我们还是通过Toast的方式对用户进行提示。
另外,这里有非常重要的一点需要说明,Android 系统为了保护用户设备的安全和隐私,做了严格的规定:如果程序需要进行一些对用户来说比较敏感的操作,就必须在配置文件中声明权限才可以,否则程序将会直接崩溃。比如这里访问系统的网络状态就是需要声明权限的。
打开AndroidManifest.xml文件,在里面加入如下权限就可以访问系统网络状态了:
.....................
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
.....................
效果:
三、静态注册实现开机启动
3.1、前言
动态注册的广播接收器可以自由地控制注册与注销,在灵活性方面有很大的优势,但是它也存在着一个缺点,即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写onCreate()
方法中的。
那么有没有什么办法可以让程序在未启动的情况下就能接收到广播呢?这就需要使用静态注册的方式了。
3.2、创建一个广播
这里准备让程序接收一条开机广 播,当收到这条广播时就可以在onReceive()方法里执行相应的逻辑,从而实现开机启动的功能。可以使用Android Studio提供的快捷方式来创建一个广播接收器,右击com.example.broadcasttest 包→New-→Other-→Broadcast Receiver,会弹出如下窗口:
3.3、修改BootCompleteReceiver代码如下
package com.example.broadcasttest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
public class BootCompleteReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context , "Boot Complete" , Toast.LENGTH_LONG).show();
}
}
3.4、在AndroidManifest.xml中设置相应权限
另外,静态的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用,不过由于使用Android Studio 的快捷方式创建的广播接收器,因此注册这一步已经被自动完成了。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcasttest">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<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/Theme.BroadcastTest">
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
可以看到,标签内 出现了一个新的标签,所有静态的广播接收器都是在这里进行注册的。
它的用法其实和标签非常相似,也是通过android : name来指定具体注册哪一个广 播接收器,而enabled和exported属性则是根据我们]刚才勾选的状态自动生成的。
由于Android系统启动完成后会发出一-条值为android.intent . action.BOOT_COMPLETED的广播,因此我们在<intent - filter>标签里添加了相应的action。
另外,监听系统开机广播也是需要声明权限的,,于是使用<uses - permission>标签又加人了一条android.
permission.RECEIVE_ BOOT_ COMPLETED 权限。
3.5、启动程序
重新运行程序后,我们的程序就已经可以接收开机广播了。将模拟器关闭并重新启动,在启动完成之后就会收到开机广播,如下所示。