实验目的
- 掌握 Broadcast 编程基础。
- 掌握动态注册 Broadcast 和静态注册 Broadcast。
- 掌握Notification 编程基础。
- 掌握 EventBus 编程基础。
设置Activity为单例模式
manifests中添加
android:launchMode="singleInstance"
还有这几种情况:
(1)
standard:每次激活Activity时(startActivity),都创建Activity实例,并放入任务栈;(2)
singleTop:如果某个Activity自己激活自己,即任务栈栈顶就是该Activity,则不需要创建,其余情况都要创建Activity实例;(3)
singleTask:如果要激活的那个Activity在任务栈中存在该实例,则不需要创建,只需要把此Activity放入栈顶,并把该Activity以上的Activity实例都pop;(4)
singleInstance:如果应用1的任务栈中创建了MainActivity实例,如果应用2也要激活MainActivity,则不需要创建,两应用共享该Activity实例;
Broadcast使用
BroadcastReceiver(广播接收器),属于 Android 四大组件之一
注册的方式分为两种:静态注册、动态注册
静态注册
1.注册广播
创建一个java类StaticReceiver
1 | public class StaticReceiver extends BroadcastReceiver { |
然后在配置文件App->manifests->AndroidManifest.xml中加上
1 | <receiver android:name=".StaticReceiver" |
这里的
com.janking.sysuhealth.myapplication2.MyStaticFilter理论上可以自定义,只要保持一致就可以(后面还会出现)
2.发送广播
这里要实现打开页面就会弹出通知并随机出现一个Food的名字,点击跳到其详情页面
所以在Food列表的onCreate函数(即SecondActivity)中加上
1 | //broadcast |
这里我用的API是28,即compileSdkVersion 28,跟之前的版本可能会不一样!
关于Android SDK里的compileSdk、minSdk、targetSdk
3.接收广播
要认识到,广播其实也是以Intent的方式传过来的
所以还是这样解析传过来的Food
1 | Bundle bundle = intent.getExtras(); |
4.发送通知
不管什么时候,看到发送、接收、跳转这些字眼就只要有数据的传送,一般用的是Intent,这里要用PendingIntent
根据字面意思就知道是延迟的
intent,主要用来在某个事件完成后执行特定的Action。PendingIntent包含了Intent及Context,所以就算Intent所属程序结束,PendingIntent依然有效,可以在其他程序中使用。
常用在通知栏及短信发送系统中。
创建PendingIntent
1 | //获取pendingIntent |
又提到Android SDK版本的问题了,这里是8.0(API26)以上的使用方法,理论上低版本的方法更容易找到,但是我们目光还是要向前看(/滑稽)
创建通知Notification
1 | //获取状态通知栏管理 |
(以上三段代码都是放入
StaticReceiver中的if语句中)
mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
和
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
一定要用同一个CHANNEL_ID,不然就会出现下列错误

那么现在,如果点击通知跳转到食品详情页面,Detail.java需要更改什么吗?
不需要,这就像从食品列表跳转到详情一样,解析Food就行了,而且我很偷懒地把从Notification中发出的PendingIntent中的Food关键词也设置成Click_Food,对Detail来讲,它就像从列表跳过来一样!
动态注册
其实在Android 8.0之后是不建议用静态注册的,毕竟不灵活,所以还是关注下动态注册吧
1.注册广播
创建类DynamicReceiver
1 | public class DynamicReceiver extends BroadcastReceiver { |
在需要发送广播的类中(这里是Detail)添加
1 | //注册广播 |
注意两个地方的
Action要一致(下面还会出现)
2.发送广播
这里发送广播的激发事件是点击了Detail中的收藏图标

所以修改collect.setOnClickListener为
1 | collect.setOnClickListener(new View.OnClickListener() { |
3.接收广播
在DynamicReceiver中的if语句中添加
1 | Bundle bundle = intent.getExtras(); |
4.发送通知
再添加
1 | //获取pendingIntent |
其实本质上这两个广播都是一样的,只是注册方式不一样,处理方式都是差不多的
5. 取消注册
这个是一定要做的,不然会出现内存泄漏的错误
1 | E/ActivityThread: Activity com.janking.sysuhealth.Detail has leaked IntentReceiver com.janking.sysuhealth.DynamicReceiver@c408803 that was originally registered here. Are you missing a call to unregisterReceiver()? |

注意:动态广播最好在
Activity的onResume()注册、onPause()注销,当然我这里是在Oncreate()中注册的也行
所以在Detail中添加
1 |
|
重复注册、重复注销也不允许
EventBus的简单使用
其实到这里,数据同步的问题还没有解决
当点击收藏弹出消息时,点击消息只能进去SecondActivity(即食品列表)界面,现在需要它显示为收藏食品列表界面,并且能够更新某些食品
当然直接用广播传来的数据是可以的,不过还是要学会使用多种方法(因为其实用广播处理的话是有点难…….)
1.添加依赖
在Gradle Scripts ->build.gradle(Module: app)中的dependencies添加
implementation 'org.greenrobot:eventbus:3.0.0'
2.声明订阅方法
在需要接收数据的Activity(这里是SecondActivity)中添加
1 | //EventBus |
不要添加到
onCreate()方法或者其它方法中,要直接加到Activity类中,当做一个成员。不然会出现错误
@Override “Annotations are not allowed here”
这里的处理大概就是更新食品总列表中的数据(不可见)以及收藏食品列表中的元素(课件),而且改变下面的FloatingButton,并且使RecyclerView不可见,ListView可见
3.订阅
在SecondActivity中添加(比如在onCreate())
1 | EventBus.getDefault().register(this); |
4.发送数据
这里就是在点击Detail中的通知后返回到SecondActivity页面,想来想去,也只有一个Food类型的数据要传过来,那就在之前的DynamicReceiver中的if语句中添加一句
1 | EventBus.getDefault().post(food); |
就能传送数据了,真的是超级简单啊!!!
5.解决列表重复的问题
在Detail页面中多次点击收藏图标的话,虽然我之前做了处理,弹出的Toast会显示“重复收藏”,但是每次点击都会发送广播,每次发送广播都会有一个post(food)的动作,即使不跳过去看,但是其实数据已经右EventBus传到了SecondActivity`了,并且也已经处理了。
所以我想了个办法,对这一句myListViewAdapter.addNewItem(f);做做手脚,找到MyListViewAdapter中的MyListViewAdapter,改为
1 | public void addNewItem(Food f) { |
就不会重复啦!
效果:(AVD实在有点卡)

参考资料
AVD,Guest isn’t online after 7 seconds 问题
安卓8.0系统notification适配Failed to post notification on
android show notification with a popup on top of any application