目录
第六章
6.1广播
广播类型:标准广播和有序广播。
标准广播:无序、无法被截断、效率高;
有序广播:有序、可以被截断(可以设置优先级)
6.2接收系统广播
注册BroadcastReceiver方式:动态注册(在代码中)、静态注册(在AndroidManifest.xml中)
6.2.1动态注册
动态广播必须在程序启动之后。
新建一个类让其继承自BroadcastReceiver,并重写父类的onReceive()方法,在onCreate方法中创建IntentFilter实例,并添加一个为android.intent.action.TIME_TICK的action,接着创建一个TimeChangeReceiver的实例,调用registerReceiver方法将TimeChangeReceiver以及IntentFilter传入,最后在onDestroy方法中调用unregisterReceiver方法取消注册。
查看完整的系统广播列表可到如下路径查看:
<Android SDK>/platforms/<任意android api版本>/data/broadcast_actions.txt
class MainActivity : AppCompatActivity() {
lateinit var timeChangeReceiver: TimeChangeReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 创建IntentFilter实例
val intentFilter = IntentFilter()
// 添加一个action
// android.intent.action.TIME_TICK为系统时间变化时发出的广播
intentFilter.addAction("android.intent.action.TIME_TICK")
// 创建一个TimeChangeReceiver实例
timeChangeReceiver = TimeChangeReceiver()
// 用registerReceiver方法将TimeChangeReceiver与intentFilter传入
registerReceiver(timeChangeReceiver, intentFilter)
}
//动态注册的广播一定要取消注册!
override fun onDestroy() {
super.onDestroy()
// 在onDestroy中调用unregisterReceiver方法
unregisterReceiver(timeChangeReceiver)
}
// 新建一个类继承自BroadcastReceiver并重写onReceive方法
inner class TimeChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// 当收到广播时显示toast消息
Toast.makeText(context, "Time has changed", Toast.LENGTH_SHORT).show()
}
}
}
6.2.2静态注册
在Android 8.0系统之后,所有隐式广播都不允许使用静态注册的方式来接收。隐式广播指的 是那些没有具体指定发送给哪个应用程序的广播。
少数特殊的系统广播目前仍然允许使用静态注册的方式来接收。这些特殊的系统广播列表详见 https://developer.android.google.cn/guide/components/broadcast-exceptions.html
在清单文件中的application 标签下进行注册,并在<uses-permission>标签声明对应的权限。
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true" // 表示是否启用这个BroadcastReceiver
android:exported="true"> // 表示是否允BroadcastReceiver接收本程序外的广播
<intent-filter>
// 添加一个广播
// android.intent.action.BOOT_COMPLETED为一条开机广播
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
尽量不要在onReceive()方法中添加过多的逻辑或者进行任何的耗时操作,因为BroadcastReceiver是不允许开启线程的。
6.3发送自定义广播
广播分为标准广播和有序广播
6.3.1发送标准广播
在清单文件中进行注册要接收的广播,然后构建一个intent对象,传入要发送的广播的值。由于在Android8.0之后,静态注册的BroadcastReceiver是无法接收隐式广播,因此这里一定要调用setPackage()方法,指定这条广播是发送给哪个应用程序的,从而让它变为一条显式广播。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
// 构建一个Intent对象,将要发送广播的值传入
val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
intent.setPackage(packageName)
// 调用sendBroadcast将广播发送出去
sendBroadcast(intent)
}
}
6.3.2发送有序广播
发送有序广播只需将sendBroadcast()方法改成sendOrderedBroadcast(),sendOrderedBroadcast()接收两个参数,第一个参数为Intent,第二个参数是一个与权限相关的字符串。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button.setOnClickListener {
val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
intent.setPackage(packageName)
sendOrderedBroadcast(intent, null)
}
}
通过android:priority属性给BroadcastReceiver设置优先级,优先级越高可以先接收到广播。
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.example.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
调用abortBroadcast()方法,表示将这条广播截断。
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "received in MyBroadcastReceiver",
Toast.LENGTH_SHORT).show()
// 表示将广播截断
abortBroadcast()
}
}
6.4高阶函数
6.4.1定义高阶函数
高阶函数定义:如果一个函数接收另一个函数作为参数,或者返回值类型是另一个函数那么该函数就称为高阶函数。
基本规则如下:
(String, Int) -> Unit
->左边的部分是用来声明该函数接收什么参数,多个参数之间用逗号隔开,如果不接收任何参数,写一对空括号即可。而->右边的部分用于声明该函数的返回值是什么类型,如果没有返回值就用Unit,大致相当于Java中的void。
将上述函数类型添加到某个函数声明或者返回值声明上,那么该函数就是一个高阶函数。
如下的example函数即为一个高阶函数:
fun example(func: (String, Int) -> Unit) {
func("hello", 123)
}
调用示例:
1、传递一个具名函数作为参数
fun myFunction(str: String, num: Int) {
println("String: $str, Number: $num")
}
example(::myFunction) //输出结果为:String: hello, Number: 123
2、使用Lambda表达式作为参数
example { str, num ->
println("Received parameters: $str, $num")
}
3、使用匿名函数作为参数
example(fun(str: String, num: Int) {
println("Received parameters: $str, $num")
})
6.4.2内联函数
内联函数是一种在编译期将函数调用替换为函数体的特殊函数,使用内联可以避免函数调用的开销,提高代码执行效率。在定义的高阶函数加上inline关键字。
6.4.3noinline与crossinline
一个高阶函数如果接收两个或者更多函数类型的参数,这时kotlin编译器会自动将所有引用的Lambda表达式全部进行内联,如果只想内联其中一个Lambda表达式就可以使用noinline关键字。
inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit) {
}
在默认情况下,Lambda表达式内部的语句会直接从包含它的函数中返回,但有时我们可能需要在Lambda表达式中使用return语句,而不会影响到包含它的函数的返回。这时就可以使用crossinline关键字来修饰Lambda参数。这告诉编译器,即使Lambda表达式中使用了return语句,也不能让函数提前返回,从而可以确保函数的执行顺序和预期一致。
例如使用如下写法会报错:
inline fun runRunnable(block: () -> Unit) {
val runnable = Runnable {
block()
}
runnable.run()
}
改为使用crossinline即可:
inline fun runRunnable(crossinline block: () -> Unit) {
val runnable = Runnable {
block()
}
runnable.run()
}