介绍
Android 应用可以发送或接收源于系统或其他App的广播,类似于发布—订阅模式。一般来说,广播主要用于应用内或跨应用的的通信。
注册方式
Context.register(动态注册)
1.创建BroadcastReceiver实例,代码如下:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
}
};
2.创建IntentFilter,并使用Context.registerReceiver(BroadcastReceiver,IntentFilter)注册,代码如下:
private void register(String action){
IntentFilter filter = new IntentFilter(action);
registerReceiver(broadcastReceiver,filter);
}
动态注册的BroadcastReceiver的生命周期取决于Context,如果是使用Activity Context注册,则Activity销毁时,广播也会销毁。如果使用Application的Context注册,则广播的生命周期与应用同步,应用销毁时,广播才会销毁。
3.销毁广播时,使用unRegister(BroadcastReceiver),代码如下:
unregisterReceiver(broadcastReceiver);
注:
1.如果在onCreate使用activity的context注册receiver,应该在onDestroy中移除注册。因为receiver持有activity的context引用,导致activity销毁时无法正常释放内存,从而造成内存泄漏(Memory Leak)。
2.如果在onResume中注册receiver,为避免多次注册,应该在onPause中移除注册。不要在onSaveInstanceState移除注册,因为这个方法在用户自然退出时,不会调用。
Manifest-Register(静态注册)
1.创建广播类,集成BroadcastReceiver,实现onReceiver方法:
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
System.out.println("系统开机");
}
}
2.在Manifest.xml中指定广播的action:
<receiver android:name=".BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
针对静态注册广播,系统会为收到的每一条广播创建一个新的BroadcastReceiver实例,该实例仅在onReceiver执行期间有效。当该方法结束时,系统会将之视为不可用,然后销毁。
goAsync()
BroadcastReceiver的状态(正在运行与否)会影响它所在的进程是否会被系统杀死。如,当进程正在执行receiver(也就是说在onReceive中正在执行代码)时,它会被视为前台进程。除非系统内存极度紧缺,否则系统会一直保持它的运行。
然而,一旦onReceive() 返回,BroadcastReceiver就不再活跃。receiver 的持有进程的重要性就和它所持有的其他组件的重要性一致了。如果进程仅持有一个Manifest-receiver(对于用户从未或最近没有使用的app来说很常见),当onReceive()返回时,系统会将该进程视为低优先级进程,并可能会为更重要的进程获取资源而将之杀死。
因此,不应该在broadcast receiver中启动长久运行的后台进程。onReceive()执行完毕后,系统会在任意时间杀死进程回收内存。于此同时,中止该进程中运行的线程。为避免这种情况,应该调用goAsync()(使得receiver返回时不会被中止,仍处于activie状态,从而保证进程优先级,为后台线程执行费时操作争取更多时间)或从receiver中使用JobScheduler安排JobService,以便系统了解到进程正在执行活跃的工作。可参考”进程和应用的生命周期”
用法如下:
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, final Intent intent) {
System.out.println("系统开机");
final PendingResult pendingResult = goAsync();
AsyncTask<String,Integer,String> asyncTask = new AsyncTask<String, Integer, String>() {
@Override
protected String doInBackground(String... params) {
StringBuilder sb = new StringBuilder();
sb.append("Action: " + intent.getAction() + "\n");
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
// Must call finish() so the BroadcastReceiver can be recycled.
pendingResult.finish();
return sb.toString();
}
};
asyncTask.execute();
}
}
广播处理完毕后,必须调用PendingResult.finish()方法。关于PendingResult.finish()方法,官方解释如下:
/**
* Finish the broadcast. The current result will be sent and the
* next broadcast will proceed.
*/
意思是结束广播,当前的结果将被发送然后处理下一条广播。测试发现,如果不调用finish
()方法,该receiver无法继续接收广播。结合上面说的goAsync()使得广播保持active状态,不被中止来看,当前广播没有被销毁。
广播安全
发送安全
1.指定权限:为避免应用内广播发送到应用外或应用接收到应用外的广播,可以给广播指定权限,只有特定权限的应用才能接收到广播。如:
sendBroadcast(new Intent("com.chaos.message"), Manifest.permission.SEND_SMS);
2.指定包名:setPackage(String)可以设定只有特定包名的应用才能接受广播。如:
Intent intent = new Intent("com.chaos.message");
intent.setPackage("com.chaos.test");
sendBroadcast(intent, Manifest.permission.SEND_SMS);
3.使用LocalBroadcastManager
接收安全
1.设定接收权限:
<receiver android:name=".BootReceiver"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
2.android:export = “false”,export设置为false,该receiver将不会接受应用外的广播。
3.使用LocalBroadcastManager