第六章 广播
-
广播分类
-
标准广播
异步执行的广播,在广播发出后,所有BroadcastReceiver几乎在同一时刻接收到这条广播。
-
有序广播
同步执行的广播,在广播发出后,同一时刻只会有一个BroadcastReceiver能够接收到这条广播。当这个BroadcastReceiver中的逻辑执行完毕后,广播才会继续传递。
-
-
接收广播
-
注册方式
-
动态注册,在代码中注册
-
静态注册,
AndroidManifest.xml中注册
-
-
动态注册
class TimeChangeReceiverActivity : AppCompatActivity() { private lateinit var timeChangedReceiver: TimeChangedReceiver override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_time_change_receiver) timeChangedReceiver = TimeChangedReceiver() val intentFilter = IntentFilter() intentFilter.addAction("android.intent.action.TIME_TICK") registerReceiver(timeChangedReceiver, intentFilter) } override fun onDestroy() { super.onDestroy() unregisterReceiver(timeChangedReceiver) } class TimeChangedReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { Toast.makeText(context, "Time has changed", Toast.LENGTH_SHORT).show() } } }⚠️动态注册的BroadcastReceiver一定要取消注册。否则会内存泄漏。
-
静态注册
Android8.0之后所有隐式广播都不允许静态注册。隐式广播是指那些没有具体指定发送给哪个应用程序的广播。-
New->Other->BroadcastReceiver创建BroadcastReceiver文件。
class BootCompletedReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // This method is called when the BroadcastReceiver is receiving an Intent broadcast. TODO("BootCompletedReceiver.onReceive() is not implemented") } } -
AndroidManifest.xml文件注册
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.youngly.firstlineofcode"> <!-- 权限声明 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.FirstLineOfCode"> <!-- enabled 是否启用 exported 是否允许接收本程序外的广播 --> <receiver android:name=".chapter6.BootCompletedReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> </intent-filter> </receiver> ... </application> </manifest>
⚠️不要在onReceive()方法中执行耗时操作,BroadcastReceiver不允许开启线程。
-
-
-
发送自定义标准广播
<receiver android:name=".chapter6.standardbroadcast.MyStandardReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.youngly.firstlineofcode.MyStandardReceiver" /> </intent-filter> </receiver>class MyStandardReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context, "received in MyStandardReceiver", Toast.LENGTH_SHORT).show() } }fun myStandardBroadcastReceiver(view: View) { val intent = Intent("com.youngly.firstlineofcode.MyStandardReceiver") intent.`package` = packageName sendBroadcast(intent) }⚠️Android 8.0系统之后,静态注册的BroadcastReceiver是无法接收隐式广播的,而默认情况下我们发出的自定义广播恰恰都是自定义广播。因此这里调用setPackage()方法,指定这条广播是发送给哪个应用程序的,从而使它变成一条显式广播。
-
发送有序广播
<receiver android:name=".chapter6.OrderBroadcast.OrderReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="1000"> <action android:name="com.youngly.firstlineofcode.MyStandardReceiver" /> </intent-filter> </receiver>val intent = Intent("com.youngly.firstlineofcode.MyStandardReceiver") intent.`package` = packageName sendOrderedBroadcast(intent, null)class OrderReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // This method is called when the BroadcastReceiver is receiving an Intent broadcast. Toast.makeText(context, "received in OrderReceiver", Toast.LENGTH_SHORT).show() abortBroadcast() } }android:priority设置优先级sendOrderedBroadcast(intent, null)发送有序广播abortBroadcast()截断广播
-
Kotlin课堂
-
高阶函数
如果一个函数接收另外一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数。
函数类型的定义:
(String, Int) -> Unit函数类型最关键的是声明该函数接收的
参数和返回值。->左边是函数参数,多个参数用,分隔。如果不接收任何参数,写一对空括号即可。->右边部分用于声明函数的返回值类型。fun example(func: (String, Int) -> Unit) { }上例中,example()函数就是高阶函数,它接收一个函数作为参数
fun main() { println(operatorInts(1, 2, ::plus)) } fun operatorInts(a: Int, b: Int, operator: (Int, Int) -> Int): Int { return operator(a, b) } fun plus(a: Int, b: Int): Int { return a + b }::plus函数引用方式的写法,表示将函数作为参数传递给函数。 -
inline 内联函数
-
高阶函数原理解析:
fun main() { operatorInts(4, 2) { a, b -> a / b } } fun operatorInts(a: Int, b: Int, operator: (Int, Int) -> Int): Int { return operator(a, b) }上述代码调用operatorInts()函数,并通过Lambda表达式指定对传入的两个整形参数进行求商操作。Kotlin代码最终还是编译成Java字节码,但Java中没有高阶函数的概念。
原理是Java编译器将高阶函数的语法转化成Java支持的语法结构。
public class HighOrderFuncTest { public static void main(String[] args) { int result = operatorIntegers(4, 2, new Function2<Integer, Integer, Integer>() { @Override public Integer invoke(Integer integer, Integer integer2) { return integer / integer2; } }); } public static int operatorIntegers(int a, int b, Function2<Integer, Integer, Integer> operator) { return operator.invoke(a, b); } }在这里Lambda表达式变成了Function2接口,这是Kotlin内置的接口,里面有一个待实现的invoke()函数。
这里会发现我们一直使用的Lambda表达式在底层被转换成了匿名内部类的实现方式。这就表明我们每调用一次Lambda表达式,都会创建一个新的匿名内部类实例。当然也会造成额外的内存和性能开销。
Kotlin提供了内联函数的功能,它可以将Lambda表达式带来的运行时开销完全消除。
-
内联函数的用法
inline fun operatorInts(a: Int, b: Int, operator: (Int, Int) -> Int): Int { return operator(a, b) }用法很简单,只需要在定义高阶函数时加上
inline关键字声明即可 -
内联函数工作原理
fun main() { val a = 4 val b = 2 a / b }相当于将operatorInts()函数平铺开来
-
-
noinline
一个高阶函数如果接收两个或者更多函数类型的参数,这时我们需要给函数加上inline关键字,那么Kotlin编译器自动将所有引用的Lambda表达式全部进行内联。
这时我们只想内联其中一个Lambda表达式时,可以使用noinline关键字
inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit) { }因为内联的函数类型参数在编译的时候被代码替换,因此它没有真正的属性参数
inline fun inlineTest(block1: () -> Unit, block2: () -> Unit): () -> Unit { // 编译报错,block2会被代码替换,不再是函数类型 return block2 }// 添加noinline关键字,编译👌 inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit): () -> Unit { return block2 } -
crossinline
看下下方代码:
fun main() { println("main start") inLineTest { println("lambda run") return } println("main end") } inline fun inLineTest(block: () -> Unit) { println("out method start") block() println("out method end") }实际输出:
main start out method start lambda run因为之前inline的原理中已经说明,相当于将函数平铺,所以此处return,是直接return的
main()函数fun main() { println("main start") noinlineTest({ -> print("") }, { -> print("noinline lambda run") // 此处return编译报错 return }) println("main end") } inline fun noinlineTest(block: () -> Unit, noinline block2: () -> Unit) { println("out method start") block2() println("out method end") }为何上面return会编译报错?
在上例inline的return中,return返回的是最外层的调用,这就导致一个问题,内联函数的return会结束最外层函数的调用,当使用return时需要确认是否是内联函数,所以Kotlin规定只有内联函数所引用的Lambda表达式才可以使用return关键字来进行函数返回
那上面例子有办法使用return返回吗?可以使用
return@函数名进行局部返回,修改如下fun main() { println("main start") noinlineTest({ -> print("") }, { -> print("noinline lambda run") return@noinlineTest }) println("main end") } inline fun noinlineTest(block: () -> Unit, noinline block2: () -> Unit) { println("out method start") block2() println("out method end") }实际输出:
main start out method start noinline lambda runout method end main end查看下方示例:
inline fun inlineWithRunable(block: () -> Unit) { Runnable() { // 编译报错 block() } }内联函数引用的Lambda表达式允许使用
return关键字进行返回,但是由于我们在匿名类中使用,此时不可能进行最外层调用函数返回了,只能对匿名类中的函数进行返回,可以添加crossinline关键字,保证内联函数引用的Lambda中不使用return关键字进行最外层函数返回,但可以使用return@内联函数名进行局部返回。inline fun inlineWithRunable(crossinline block: () -> Unit) { Runnable() { block() } }
-