Android

Java安卓学习总结(三十三)

第二十九章 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);
}
}
}

发表评论