Android四大组件之Broadcast Receiver--学习过程中查询资料和书籍自我总结:
基本概念
广播是一个全局监听器,用于接收来自系统和其他APP的广播,每个APP都可以对自己感兴趣的广播进行注册,这样就可以接收到自己关心的广播了并做出对应的响应;
广播分类
- 标准广播:是一种完全异步执行的广播,在发出之后几乎所有程序同时接收到,没有先后顺序;这种广播的效率很高,但同时也是无法被拦截的;通过Context.sendBroadcast(Intent myIntent)发送;
-
有序广播:是一种同步执行的广播,在发出之后同一时刻只有一个广播接收器能接收到,当这个接收器的逻辑执行完之后,下一个接收器才会接收到,有先后顺序;优先级高的接收器先接收到(同级别接收顺序是随机的),并且前面的接收器还可以拦截广播(使用abortBroadcast()拦截),这样后边的接收器就接收不到这条广播了;通过Context.sendOrderedBroadcast(intent, receiverPermission)发送,第二个参数是广播的级别,级别在-1000-1000之间,值越大优先级越高,可通过intentfilter中的priority进行设置,设为2147483647时优先级最高,也可以通过IntentFilter的setPriority()方法设置;
注册广播
- 动态注册(在代码中注册):新建一个类A继承BroadcastReceiver类,重写onReceiver()方法,当接收到广播时onReceiver就会得到执行,在该方法中编写具体处理逻辑,但要将耗时的操作放到线程中;
//自定义NetWorkChangeReceiver继承BroadcastReceiver
class NetWorkChangeReceiver extends BroadcastReceiver {
//重写onReceiver方法,进行逻辑处理
public void onReceive(Context context, Intent intent) {
//通过getSystemService获取管理网络连接的类ConnectivityManager
ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
//获取当前网络状态信息类
NetworkInfo mNetworkInfo = manager.getActiveNetworkInfo();
if (mNetworkInfo != null && mNetworkInfo.isAvailable()) {
Toast.makeText(context, "network is available", Toast.LENGTH_SHORT).show();
} else
Toast.makeText(context, "network is unavailable", Toast.LENGTH_SHORT).show();
}
}
//在onCreate中注册
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mIntentFilter = new IntentFilter();
mIntentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
mchange = new NetWorkChangeReceiver();
//注册
registerReceiver(mchange, mIntentFilter);
}
//最后取消注册
protected void onDestroy() {
super.onDestroy();
//取消注册
unregisterReceiver(mchange);
}
- 静态注册(在AndroidMainfest.xml中注册)
AndroidManifest.xml中注册
<receiver
android:name=".MyReceivers"
android:enabled="true"//是否启用广播
android:exported="true">//是否允许接收本APP外的广播
<intent-filter android:priority="2147483647">
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
public class MyReceivers 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_SHORT).show();
throw new UnsupportedOperationException("Not yet implemented");
}
}
动态注册:可以自由的控制注册与注销,使用起来非常灵活,但必须是APP启动之后或者是注册广播的Activity打开后才有效;
静态注册: ,只要设备是开启状态,广播接收器也是打开着的。即使APP未打开接收器也是起作用的;
需要注意的是:广播接收器命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报ANR(Application No Response) 程序无响应的错误信息,如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由Service 来完成 . 这里不能使用子线程来解决 , 因为 BroadcastReceiver 的生命周期很短 , 子线程可能还没有结束BroadcastReceiver 就先结束了 .BroadcastReceiver 一旦结束 , 此时 BroadcastReceiver 的所在进程很容易在系统需要内存时被优先杀死 , 因为它属于空进程 ( 没有任何活动组件的进程 ). 如果它的宿主进程被杀死 , 那么正在工作的子线程也会被杀死 . 所以采用子线程来解决是不可靠的;
发送广播
- 发送标准广播:
//DynamicReceiver1,DynamicReceiver2动态注册,DynamicReceiver静态注册 public class DynamicReceiver1 extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String str = intent.getStringExtra("Dynamic");
abortBroadcast();Log.i("DynamicReceiver1", intent.getAction().toString() + " " + str); }}public class DynamicReceiver2 extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { String str = intent.getStringExtra("Dynamic");
abortBroadcast();Log.i("DynamicReceiver2", intent.getAction().toString() + " " + str); }}public class DynamicReceiver2 extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { String str = intent.getStringExtra("Dynamic");
Log.i("DynamicReceiver2", intent.getAction().toString() + " " + str); }}abortBroadcast();
//button发送 Intent mDynamic = new Intent("com.units.jonney.myunits.DYNAMIC_BROADCAST"); mDynamic.putExtra("Dynamic", "send"); sendBroadcast(mDynamic); //注册
protected void onResume() { super.onResume(); registerTestReceiver(); }
private void registerTestReceiver() { r1 = new DynamicReceiver1(); r2 = new DynamicReceiver2(); r3 = new DynamicReceiver3(); IntentFilter mintent = new IntentFilter(); mintent.addAction("com.units.jonney.myunits.DYNAMIC_BROADCAST"); registerReceiver(r1, mintent); registerReceiver(r2, mintent); registerReceiver(r3, mintent); }
<receiver android:name=".DynamicReceiver3" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.units.jonney.myunits.DYNAMIC_BROADCAST"></action> </intent-filter> </receiver> 结果:
标准广播中动态注册的的接收器会比静态注册的接收器先接收到;08-26 12:22:29.077 15491-15491/com.units.jonney.myunits I/DynamicReceiver1: com.units.jonney.myunits.DYNAMIC_BROADCAST send 08-26 12:22:29.082 15491-15491/com.units.jonney.myunits I/DynamicReceiver2: com.units.jonney.myunits.DYNAMIC_BROADCAST send 08-26 12:22:29.082 15491-15491/com.units.jonney.myunits I/DynamicReceiver3: com.units.jonney.myunits.DYNAMIC_BROADCAST send
- 发送有序广播:
将上述发送广播的方法由sendBroadcast改为sendOrderBroadcast;
Intent mDynamic1 = new Intent("com.units.jonney.myunits.DYNAMIC_BROADCAST"); mDynamic1.putExtra("Dynamic", "send"); //第二个参数表示如果要接收这个广播要指定权限,处于安全考虑;如果为空则不需要指定权限 sendOrderedBroadcast(mDynamic1, "android.permission.MY_BROADCAST_PERMISSION");
在AndroidManifest.xml中注册和使用权限,并注册另外两个接收器并制定priority属性 <permission android:name="android.permission.MY_BROADCAST_PERMISSION" android:protectionLevel="normal" />
<uses-permission android:name="android.permission.MY_BROADCAST_PERMISSION" />
在DynamicReceiver1中添加abortBroadcast后:<receiver android:name=".DynamicReceiver1" android:enabled="true" android:exported="true"> <intent-filter android:priority="1000"> <action android:name="com.units.jonney.myunits.DYNAMIC_BROADCAST"></action> </intent-filter> </receiver> <receiver android:name=".DynamicReceiver2" android:enabled="true" android:exported="true"> <intent-filter android:priority="900"> <action android:name="com.units.jonney.myunits.DYNAMIC_BROADCAST"></action> </intent-filter> </receiver>
DynamicReceiver1: com.units.jonney.myunits.DYNAMIC_BROADCAST send
在DynamicReceiver1中添加abortBroadcast后:
DynamicReceiver1: com.units.jonney.myunits.DYNAMIC_BROADCAST send
DynamicReceiver2: com.units.jonney.myunits.DYNAMIC_BROADCAST send
- 开机广播:
//接收器 public class RootCompleteBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "RootCompleteReceiver"; @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "接收到开机启动广播", Toast.LENGTH_SHORT).show(); Log.i(TAG, "接收到开机启动广播"); } }
<receiver android:name=".RootCompleteBroadcastReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter> </receiver><category android:name="android.intent.category.DEFAULT" />
不要忘记申请权限 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
- 电池状态
public class BatteryStateReceiver extends BroadcastReceiver { private static final String TAG = "BatteryStateReceiver"; public BatteryStateReceiver() { } @Override public void onReceive(Context context, Intent intent) { int current = intent.getExtras().getInt("level"); int total = intent.getExtras().getInt("scale"); float percent = (current / total) * 100; Toast.makeText(context, "当前电量:" + percent + "%", Toast.LENGTH_SHORT).show(); Log.i(TAG, "当前电量:" + percent + "%"); } }
<receiver android:name=".BatteryStateReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BATTERY_CHANGED" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </receiver>
一般电池状态监控使用动态注册即可;如果要立即获取当前的电量可以自己手动发送一条电量变化的广播来获取;<uses-permission android:name="android.permission.BATTERY_STATS" />
- 网络监控
public class NetWorkChangeReceiver extends BroadcastReceiver { private static final String TAG = "NetWorkChangeReceiver"; @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "网络发生改变", Toast.LENGTH_SHORT).show(); Log.i(TAG, "网络发生改变"); ConnectivityManager mconn = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mInfo = mconn.getActiveNetworkInfo(); if (mInfo != null && mInfo.isAvailable()) { Toast.makeText(context, "当前网络可用", Toast.LENGTH_SHORT).show(); Log.i(TAG, "当前网络可用"); } else { Toast.makeText(context, "当前网络不可用", Toast.LENGTH_SHORT).show(); Log.i(TAG, "当前网络不可用"); } }
<receiver android:name=".NetWorkChangeReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<category android:name="android.intent.category.DEFAULT" /></intent-filter> </receiver>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />