符合中小企业对网站设计、功能常规化式的企业展示型网站建设
本套餐主要针对企业品牌型网站、中高端设计、前端互动体验...
商城网站建设因基本功能的需求不同费用上面也有很大的差别...
手机微信网站开发、微信官网、微信商城网站...
(a).动态注册 在UI中注册的广播,例如:
为凤台等地区用户提供了全套网页设计制作服务,及凤台网站建设行业解决方案。主营业务为成都做网站、成都网站设计、成都外贸网站建设、凤台网站设计,以传统方式定制建设网站,并提供域名空间备案等一条龙服务,秉承以专业、用心的态度为用户提供真诚的服务。我们深信只要达到每一位用户的要求,就会得到认可,从而选择与我们长期合作。这样,我们也可以走得更远!
(b).静态注册
需要在manifest中进行注册(在安卓8.0后系统废除了大部分静态广播,最好使用动态注册)。
(a).系统广播
系统中已经定义的广播,此类广播只能由系统发出,并且需要在intent-filter中加上系统已经写的action。
(b).自定义广播
顾名思义,是用户自己定义的广播。
(a)我们首先需要一个广播接收类
(b)其次注册动态广播
(c)最后需要通过send方法发送一个广播供广播接收者接受
另外还有有序广播和无序广播,这篇博客写的比较详细,供大家参考: Android的有序广播和无序广播(解决安卓8.0版本之后有序广播的接收问题) - ming3 - 博客园 (cnblogs.com)
Broadcast (广播)是一种广泛运用的应用程序之间传输信息的机制,而 BroadcastReceiver (广播接收器)则是用于接收来自系统和应用的广播对并对其进行响应的组件,Android中我们要发送的广播内容是一个 Intent ,这个 Intent 中可以携带我们要传送的数据
创建一个广播接收器非常简单,只需要继承 BroadcastReceiver ,并重写 onReceive() 即可
BroadcastReceiver 也是四大组件之一,所以我们也需要对 BroadcastReceiver 进行注册,不同于其他四大组件, BroadcastReceiver 有两种注册方式,分别是 静态注册 和 动态注册
静态注册
当我们的应用首次启动的时候,系统会自动实例化我们静态注册的 BroadcastReceiver ,然后将这个 BroadcastReceiver 注册到系统中,系统接收到广播之后,就会做出相应的判断,调用 onReceive() 方法。通过这种方式注册的广播,即使我们的应用被销毁,依然能收到广播。 这里要注意的是,我们的应用一定要被启动过 ,如果没有被启动可能就无法接收到广播,可以参考文章 Android应用在未启动的情况下无法收到指定广播的问题总结
正是因为静态注册耗电、占内存、不受程序生命周期影响,所以Google在Android 8.0上禁止大部分广播的静态注册,可以参考官文文档 Android 8.0 功能和 API
动态注册
通过动态注册的广播, BroadcastReceiver 的生命周期跟随Activity的生命周期
注意: 要在 Activity 的 onPause() 中 unRegeisterReceiver() ,否则会引起内存泄漏。比较推荐 onResume() 中去注册广播,在 onPause() 中去注销广播。因为在内存资源比较吃紧的情况下,可能我们的 Activity 执行完 onPause() 之后就被销毁,这时候 Activity 的 onStop() 和 onDestory() 方法就不会执行了
BroadcastReceiver注册完之后,这个 BroadcastReceiver 就能够接收响应的广播,下面我们来说说如何发送一条广播
普通广播(Normal Broadcast)
普通广播完全是异步的,通过 context.sendBroadcast() 方法发送,消息传递效率比较高,但所有接收器的执行顺序不确定。缺点是接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传播
有序广播(Ordered Broadcast)
有序广播是通过 context.sendOrderedBroadcast() 方法发送,所有的广播者按照优先级依次执行,广播接收器的优先级通过 receiver 的 intent-filter 中的 android:priority 属性来设置,数值越大优先级越高。当广播接收器接收到广播后,可以使用 setResult() 方法把结果传递给下一个接收者,通过 getResult() 方法获取上一个接收者传递过来的结果,并可以通过 abortBroadcast() 方法丢弃该广播,使该广播不再传递给下一个接收者
粘性广播(Sticky Broadcast)
粘性广播通过 context.sendStickBroadcast() 方法来发送,用此方法发送的广播会一直滞留,当有匹配此广播的接收器被注册后,该广播接收器就会收到此广播。使用此广播时,需要获得 BROADCAST_STICKY 权限
由于在Android5.0 API 21中已经失效,所以不建议使用。
本地广播(Local Broadcast)
前三种广播都是全局广播,所有应用都可以接收到,这样就带来安全隐患,而本地广播只在进程内传播,可以起到保护数据安全的作用
其实,本地广播的使用与其十分类似,之前的步骤均是一样的,调用者不同而已,本地广播调用的是 LocalBroadcastManager 相关方法,全局广播调用的是 Context 的相关方法,其方法名都是一样的
这里需要说一下, 使用本地广播并没有静态注册的方法 ,因为静态注册主要是为了让程序在未启动的情况下也能收到广播,而发动本地广播的时候,我们的程序已经是启动了,所以,自然是没有静态注册这个方法
Android中内置了多个系统广播,当使用系统广播时,只需要在注册广播接收者时定义相关的 action 即可,并不需要手动发送广播,当系统有相关操作(如开机、网络状态变化、拍照等等)时会自动进行系统广播
Android系统广播 action 如下:
本文介绍了 BroadcastReceiver 的两种注册方式(动态注册、静态注册),四种发送方式(普通广播、有序广播、粘性广播(API21废弃)、本地广播),以及系统广播的用法。几乎涵盖了 BoradcastReceiver 在应用开发过程中的所有知识,对于BroadcastReceiver原理感兴趣的可以继续看 BroadcastReceiver详解(原理篇)
以下广播简称Broadcast
是Android四大组件之一,在四大组件的另外两个组件 和 拥有发送和接收广播的能力。Android 是在 进程间通信机制的基础上实现的,内部基于消息发布和订阅的事件驱动模型,广播发送者负责发送消息,广播接收者需要先订阅消息,然后才能收到消息。 进程间通信与 的区别在于:
有三种类型
存在一个注册中心,也可以说是一个调度中心,即 。广播接收者将自己注册到 中,并指定要接收的广播类型;广播发送者发送广播时,发送的广播首先会发送到 , 根据广播的类型找到对应的 ,找到后边将广播发送给其处理。
这里以普通广播为例子, 接收者有两种注册方式,一种是 ,一种是 :
(广播的发送分为 两种,这里针对有序的广播) 中的android:priority=""和 中的IntentFilter.setPriority(int)可以用来设置广播接收者的优先级,默认都是0 , 范围是[-1000, 1000],值越大优先级越高,优先级越高越早收到。
在相同优先级接收同个类型广播时, 的广播接收器比 的广播接收者更快的接收到对应的广播,这个之后会进行分析。
注:以下源码基于rk3399_industry Android7.1.2
的流程可分为 , 和 三个部分,这里依次分析下
在Android系统的 机制中,前面提到, 作为一个注册和调度中心负责注册和转发 。所以 的注册过程就是把它注册到 的过程。
这里我们分析 广播的过程, 和 有一个共同的父类 ,所以它们对应的注册过程其实是调用 ,接下来我们按照流程逐步分析调用流程的源码。
frameworks/base/core/java/android/content/ContextWrapper.java
在之前的 Android应用程序启动入口ActivityThread.main流程分析 分析过,在我们启动 Activity 时会创建一个 对象,然后通过 传给我们启动的 ,其内部就会将该对象赋值给 ; 的 方法也是类似的赋值流程,这里放个简易的源码应该更好理解
可以看到最后都会将生成的 对象赋值给对应的
对象。接下来继续分析 , 即 函数。
/frameworks/base/core/java/android/app/ContextImpl.java
这里我们首先看下如何将广播接收者 封装成一个 接口的 本地对象
/frameworks/base/core/java/android/app/LoadedApk.java
每一个注册过广播接收者的 或 组件在font color='Crimson' LoadedApk /font类中都有个对应的 对象,该对象负责将 与 组件关联起来。这些对象,以关联的 作为关键字保存在一个 中。之后对应的 又以 的 作为关键字保存在 的成员变量 对象中。最后通过 对应的 方法获得其 接口的 本地对象。之后再回到 注册方法内,将 对象发给 进行注册。
/frameworks/base/core/java/android/app/ActivityManagerNative.java
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
在的 或 注册一个 时,并不是将其注册到font color='OrangeRed'AMS/font中,而是将与它关联的font color='OrangeRed'InnerReceiver/font对象注册到font color='OrangeRed'AMS/font中,当font color='OrangeRed'AMS/font接收到广播时,会根据 在内部找到对应的font color='OrangeRed'InnerReceiver/font对象,然后在通过这个对象将这个广播发送给对应的 处理。
注册过程这边画了一个简单的流程图:
font color='OrangeRed'Broadcast/font的发送过程可简单描述为以下几个过程:
frameworks/base/core/java/android/content/ContextWrapper.java
/frameworks/base/core/java/android/app/ContextImpl.java
/frameworks/base/core/java/android/app/ActivityManagerNative.java
/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
android的粘性广播,是指广播接收器一注册马上就能接收到广播的一种机制,当然首先系统要存在广播。而普通广播就是要先注册广播接收器,然后广播被发送到系统,广播接收器才能接收到广播。
所以他们的区别是:
粘性广播调用registerReceiver能马上接受广播,而普通广播不行。
对于粘性广播:
1.系统首先存在粘性广播
2.注册广播接收器
3.处理广播
下面用一个例子展示下他们的区别
主Acitivity
布局
布局有两个按钮,一个是注册粘性广播,一个是注册普通广播。点击注册粘性广播按钮会马上返回结果。而点击注册普通广播按钮则没有反应
Android 中的广播主要可以分为两种类型:标准广播和有序广播
一种完全异步执行的广播,在广播发出之后,所有的BroadcastReceiver几乎会在同一时刻收到这条广播消息,因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,但同时也意味着它是无法被截断
标准广播工作示意图:
一种同步执行的广播,在广播发出之后,同一时刻只会有一个BroadcastReceiver能够收到这条广播消息,当这个BroadcastReceiver中的逻辑执行完毕后,广播才会继续传递。所以此时的BroadcastReceiver是有先后顺序的,优先级高的BroadcastReceiver就可以先收到广播消息,并且前面的BroadcastReceiver还可以截断正在传递的广播,这样后面的BroadcastReceiver就无法收到广播消息了
有序广播工作示意图:
可以让程序在未启动的情况下接收广播
在Android 8.0系统之后,所有隐式广播都不允许使用静态注册的方式来接收了。隐式广播指的是那些没有具体指定发送给哪个应用程序的广播,大多数系统广播属于隐式广播,但是少数特殊的系统广播目前仍然允许使用静态注册的方式来接收,详见网址:
在 AndroidManifest.xml 文件中注册
在 AndroidManifest.xml 文件中进行权限声明
不要在 onReceive() 方法中添加过多的逻辑或者进行任何的耗时操作,因为BroadcastReceiver中是不允许开启线程的,当 onReceive() 方法运行了较长时间而没有结束时,程序就会出现错误
先定义一个BroadcastReceiver来准备接收此广播
在 AndroidManifest.xml 文件中注册
有序广播是一种同步执行的广播,并且是可以被截断的。为了验证这一点,我们需要再创建一个新的BroadcastReceiver。新建AnotherBroadcastReceiver
同样,在 AndroidManifest.xml 文件中注册,同时,使用 intent-filter 标签的 android:priority 属性设置优先级
前面的 AnotherBroadcastReceiver 的优先级比较高,因此 AnotherBroadcastReceiver 一定比 MyBroadcastReceiver 先收到广播,因此,可以在 AnotherBroadcastReceiver 的 onReceive 方法中使用 abortBroadcast() 方法截断广播,这样 MyBroadcastReceiver 就收不到该广播了
在界面上弹出一个对话框,让用户无法进行任何其他操作,必须点击对话框中的“确定”按钮,关闭所有的Activity,然后回到登录界面即可
ActivityCollector 类用于管理所有的Activity,具有关闭所有Activity的功能
创建 BaseActivity 类作为所有 Activity 的父类,并在里面实现强制下线功能,在这里实现此功能,有以下几点原因
创建一个LoginActivity来作为登录界面
activity_login.xml
LoginActivity 如果输入 123 就到 MainActivity界面
MainActivity 中点击强制下线按钮,就发送强制下线的广播
MainActivity 布局
对于Activity的启动流程,我们已经有了几个版本的分析了。这里我们分析一个更容易一些的,四大组件中最简单的Broadcast Receiver。
关于Broadcast,有几点需要了解。首先是广播的类型,然后是广播的发送方法,最后是广播是如何被接收的。这三者相辅相承的,比如普通广播和有序广播只有在详细了解了广播的接收过程了之后,才能真正明白它的含义。
普通的广播是不在意顺序的,最简单的理解是同时可以收到这个广播。如果应用是动态注册这个广播的,且广播发送时这个进程还活着,那么当然可以并发的把广播尽快地传送出去是最好的。
但是,如果是通过AndroidManifest.xml静态注册的情况,也就是说这个广播首先要把一个进程启动起来,这时并发启动很多进程就是个问题了。Android目前的做法是,对这种静态的广播接收者,自动按有序广播的方式来串行处理。但是这对应用是透明的,应用不能假设系统已经把静态的无序广播当成有序广播来处理。
这个时候讲粘性广播有福了,因为从Android 5.0(API 21)开始,因为安全性的问题,官方已经正式废弃了粘性广播。
Context类提供两个方法可以用于发送普通广播:
差别是第二个设置权限。
发给特定的用户:
有序广播因为要处理消息的处理结果,所以要复杂一些。
如果只是想让广播可以按优先级来收取,并不在意处理的结果,可以用下面的版本:
同样,在多用户环境下,也可以选择给哪个用户发广播:
不管是普通的还是有序的广播都对应有粘性的版本:
以上的API都是定义于Context类中:
首先我们先看看发送端是如何发送的。
我们首先先放一个大图,让大家先有一个直观的印象,不管普通广播、有序广播、粘性广播如何组合,最终都汇集到一个大方法中。
我们先看应用发送普通广播的一个简单的例子:
非常简单,调用ContentWrapper的sendBroadcast方法就可以了。
然后我们顺藤摸瓜就好了。
Activity中的sendBroadcast,实际上调用的是:
我们来看frameworks/base/core/java/android/content/ContextWrapper.java中对sendBroadcast的定义:
ContextWrapper只是一个包装,真正的实现在ContextImpl中
我们来看/frameworks/base/core/java/android/app/ContextImpl.java中真正实现sendBroadcast的功能:
它会通过IPC去调用AMS的broadcastIntent。由于我们这个普通的广播的方法参数最少,所以好多都是传null。
加锁,定参数,然后调用真正的逻辑的实现。
我们先把broadcastIntentLocked的真正逻辑放一下,先看看有序广播是如何发送的。
ContextWrapper.sendOrderedBroadcast
Context是abstract方法,调用的是ContextWrapper的实现:
跟普通广播一样,还是会调用到ContextImpl.sendOrderedBroadcast
有序广播调用broadcastIntent的区别在于serialized参数,普通广播为false,有序广播为true.
原型为:
前面讲过带有回调的版本,我们看看它是如何实现的:
当然还是调用ContextImpl.sendOrderedBroadcast
这次变成只是一个封装了,它会调用一个更多参数的版本:
这次是一个全参数调用broadcastIntent的版本了,除了sticky就齐了
我们也不绕圈子了,直接看ContextImpl.sendStickyBroadcast.