技术标签: # 安卓 原生 react-native Kotlin 安卓 广播 android kotlin
Android
01: Android Studio目录结构介绍, 安卓开发入门
02: Android中的日志工具类Log详细介绍
03: 添加Button元素, 并且在活动中使用Toast和Menu
04: 安卓开发之Intent使用介绍(显式Intent和隐式Intent)
05: 安卓广播机制讲解(标准广播和有序广播)
06: 安卓广播实现强制下线功能(Kotlin语言实现)
是一种完全异步执行的广播,在广播发出之后,
所有的广播接收器几乎都会在同一时刻接收到这条广播消息,
因此它们之间没有任何先后顺序可言。这种广播的效率会比较高,
但同时也意味着它是无法被截断的。标准广播的工作流程如图所示。
是一种同步执行的广播,在广播发出之后,
同一时刻只会有一个广播接收器能够收到这条广播消息,
当这个广播接收器中的逻辑执行完毕后,广播才会继续传递。
所以此时的广播接收器是有先后顺序的,
优先级高的广播接收器就可以先收到广播消息,
并且前面的广播接收器还可以截断正在传递的广播,
这样后面的广播接收器就无法收到广播消息了。
有序广播的工作流程如图所示。
Android内置了很多系统级别的广播,
我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息。
比如手机开机完成后会发出一条广播,电池的电量发生变化会发出一条广播,
时间或时区发生改变也会发出一条广播,等等。
如果想要接收到这些广播,就需要使用广播接收器,
下面我们就来看一下它的具体用法。
广播接收器可以自由地对自己感兴趣的广播进行注册BroadcastReceiver,
这样当有相应的广播发出时,广播接收器就能够收到该广播,
并在内部处理相应的逻辑。注册广播的方式一般有两种,
在代码中注册和在 AndroidManifest.xml中注册,其中前者也被称为动态注册,
后者也被称为静态注册。
那么如何创建一个BroadcastReceiver呢?
其实只需要建立一个类,让它继承自BroadcastReceiver,
并重写父类的onReceive()方法就行了.这样当广播来到时,
onReceive()方法就会执行,我们可以根据需求改变相应的逻辑.
那我们就先通过动态注册的方式编写一个能够监听时间变化的程序,
借此学习一下广播接收器的基本用法吧。
新建一个BroadcastTest项目,然后修改MainActivity中的代码,如下所示:
class MainActivity : AppCompatActivity() {
lateinit var timeChangeReceiver: TimeChangeReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intentFilter =IntentFilter()
intentFilter.addAction("android.intent.action.TIME_TICK")
timeChangeReceiver= TimeChangeReceiver()
registerReceiver(timeChangeReceiver,intentFilter)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(timeChangeReceiver)
}
inner class TimeChangeReceiver:BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
TODO("Not yet implemented")
Toast.makeText(context,"Time has Changed",Toast.LENGTH_SHORT).show()
}
}
}
可以看到,我们在MainActivity 中定义了一个内部类TimeChangeReceiver,
这个类是继承自BroadcastReceiver的,
并重写了父类的onReceive()方法。这样每当系统时间发生变化时,
onReceive()方法就会得到执行,这里只是简单地使用Toast提示了一段文本信息。
然后观察oncreate()方法,首先我们创建了一个IntentFilter的实例,
并给它添加了一个值为android.intent.action.TIME_TICK的action,
为什么要添加这个值呢?因为当系统时间发生变化时,
系统发出的正是一条值为android.intent.action.TIME_TICK的广播,
也就是说我们的广播接收器想要监听什么广播,就在这里添加相应的action。
接下来创建了一个TimeChangeReceiver的实例,
然后调用registerReceiver()方法进行注册,
将TimeChangeReceiver的实例和IntentFilter 的实例都传了进去,
这样TimeChangeReceiver就会收到所有值为android.intent.action.TIME_TICK的广播,
也就实现了监听系统时间变化的功能.
最后要记得,动态注册的广播接收器一定都要取消注册才行,
这里我们是在 onDestroy()方法中通过调用unregisterReceiver()方法来实现的。
整体来说,代码还是非常简单的,现在运行一下程序,
然后静静的等待时间的变化.系统每隔一分钟就会发出一条
android.intent.action.TIME_TICK的广播,
因此我们最多只需要等待一分钟就可以收到这条广播了.
运行结果:
这是动态注册BroadcastReceiver的基本用法,
虽然这里我们只使用了一种系统广播来举例,
但是接收其他系统广播的用法是一模一样的.
Android系统还会再里亮屏熄屏,电量变化,网络变化等场景下发出广播.
如果你想查看完整的广播列表,可以到如下路径去查看:
SDK\platforms\任意Android版本\data\broadcast_actions.txt
比如我的在:
D:\softWare\SDK\platforms\android-29\data\broadcast_actions.txt
进去查看效果如图:
动态注*的广播接收器可以自由地控制注册与注销,
在灵活性方面有很大的优势,但是它也存在着一个缺点,
即必须要在程序启动之后才能接收到广播,因为注册的逻辑是写在onCreate()方法中的。
那么有没有什么办法可以让程序在未启动的情况下就能接收到广播呢?
这就需要使用静态注册的方式了。
从理论上面讲动态注册可以监听到的系统广播,
静态注册应该也可以监听到,在过去Android确实是这样,
但由于大量的恶意应用利用这个机制在程序为启动的情况下监听系统广播,
从而使任何应用都可以频繁的从后台被唤醒,
严重影响了用户手机的电量和性能,
以此Android几乎每个版本都在削减静态注册BroadcastReceiver的功能.
在Android8.0后所有隐式广播都不允许使用静态注册的方式来接收了,
隐式广播指的是那些没有具体指定发送给哪个应用的广播,
大多数系统广播都属于隐式广播,
但是少数特殊的系统广播目前仍然允许使用静态注册的方式来接收.
这些特殊的系统广播列表详见:
(https://developer.android.google.cn/guide/components/broadcast-exceptions.html)
接下来我们来学习一个特殊的系统广播:
android.intent.action.BOOT_COMPLETED ,
这是一条开机广播
这里我们准备让程序接收一条开机广播,
当收到这条广播时就可以在 onReceive()方法里执行相应的逻辑,
从而实现开机启动的功能。
可以使用Android Studio提供的快捷方式来创建一个广播接收器,
右击com.example.broadcasttest包→New→Other→Broadcast Receiver,
会弹出如图的界面:
可以看到,这里我们将广播接收器命名为BootCompleteReceiver,
Exported属性表示是否允许这个广播接收器接收本程序以外的广播,
Enabled属性表示是否启用这个广播接收器。勾选这两个属性,点击Finish完成创建。
然后修改BootCompleteReceiver 中的代码,如下所示:
class BootCompleteReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context,"Boot Complete",Toast.LENGTH_SHORT).show()
}
}
代码很简单,只是在onReceive()方法中使用Toast弹出一段消息.
另外,静态的广播接收器一定要在AndroidManifest.xml文件中注册才可以使用,
不过由于我们是使用Android Studio的快捷方式创建的广播接收器,
因此注册这一步已经被自动完成了。
打开AndroidManifest.xml文件瞧一瞧,代码如下所示:
可以看到,application标签内出现了一个新的标签receiver,
所有静态的广播接收器都是在这里进行注册的。
它的用法其实和activity标签非常相似,
也是通过android:name来指定具体注册哪一个广播接收器,
而enabled和exported属性则是根据我们刚才勾选的状态自动生成的。
不过目前BootCompleteReceiver还是不能接收到开机广播的,
我们还需要对AndroidManifest.xml文件进行修改才行,如下所示:
由于Android系统启动完成后会发出一条值为
android.intent.action.BO0T_COMPLETED
的广播,因此我们在intent-filter标签里添加了相应的action。
另外,监听系统开机广播也是需要声明权限的,
可以看到,我们使用uses-permission标签又加入了一条android.
permission.RECEIVE_BOOT_COMPLETED权限。
现在重新运行程序后,我们的程序就已经可以接收开机广播了。
将模拟器关闭并重新启动,在启动完成之后就会收到开机广播.
在发送广播之前,我们先定义一个BroadcastReceiver类来接收此广播:
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context,"receiver in MyBroadcastReceiver",Toast.LENGTH_LONG).show()
}
}
当MyBroadcastReceiver收到自定义广播后,
就会弹出"receiver in MyBroadcastReceiver"的提示.
然后对AndroidManifest.xml中BroadcastReceiver进行修改:
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.broadcasttest.MY_BROADCAST"></action>
</intent-filter>
</receiver>
可以看到这里让MyBroadcastReceiver收到一条值为:
com.example.broadcasttest.MY_BROADCAST的广播,
因此待会在发送广播的时候,我们需要发送一条这样的广播.
接下来修改activity_main.xml里的代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send Button" />
</LinearLayout>
这里定义了一个按钮,用于作为发送广播的触发点.
然后修改MainActivity中的代码:
在onCreate()方法里面添加如下代码:
button.setOnClickListener {
val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
intent.setPackage(packageName)
sendBroadcast(intent)
}
可以看见,我们在按钮的点击事件里面加入了发送自定义广播的逻辑.
首先构建了一个Intent对象,并把要发送的广播植入.
然后调用Intent的setPackage()方法,并且传入当前程序的包名,
packageName是getPackageName()的语法糖写法,
用于获取当前应用程序的包名,最后调用sendBroadcast()方法将标准广播发送出去.
注意这里一定要调用setPackage()方法,
指定这条广播是发送给哪个应用程序的,
从而让它变为一条显示广播,否则静态注册的BroadcastReceiver将无法收到这条广播.
接下来运行程序,点击发送按钮:
这样我们就完成了发送自定义广播的功能,另外,
由于广播是使用Intent发送的,所以我们可以在广播里面
传输一些数据给BroadcastReceiver.
和标准广播不同,有序广播是同步执行的广播,并且可以被截断,
接下来我们创建一个新的BroadcastReceiver.
class AnotherBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(
context, "received in another AnotherBroadcastReceiver",
Toast.LENGTH_LONG
).show()
}
}
很简单,这里仍然是弹出一段文本信息.
接下来对AndroidManifest.xml中BroadcastReceiver配置进行修改:
可以看到,AnotherBroadcastReceiver同样接收的是
com.example.broadcasttest.MY_BROADCAST这条广播。
现在运行这个程序,然 后点击一下发送按钮,就会分别弹出两次提示信息.如图:
不过到目前位置,我们发送的都是标准广播,
接下来我们来发送有序广播,修改MainActivity中的代码:
button.setOnClickListener {
val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
intent.setPackage(packageName)
sendOrderedBroadcast(intent, null)
}
可以看到,发送有序广播只需要改动一行代码,
即将sendBroadcast()方法改成send-OrderedBroadcast()方法。
sendOrderedBroadcast()方法接收两个参数,
第一个参数仍然是 Intent,第二个参数是一个与权限相关的字符串,
这里传入null就行了。现在重新运行程序,
并点击Send Broadcast按钮,你会发现,两个应用程序仍然都可以接收到这条广播。
看上去好像和标准广播没什么区别嘛,不过别忘了,
这个时候的广播接收器是有先后顺序的,
而且前面的广播接收器还可以将广播截断,以阻止其继续传播。
那么该如何设定广播接收器的先后顺序呢?
当然是在注册的时候进行设定的了,修改AndroidManifest.xml 中的代码,如下所示:
可以看到,我们通过android:priority属性给广播接收器设置了优先级,
优先级比较高的广播接收器就可以先收到广播。
这里将MyBroadcastReceiver的优先级设成了100,以保证它一定会在AnotherBroadcastReceiver之前收到广播。
既然已经获得了接收广播的优先权,
那么MyBroadcastReceiver就可以选择是否允许广播继续传递了。
修改MyBroadcastReceiver中的代码,如下所示:
如果在onReceive()方法中调用了abortBroadcast()方法,就表示将这条广播截断,
后面的广播接收器将无法再接收到这条广播。现在重新运行程序,
并点击一下Send Broadcast按钮,你会发现,
只有MyBroadcastReceiver中的Toast信息能够弹出,
说明这条广播经过 MyBroadcastReceiver之后确实是终止传递了。
近期由于一个项目的需要,对Flash版本的播放器JWPlayer做了一些改进以支持一些功能,这里把中间用到的一些思路和做法记录下。首先一个功能是:客户的很多flv视频都是完整的一个大视频,希望JWPlayer可以对flv视频文件播放时不全部载入,而是先只载入前面5分钟,等播放到4分钟左右时再去载入后面的5分钟视频。这样的好处显而易见就是可以节省很多服务器流量,避免用户只看2-3分钟暂停在那,也把..._视频播放器 节约带宽
这几天搞pop3接受邮件,现在困在邮件解码这里,不过还顺便总结下心得(1) POP3命令由一个命令和一些参数组成。所有命令以一个CRLF对结束 (2) 命令和参数由可打印的ASCII字符组成,它们之间由空格间隔 如user username(注意,连上去后,有时要大写USER,有时要小写user)(3) POP3响应由一个状态码("确定" ("+OK")和"失败" ("-ERR")) 和一个可能跟_pop3协议原理
[quote][b]对一项管理策略进行的测试……这不在于答案的对错,而要看它是否行之有效。[/b]通用公司的经理人以为,他们发现了管理的原则。这些原则好比铁律,是毋庸置疑的。但在我看来,人们创造出原则一类的东西,充其量是只能作为能给人带来启发的观点。在这一点上,我的管理学研究方法和许多同学科的作家或理论家不同——原因也许就在于我在学术界算不上声名显赫吧。我坚信根本价值观念的存在,特别是...
1.有100个人围成一个圈,从1开始报数,报到14的这个人就要退出。然后其他人重新开始,从1报数,到14退出。 问:最后剩下的是100人中的第几个人?import java.util.LinkedList;import java.util.List;public class Test10 { public static void main(String[] args) {
结构体1、最正规定义方式:定义结构体data,此时结构体相当于一个类型,比如int,如需使用此结构体,方法同intstruct data char a; int d;}; struct data B2、定义结构体data同时,定义需要使用的结构体变量sum1,sum2。如后面再需要定义结构体变量,方法同1struct data{char a; ..._类中定义结构体
一、今日内容一、Django ORM连表操作Q,F二、Form表单验证 面向对象 正则三、Session框架 面向对象 cookie toanado扩展二、Django ORM一对多数据创建和查找1、数据创建对于不是外键关联的表,可以直接创建对于通过外键关联的表,可以通过两种方式创建数据: 方式a:...
原来听框架,觉得是一种很玄,很抽象的东西。_ef view(model)
Arduino+nRF24L01多按键远程无线控制(发射端)采用的是arduino nano注意ce和csn引脚,在使用nano开发板时,接7和8引脚。本文只添加了2个按钮控制发射端2路io口信号的触发,想要增加按键只需依次添加即可,添加的按钮控制越多,对于程序运行速度会有所影响,程序采用的是枚举扫描方式来检测按键是否按下。发射端代码:#include <SPI.h>#include <nRF24L01.h>#include <RF24.h>#defin_arduino nano和24l01 控制电调
objectBaiWordCount2{defmain(args:Array[String]){.....//Createthecontextvalssc=newSparkContext(args(0),"BaiWordCount",System.getenv("SPARK_HOME"),Seq(System.getenv("SPARK_EXAMPLES_JAR")))vallines=ssc.text..._mappedrdd、mappartitionsrdd、shuffledrdd
List of Headings题也叫做小标题题,顾名思义就是为文章中每个段落选择合适的能够概括段落大意的小标题来。这种题型在雅思阅读考试中经常出现。然而这种题型却往往因为解题方法的错误被误认为是难题。其实,只要掌握正确的解题方法,该种题型一定会变得很简单并且可以全部做对。大多数考生认为这种题型较难,因为他们采用了如下错误的解题思路:第一,大多数考生解答这种题型的思路在于寻找段落的_list of headings
Javascript 面试题连等赋值 a.x = a = {n: 2}var a = {n: 1}var b = a;a.x = a = {m: 2}// 问a, b是什么?这个题可能考察的知识点有以下几个Javascript中object属于哪种数据类型 object类型数据的存储方式 Javascript中运算符的优先级 Javascript编译器关于赋值运算的知识...
Shader "Custom/WaterBlur" { Properties { _blurSizeXY("BlurSizeXY", Range(0,10)) = 2} SubShader { // Draw ourselves after all opaque geometry Tags { "Queue" = "Transparent" }_glsl毛玻璃shader