• 安卓权威编程指南-笔记(第27章 broadcast intent)


    本章需求:首先,让应用轮询新结果并在有所发现时及时通知用户,即使用户重启设备后还没有打开过应用。其次,保证用户在使用应用时不出现新结果通知。

    1. 一般intent和broadcast intent

      许多系统组件需要知道某些事件的发生(WIFI信号时有时无,电话的呼入等),为满足这样的需求,Andorid提供了broadcast intent 组件。

      broadcast intent的工作原理类似于之前学过的intent,但不同的是broadcast intent可以被多个叫做broadcast receiver的组件接收、

    2. 接受系统broadcast : 重启后唤醒

    2.1 standalone receiver

      standalone receiver 是一个在manifest配置文件中声明的broadcast receiver。即使应用进程已消灭,standalone receiver也可以被激活。(还有一种是可以同fragemt或activity的生命周期绑定的dynamic receiver)

      broadcast receiver必须在系统中登记后才能发挥作用,如果不登记,系统就不知道该向哪里发送intent,broadcast receiver的onReceiver()方法也就得不到预定的调用了。

         要登记broad receiver,首先要创建它:

    public class StartupReceiver extends BroadcastReceiver {
        private static final String TAG = "StartupReceiver";
    
        @Override
        public void onReceive(Context context, Intent intent) {  //onReceiver是在主线程中执行的
            Log.i(TAG, "Received broadcast intent: " + intent.getAction());
    
            boolean isOn = QueryPreferences.isAlarmOn(context);
            PollService.setServiceAlarm(context, isOn);
        }
    }

      broadcast receiver是接受intent的组件,当有intent发送给StartupReceiver时,它 的onReceive()方法会被调用。

      然后在AndroidManifest.xml文件中声明:

      

    <usespermissionandroid:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
    <receiver android:name=".StartupReceiver">
    <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>
    </receiver>

      完成声明后,即使app并未运行,只要有匹配的broadcast intent发来,broadcast receiver就会醒来接受,一收到intent,broadcast receiver的onReceive(Context. Intent)方法即开始执行,随后会被销毁。

    3. 过滤前台通知消息

      PhotoGallery应用的另一缺陷,通知消息虽然很有用,但应用开着的时候不应该受到通知消息。

      解决方式:

        首先,我们发送(或接收)定制版broadcast intent(最后会锁定它,只允许PhotoGallery应用部件接收它)。其次,不再使用manifest文件,改用代码为broadcast intent动态登记receiver。(动态注册的receiver与fragment进行绑定,收到广播时说明是在app中) 最后,发送一个有序broadcast在一组receiver中传递数据,借此保证最后才运行某个receiver(最后的receiver决定显不显示通知,这个receiver是静态注册的)。

      3.1 发送broadcast intent

        要发送broadcast intent,需要创建一个intent,并传入sendBroadcast(intent)方法即可。

    public static final String ACTION_SHOW_NOTIFICATION = "com.bignerdranch.android.photogallery.SHOW_NOTIFICATION";
    
    sendBroadcast(new Intent(ACTION_SHOW_NOTIFICATION));

      3.2 动态broadcast receiver

        动态broadcast receiver是在代码中,而不是在配置文件中完成登记声明。要在代码中登记,可调用registerReceiver(BroadcastReceiver, IntentFliter)方法,取消登记时,则调用unregiseterReceiver

    (BroadcastReceiver)方法。receiver自身通常被定义为一个内部类实例,如同一个按钮点击监听器。在registerReceiver()和unregisterReceiver()方法的BroadcastReceiver需要的是同一个实例、

        我们要只在应用开启的时候接受发过来的广播过滤,就不能在 manifest 中声明一个过滤器,而是要动态地建立一个广播接收器。我们在这里建立一个用于隐藏前台通知的通用 fragment 子类:

        

    public abstract class VisibleFragment extends Fragment {
        private static final String TAG = "VisibleFragment";
    
        @Override
        public void onStart() {
            super.onStart();
            IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);
            getActivity().registerReceiver(mOnShowNotification, filter,
                    PollService.PERM_PRIVATE, null);
        }
    
        @Override
        public void onStop() {
            super.onStop();
            getActivity().unregisterReceiver(mOnShowNotification);
        }
    
        private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // 如果接收到广播,说明应用正在前台,所以把 ResultCode 更改掉
                Log.i(TAG, "canceling notification");
                setResultCode(Activity.RESULT_CANCELED);
            }
        };
    }

      3.3 使用私有权限

        使用动态broadcast receiver存在一个问题,即系统中的任何应用均可监听并触发我们的receiver。

        有两种办法可以阻止应用闯入我们的私人领域,一种办法是在mainfest配置文件里给receiver标签添加一个android:exported= “false”属性,声明它仅限应用内部使用。

        

        另外,也可以创建自己的使用权限,可以通过在AndroidManifest.xml中添加一个permission标签来完成:

    <permission android:name="com.bignerdranch.android.photogallery.PRIVATE"
    android:protectionLevel="signature" />
    
    <uses-permission android:name="com.bignerdranch.android.photogallery.PRIVATE" />

        要使用权限,须将其作为参数传入sendBroadcast(),有了这个权限,所有应用都必须使用同样的权限才能接受我们发送的intent。

        要怎么保护我们的broad receiver呢?其他应用可通过创建自己的broadcast intent来触发它。同样,在 registerReceiver(...) 方法中传入自定义权限就能解决该问题:

    public abstract class VisibleFragment extends Fragment {
        ...
        @Override
        public void onStart() {
            super.onStart();
            IntentFilter filter = newIntentFilter(PollService.ACTION_SHOW_NOTIFICATION);
            getActivity().registerReceiver(mOnShowNotification, filter,
            PollService.PERM_PRIVATE, null);
        }
        ...
    }    

       

         3.3.1 深入学习安全级别          

            自定义权限必须指定 android:protectionLevel 属性值。Android根据 protectionLevel 属性值确定自定义权限的使用方式。在PhotoGallery应用中,我们使用的 protectionLevel 是signature 。signature 安全级别表明,如果其他应用需要使用我们的自定义权限,则必须使用和当前应用相同的key做签名认证。对于仅限应用内部使用的权限,选择 signature 安全级别比较合适。既然其他开发者没有相同的key,自然也就无法接触到权限保护的东西。此外,有了自己的key,将来还可用于我们开发的其他应用中。

     

        3.4 使用有序broadcast

        如果想让程序在打开时不发送出通知,就不能再让服务来发出通知了,因为它无法知道前台的运行状态。所以我们让 PollService 发送一个有序广播。

         

    public static final String REQUEST_CODE = "REQUEST_CODE";
    public static final String NOTIFICATION = "NOTIFICATION";
    
    private void showBackgroundNotification(int requestCode, Notification notification) {
        Intent i = new Intent(ACTION_SHOW_NOTIFICATION);
        i.putExtra(REQUEST_CODE, requestCode);
        i.putExtra(NOTIFICATION, notification);
        sendOrderedBroadcast(i, PERM_PRIVATE, null, null,
                                    Activity.RESULT_OK, null, null);
    }      

        有序广播是按照优先级发送的,先发送给优先级高的接收器,再发给优先级低的接收器。因为在应用结束后也要发出通知,显然我们发出通知的广播接收器是需要声明在 manifest 文件中的。

        内部实现如下:

        

    public class NotificationReceiver extends BroadcastReceiver {
        private static final String TAG = "NotificaitonReceiver";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "received result: " + getResultCode());
            if (getResultCode() != Activity.RESULT_OK) {
                // PollService 发出的 intent 带的结果码是 RESULT_OK
                // 如果接到的不是,说明应用在前台,将结果码修改了
                return;
            }
    
                // 如果没有 return,说明应用不在前台,就可以发出通知了。
            int requestCode = intent.getIntExtra(PollService.REQUEST_CODE, 0);
            Notification notification = (Notification)
                    intent.getParcelableExtra(PollService.NOTIFICATION);
    
            NotificationManagerCompat notificationManager =
                    NotificationManagerCompat.from(context);
            notificationManager.notify(requestCode, notification);
        }
    }

      

    <receiver android:name=".NotificationReceiver"
              android:exported="false">
        <!-- 在这里将优先级设为最低,即 -999 -->
        <intent-filter
            android:priority="-999">
            <action android:name="com.kniost.photogallery.SHOW_NOTIFICATION" />
        </intent-filter>
    </receiver>

     

        3.5 receiver与长时间运行任务

        如不想受限与主线程的时间限制,希望broadcast intent触发一个长时间运行任务,该如何做呢?

    •      将任务交给服务处理,然后通过broadcast receiver启动服务。
    •         使用BroadcastReceiver.getAsync()方法。该方法返回一个 BroadcastReceiver.PendingResult 对象,随后可使用该对象提供结果。因此,可将 PendingResult 交给AsyncTask 去执行长时运行的任务,然后再调用 PendingResult 的方法响应broadcast。
    •       goAsync() 方法的弊端是不够灵活。我们仍需快速响应broadcast(10秒内),并且与使用服务相比,没什么架构模式好选择。当然, goAsync() 方法也有明显的优势:可调用该方法设置有序broadcast的结果。

          3.6 使用EventBus

    broadcast intent可实现系统内全局性的消息传递。如果仅需要应用内的消息事件广播,该怎
    么做呢?答案是使用事件总线(event bus)。

    事件总线的设计思路就是,提供一个应用内的部件可以订阅的共享总线或数据流。事件一旦
    发布到总线上,各订阅部件就会被激活并执行相应的回调代码。

    由greenrobot出品的EventBus是目前广为人知的一个第三方事件总线库。

    为实现在应用内发送broadcast intent,Android自己也提供了一个叫作 LocalBroadcast-
    Manager 的广播管理类;但上述第三方类库用起来更为灵活和方便。

        

  • 相关阅读:
    activity(工作流)初步学习记录
    npm install出现npm ERR! write after end解决方法
    npm ERR! Cannot read property 'match' of undefined 错误处理
    gist.github.com 被墙无法访问解决办法
    AS SSD Benchmark固态硬盘检测工具
    VSCode 云同步扩展设置 Settings Sync 插件
    window系统不显示预览图片的处理方法
    VsCode如何同时打开多个项目
    win10的开机启动文件夹在哪及开机自动启动软件
    Java环境配置和tomcat环境配置
  • 原文地址:https://www.cnblogs.com/chase1/p/7223696.html
Copyright © 2020-2023  润新知