第二十九章 broadcast intent
有关broadcast intent
broadcast intent的工作原理与intent相似,只不过broadcast intent可以同时被多个broadcast receiver组件接受。
broadcast receiver种类
standalone receiver
standalone receiver是一个在manifest配饰文件中声明的broadcast receiver,即便应用消亡,standalone receiver也可以被激活。
dynamic receiver
dynamic receiver同fragment或activity的生命周期绑定,其不在manifest中声明,需要自己定义。
系统broadcast:重启后唤醒
只要打开设备,系统就会发送一个带有BOOT_COMPLETED操作的broadcast。想要监听它,需要创建并且登记一个standalone receiver,需要在manifest文件中登记它:
<receiver android:name=".StartupReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
每当有匹配的broadcast intent(例如带有BOOT_COMPLETE操作的)发送过来,broadcast receiver就会醒来接收。被唤醒后的receiver会调用自己的onReceive接口来执行命令,执行完毕后这个receiver就会消亡,所以我们需要将操作写在这个方法里面:
public class StartupReceiver extends BroadcastReceiver {
private static final String TAG="StartupReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG,"Received boardcast intent: "+intent.getAction());
boolean isOn=QueryPreferences.isPreAlarmOn(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
PollJobService.setServiceAlarm(context,isOn);
}
else
{
PollService.setServiceAlarm(context,isOn);
}
}
}
过滤前台通知消息
翻译翻译,就是说应用开着时不能干的那些事。显然这些事情是伴随着应用的使用时期而被禁止的,那么需要用的receiver就不能是standalone的了。使用dynamic receiver,在应用被打开时启用,在应用被关闭时弃用。
在最开始,要先定义receiver要接收的信息(之前是接收操作系统发送的操作)。这个消息表示应用在服务中已经找到了新的搜索结果了,并且想要去将这个通知发送给用户。
sendOrderedBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));
这个ACTION_SHOW_NOTIFICATION是之后创建的一个receiver(剧透一下就是NotificationReceiver),这个receiver就是用来发送通知的。
之后的第一步就是创建并登记dynamic receiver。由于dynamic receiver是需要我们自己创建的,我们需要在应用在前台时取消通知,那么对应的这个receiver的创建时机就是生命周期中的启动时间,如onStart()或onCreate()。这个receiver需要我们自己手动清理,一般是在创建时机对应的生命周期清理,如onStop()和onDestroy()。
书上的做法是写一个fragment抽象类,在其中创建receiver让我们的PhotoGalleryFragment继承这个抽象类。需要注意的是,这个receiver虽然是代码创建的,但是其要素和manifest中的一样多。例如要手动代码写一个intentFilter:
public abstract class VisibleFragment extends Fragment { private static final String TAG="VISVBLEFragment"; @Override public void onStart() { super.onStart(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { IntentFilter filter=new IntentFilter(PollJobService.ACTION_SHOW_NOTIFICATION); getActivity().registerReceiver(mOnShowNotification,filter); } else { IntentFilter filter=new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION); getActivity().registerReceiver(mOnShowNotification,filter); } } @Override public void onStop() { super.onStop(); getActivity().unregisterReceiver(mOnShowNotification); } private BroadcastReceiver mOnShowNotification=new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { }; }
发送带有权限的broadcast
有的receiver,只想让它在这一个应用中被监听且触发,可以让其加上android:exported=”false”,书中还介绍了另一个做法,就是发送一个具有私有权限的broadcast,并且让那个receiver只接受那个权限的broadcast。
发送带有权限的broadcast很简单,只需要发送权限时加上权限就可:
public static final String PREM_PRIVATE="com.shopkeeper.photogallery.PRIVATE"; sendOrderedBroadcast(new Intent(ACTION_SHOW_NOTIFICATION),PREM_PRIVATE);
私有的权限也是在manifest中定义的:
<permission android:name="com.shopkeeper.photogallery.PRIVATE" android:protectionLevel="signature"/> ... <uses-permission android:name="com.shopkeeper.photogallery.PRIVATE"/>
android自定义permission android:protectionLevel说明
然后让receiver只接受具有那个权限的broadcast:
public abstract class VisibleFragment extends Fragment {
private static final String TAG="VISVBLEFragment";
@Override
public void onStart() {
super.onStart();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
IntentFilter filter=new IntentFilter(PollJobService.ACTION_SHOW_NOTIFICATION);
getActivity().registerReceiver(mOnShowNotification,filter,PollJobService.PREM_PRIVATE,null);
}
else
{
IntentFilter filter=new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);
getActivity().registerReceiver(mOnShowNotification,filter,PollService.PREM_PRIVATE,null);
}
}
@Override
public void onStop() {
super.onStop();
getActivity().unregisterReceiver(mOnShowNotification);
}
private BroadcastReceiver mOnShowNotification=new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(getActivity(),"Got a broadcast: "+intent.getAction(),Toast.LENGTH_LONG).show();
Log.i(TAG,"canceling notification");
setResultCode(Activity.RESULT_CANCELED);
}
};
}
使用有序broadcast收发数据
最后要解决的问题是,怎么样才能最终确定到底该不该发送通知。怎么把取消发送通知的消息传递给负责发送通知的模块、我现在已经有了取消发送通知的receiver了,但是如果我早就把通知发送出去了怎么办。
有序的broadcast,它可以设置返回值给其对应的result receiver(就是之前剧透的NotificationReceiver)。其实我觉得不应该单独叫它result receiver,因为好像所有的receiver都是共用的一个ResultCode,接下来会发现可以在先前的dynamic receiver中调用setResultCode方法,然后发送broadcast是可以设置resultCode,然后在所谓的result receiver中可以调用getResultCode。但是书上是这么叫的。
发送有序的broadcast的方法是
sendOrderedBroadcast(i,PREM_PRIVATE,null,null, Activity.RESULT_OK,null,null);
后面的五个参数是:一个result receiver、一个支持result receiver运行的handler、结果代码的初始值、结果数据以及有序broadcast附加的bundle。
为了解决这些receiver被唤醒的先后顺序的问题,可以设置优先值,并且禁止让其它应用使用:
manifest
<receiver android:name=".NotificationReceiver" android:exported="false"> <intent-filter android:priority="-999"> <action android:name="com.shopkeeper.photogallery.SHOW_NOTIFICATION"/> </intent-filter> </receiver>
result receiver
public class NotificationReceiver extends BroadcastReceiver {
private static final String TAG="NotificationReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG,"receive result: "+getResultCode());
if (getResultCode()!= Activity.RESULT_OK)
{
return;
}
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP)
{
int requestCode= intent.getIntExtra(PollJobService.REQUSET_CODE,0);
Notification notification=(Notification)intent.getParcelableExtra(PollJobService.NOTIFICATION);
NotificationManagerCompat notificationManagerCompat=NotificationManagerCompat.from(context);
notificationManagerCompat.notify(requestCode,notification);
}
else
{
int requestCode= intent.getIntExtra(PollService.REQUSET_CODE,0);
Notification notification=(Notification)intent.getParcelableExtra(PollService.NOTIFICATION);
NotificationManagerCompat notificationManagerCompat=NotificationManagerCompat.from(context);
notificationManagerCompat.notify(requestCode,notification);
}
}
}