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

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 的广播管理类;但上述第三方类库用起来更为灵活和方便。

    

安卓权威编程指南-笔记(第27章 broadcast intent)的更多相关文章

  1. 安卓权威编程指南 -笔记(19章 使用SoundPool播放音频)

    针对BeatBox应用,可以使用SoundPool这个特别定制的实用工具. SoundPool能加载一批声音资源到内存中,并支持同时播放多个音频文件.因此所以,就算用户兴奋起来,狂按按钮播放全部音频, ...

  2. 安卓权威编程指南 -笔记(18章 处理assets)

    resources资源可以存储声音文件,但当处理多个音乐文件时,效率会很低. assets可以被看作随应用打包的微型文件系统,支持任意层次的文件目录结构.类似游戏这样需要加载大量图片和声音资源的应用通 ...

  3. 安卓权威编程指南-笔记(第21章 XML drawable)

    在Andorid的世界里,凡事要在屏幕上绘制的东西都可以叫drawable,比如抽象图形,Drawable的子类,位图图形等,我们之前用来封装图片的BitmapDrawable就是一种drawable ...

  4. 安卓权威编程指南-笔记(第23章 HTTP与后台任务)

    1. 网络连接基本 //通过指定URL获取原始数据,并返回一个字节流数组. public byte[] getUrlBytes(String urlSpec)throws IOException{ / ...

  5. 安卓权威编程指南-笔记(第22章 深入学习intent和任务)

    本章,我们会使用隐式intent创建一个替换android默认启动器的应用.名为NerdLauncher. NerdLauncher应用能列出设备上的其他应用,点选任意列表项会启动相应应用. 1. 解 ...

  6. 安卓权威编程指南-笔记(第24章 Looper Handler 和 HandlerThread)

    AsyncTask是执行后台线程的最简单方式,但它不适用于那些重复且长时间运行的任务. 1. Looper Android中,线程拥有一个消息队列(message queue),使用消息队列的线程叫做 ...

  7. 安卓权威编程指南 挑战练习 25章 深度优化 PhotoGallery 应用

    你可能已经注意到了,提交搜索时, RecyclerView 要等好一会才能刷新显示搜索结果.请接受挑战,让搜索过程更流畅一些.用户一提交搜索,就隐藏软键盘,收起 SearchView 视图(回到只显示 ...

  8. 安卓权威编程指南 - 第五章学习笔记(两个Activity)

    学习安卓编程权威指南第五章的时候自己写了个简单的Demo来加深理解两个Activity互相传递数据的问题,然后将自己的学习笔记贴上来,如有错误还请指正. IntentActivityDemo学习笔记 ...

  9. 安卓权威编程指南 挑战练习(第26章 在 Lollipop 设备上使用 JobService)

    26.11 挑战练习:在 Lollipop 设备上使用 JobService 请创建另一个 PollService 实现版本.新的 PollService 应该继承 JobService 并使用 Jo ...

随机推荐

  1. Proe5.0导出PDF至配置文件的相关方法,VC++

    定义文件bcsMessage.txt PLM PLM PLM # login login 测试 # Active messagebox menu Active messagebox menu 激活菜单 ...

  2. split - 拆分文件

    拆分文件 # 每个文件的行数为1000行 split -l 1000 test.txt # 将test文件拆分,20M一个文件 split -b 20M test.txt test文件拆分,并且文件名 ...

  3. 吴裕雄--天生自然 JAVA开发学习:继承

    class 父类 { } class 子类 extends 父类 { } public class Penguin { private String name; private int id; pub ...

  4. Different Gene Frequencies in the Two Sexes

    I.7 Different Gene Frequencies in the Two Sexes 假设存在一种基因仅在第一代亲代的不同性别之间的概率有区别,比如,A 在male中频率是Pm,a是(1-P ...

  5. Review For Exam

    Review For Exam [2019 福建省赛] 一个很简单的状态压缩DP,结果集体走偏 如何解决连续几日的限制问题?这种东西普通的DP很难写 #include <bits/stdc++. ...

  6. ElasticSearch6 报错blocked by: [FORBIDDEN/12/index read-only / allow delete (api)];

    原文连接:https://blog.csdn.net/u013042707/article/details/84110531 方案:curl -XPUT -H "Content-Type: ...

  7. SSH(struts+spring+hibernate)常用配置整理

    SSH(struts+spring+hibernate)常用配置整理 web.xml配置 <?xml version="1.0" encoding="UTF-8&q ...

  8. Hexo 下 Markdown 的配置与学习

    本篇 更换 Hexo 下的 Markdown 渲染插件 学习 Markdown 基本语法 ✎更换 Markdown 渲染插件 ✎原因 Hexo 内置的默认渲染插件是 hexo-renderer-mar ...

  9. JVM如何判断对象能否被回收

    •写在前面说起Java和C++,很容易想到让人疯狂的指针,Java使用了内存动态分配和垃圾回收技术,让我们从C++的各种指针问题中摆脱出来,更加专心于业务逻辑,不过如果我们需要深入了解java的JVM ...

  10. Qt QLabel show 显示图像、填充、缩放

    主要成员函数: 1.void setText(QString); //设置label框内的文本. 2.void hide(); //隐藏label框. 3.void setBuddy(QWidget* ...