Broadcasts
Android应用程序可以发送或接收来自Android系统和其他Android应用的广播消息,类似于发布-订阅设计模式。当发生感兴趣的事件时,这些广播就会被发送。例如,Android系统在发生各种系统事件时发送广播,比如系统启动或设备开始充电时。应用还可以发送定制的广播,例如,通知其他应用程序可能感兴趣的东西(例如,一些新数据已经被下载)。应用程序可以注册接收特定的广播。当广播被发送时,系统会自动将广播发送给那些订阅了特定类型的广播的应用。
System broadcasts
当系统发生各种系统事件时,系统会自动发送广播,例如当系统切换到飞机模式时。系统广播被发送到所有订阅该事件的应用程序。发送的广播消息采用Intent进行包裹,其中可以包括,action、extra等信息。详情见Intent。
备注:Android7.0及以上系统版本将不会再发送ACTION_NEW_PICTURE、ACTION_NEW_VIDEO广播。同时,在清单文件中注册CONNECTIVITY_ACTION广播接收者是无效的。从Android8.0(API级别26)开始,系统对已声明的广播接收者施加了额外的限制。如果你的应用程序的目标是API级别的26或更高,你就不能使用manifest来为大多数隐式广播(不是专门针对你的应用的广播)声明一个广播接收者。
Receiving broadcasts
通过注册广播接收者可以接收广播,注册广播接收者有两种方式:在清单文件中注册和Context—registered。。
清单文件注册广播接受者:当安装应用程序时,系统包管理器会注册广播接收者。然后,接收者就会成为你的应用的一个独立入口点,这意味着如果该应用目前还没有运行,系统就可以启动应用并发布广播。
代码示例:
<receiver android:name=".MyBroadcastReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED"/>
</intent-filter>
</receiver>
public class MyBroadcastReceiver extends BroadcastReceiver {
private staticfinal String TAG= "MyBroadcastReceiver";
@Override
public void onReceive(Context context,Intent intent){
StringBuilder sb = new StringBuilder();
sb.append("Action: "+ intent.getAction()+ "\n");
sb.append("URI: "+ intent.toUri(Intent.URI_INTENT_SCHEME).toString()+ "\n");
String log = sb.toString();
Log.d(TAG, log);
Toast.makeText(context, log,Toast.LENGTH_LONG).show();
}
}
Context-registered: Context-registered的接收者可以接收广播,只要它们的注册Context是有效的。例如,如果您在Activity中注册接收者,那么只要该Activity没有被Destory,您就可以接收广播。如果您在Application Context中注册,那么只要应用程序在运行,就会接收到广播。
代码示例:
BroadcastReceiver br = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
this.registerReceiver(br, filter);
确保,当您不再需要接收该广播或者是Context不再有效时,调用unregisterReceiver(android.content.BroadcastReceiver)方法停止接收广播。
备注:register 和unregister广播接收者要成对出现,如果你在onCreate()中注册,那你在onDestory()中取消注册。在onResume()中注册在onPause()中取消注册(以防止多次注册)。但是,不要再onSaveInstanceState(Bundle)方法中取消注册,因为它不一定执行。
Sending broadcast方式:发送有序广播sendOrderedBroadcast(Intent,String),广播接收者依次接收到该广播,接收该广播的优先顺序取决于定义的android:priority,同时,广播接收者在接到广播的时候可以终止广播的向下继续传播或者是对广播数据进行封装下发。当接收者的优先级相同时,它们接收到广播的顺序是随机的。发送无序广播sendBroadcast(Intent) ,以未定义的顺序向所有接收者发送广播。这就是所谓的正常广播。这是更有效的方法,但这意味着接收者不能读取其他接收者的结果,传播从广播中接收的数据,或者中止广播。发送本地广播LocalBroadcastManager.sendBroadcast,发送和接收广播只在该APP中,不用担心安全问题以及不用进行跨进程通信。
Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice me senpai!");
sendBroadcast(intent);
Effects on process state
当BroadcastRecerver在执行onRecerver()方法时,应用程序是一个前台进程,当onRecerver()执行完时,并且,应用程序只在清单文件中定义了BroadcastRecerver,该应用程序的优先级就会降低。在内存比较低的情况下,该应用程序有可能被杀死。因此,在onRecerver()方法中,不能定义长时间在后台运行的线程,在onRecerver()方法返回时,内存低的情况下,有可能被杀死。为了避免这种情况的发生,你可以调用goAsync()方法(如果你的应用程序需要更多的时间执行后台线程或者是执行JobService使用JobScheduler),因此,系统知道应用程序在持续工作。
public class MyBroadcastReceiver extends BroadcastReceiver {
private staticfinal String TAG= "MyBroadcastReceiver";
@Override
public void onReceive(finalContext context,final Intent intent){
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");
Log.d(TAG, log);
// Must call finish() so theBroadcastReceiver can be recycled.
pendingResult.finish();
return data;
}
};
asyncTask.execute();
}
}
Security considerations and best practices
1、如果您不需要向应用程序之外的组件发送广播,那么就可以使用本地广播的LocalBroadcastManager发送和接收本地广播。LocalBroadcastManager的效率更高(不需要进程间通信),并且允许您避免考虑任何与其他应用程序相关的安全问题,这些应用程序可以接收或发送您的广播。在你的应用中,本地广播可以作为通用的发布/订阅事件总线,而不需要任何系统范围的广播。
2、有些广播只能采用Context-Register的注册方式,不能采用在清单文件中的定义方式。因为如果许多应用在它们的清单上注册了相同的广播,它可能会导致系统启动大量的应用程序,从而对设备性能和用户体验产生重大影响。例如,CONNECTIVITY_ACTION广播只传递给Context-Register的接收者。
3、不要使用隐式的意图来传播敏感信息。任何注册接收广播的应用程序都可以读取这些信息。有三种方法可以控制谁可以接收你的广播:
1、发送广播时定义权限。
2、采用setPackage(String)指定广播发送给指定包名的应用程序。
3、发送本地广播。
发送权限广播:
发送广播时,定义权限,只有那些在他们的清单中要求获得许可的接收者(并且危险权限要得到用户许可)才可以接受广播。例如,以下代码发送一个广播:
sendBroadcast(newIntent("com.example.NOTIFY"),
Manifest.permission.SEND_SMS);
要接收广播,接收者应用程序必须请求许可,如下所示:
<uses-permission android:name="android.permission.SEND_SMS"/>
您可以指定一个现有的系统权限,比如SEND_SMS,或者使用元素的作用定义一个定制的权限。
备注::在安装应用程序时,会注册自定义权限。定义自定义权限的应用程序必须安装在使用它的应用程序之前。
当你注册一个广播接收者时,任何应用都可以向你的应用程序接收者发送潜在的恶意广播。有三种方式限制你的应用程序接收到的广播:
1、在定义广播接收者的时候,可以定义权限。
2、在清单文件中定义接收者时,android:export属性设置为false,可以阻止其接收到其他应用发送的广播。
3、也可以定义本地广播LocalBroadcastManager。
接收权限广播:
如果您在注册广播的时候指定一个权限 (要么registerReceiver(BroadcastReceiver,IntentFilter、接收者)或在<receiver>标记在你的清单),那么只有广播请求许可的< uses-permission >标记的清单(如果权限是危险的需要用户授予权限)发送的才可以收到。
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.SEND_SMS">
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE"/>
</intent-filter>
</receiver>
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter,Manifest.permission.SEND_SMS,null );
<uses-permission android:name="android.permission.SEND_SMS"/>