一、注册广播监听
1、动态注册
- 建立广播的监听类
- 建立过滤器
- 在过滤器中添加监听的动作
- 注册广播
- 取消注册
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)//一定要取消注册
}
//接收广播的类,需要继承BroadcastReceiver
inner class TimeChangeReceiver:BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context,"Time has changed", Toast.LENGTH_SHORT).show()
}
}
2、静态注册
- 建立广播的监听类
- 在
Manifest.xml
注册广播监听
如果使用如图方式添加广播类,会自动注册 - 在
Manifest.xml
注册广播动作
<receiver
android:name=".BootCompleteReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
- 某些操作(如监听开机)需要授予权限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
二、发送自定义广播
1、发送标准广播
val intent = Intent("com.lrgy.broadcasttest.MY_BROADCAST")//构建包含对应广播的Intent对象
intent.setPackage(packageName)//对于静态注册的隐式广播,一定要调用该方法以确定作用的应用程序,使他成为一条显式广播
sendBroadcast(intent)//发送广播
2、发送有序广播
有序和无序的差别其实不大只需要修改以下几点:
- 发送广播时使用
sendOrderedBroadcast(intent, null)//发送广播
- 在广播注册时指定优先级,优先级高的先接收到广播
android:priority="200"
<receiver
android:name=".AnotherBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="200">
<action android:name="com.lrgy.broadcasttest.MY_BROADCAST"/>
</intent-filter>
</receiver>
- 可以在广播中截断广播的传递
abortBroadcast()
三、广播的最佳实践——强制下线功能
分析:强制下线需要在任何一个页面都实现:收到下线命令,即刻下线的功能。因此为每个activity都写这个功能并不合适,所以需要使用广播。
思路:收到下线广播——触发对应receiver——关闭所有activity——启动登录界面activity
- 建立ActivityCollector方便管理所有的Activity
//单例类,伪静态方法
object ActivityCollector {
private val activities = ArrayList<Activity>()
fun addActivity(activity: Activity){
activities.add(activity)
}
fun removeActivity(activity: Activity){
activities.remove(activity)
}
fun finishAll(){
//finish所有activity
for (activity in activities){
if (!activity.isFinishing){
activity.finish()
}
}
activities.clear()//将集合中的所有activity元素清空
}
}
- 建立BaseActivity类,使得所有activity都可以被ActivityCollector控制
/*该项目中所有Activity的父类*/
//添加open才可以被继承
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("BaseActivity", javaClass.simpleName)//javaClass相当于java中的getClass方法,获取当前类的对象
ActivityCollector.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
ActivityCollector.removeActivity(this)
}
}
- 在LoginActivity中添加登录验证
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
login.setOnClickListener {
val account = accountEdit.text.toString()
val password = passwordEdit.text.toString()
if (account == "admin" && password == "123"){
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
}else{
Toast.makeText(this, "error", Toast.LENGTH_SHORT).show()
}
}
}
- 在MainActivity中添加触发下线的功能
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
forceOffLine.setOnClickListener {
val intent = Intent("com.lrgy.boardcastbestpractice.FORCE_OFFLINE")
sendBroadcast(intent)
}
}
- 在BaseActivity中可以为每个Activity注册和取消注册广播
特别地,这里使用了内部类进行动态绑定
//自定义广播的对象
lateinit var receiver: ForceOfflineReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("BaseActivity", javaClass.simpleName)//javaClass相当于java中的getClass方法,获取当前类的对象
ActivityCollector.addActivity(this)
}
override fun onResume() {
super.onResume()
val intentFilter = IntentFilter()//广播过滤器
intentFilter.addAction("com.lrgy.boardcastbestpractice.FORCE_OFFLINE")
receiver = ForceOfflineReceiver()
//这里在onResume里注册的原因是:避免非栈顶元素有广播的注册,发生WindowManager$BadTokenException: Unable to add window异常
registerReceiver(receiver,intentFilter)//注册广播绑定
}
override fun onPause() {
super.onPause()
unregisterReceiver(receiver)//!!!!!注意一定要在onPause里取消注册
}
override fun onDestroy() {
super.onDestroy()
ActivityCollector.removeActivity(this)
}
inner class ForceOfflineReceiver : BroadcastReceiver(){
override fun onReceive(context: Context, intent: Intent) {
AlertDialog.Builder(context).apply {
setTitle("Warning")
setMessage("you will offline")
setCancelable(false)//设置为不可取消
setPositiveButton("OK"){ _, _ ->//省略了参数:dialog, which
ActivityCollector.finishAll()//销毁所有activity
//启动LoginActivity
val i = Intent(context, LoginActivity::class.java)
context.startActivity(i)
}
show()
}
}
}
- 别忘了在Mainifest中修改第一个页面
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".MainActivity">
</activity>
四、kotlin课堂
1、高阶函数
- 创建方式
fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int{
val result = operation(num1, num2)
return result
}
- 调用方法
可以使用引用函数的方式
//【::函数名】 可以引用函数
val result1 = num1AndNum2(num1, num2, ::plus)
fun plus(num1: Int, num2: Int): Int{
return num1 + num2
}
也可以使用lamda表达式
val result2 = num1AndNum2(num1, num2){ n1, n2 ->
n1 + n2
}
- 使用内联函数(inline关键字)可以消除运行时带来的额外开销
inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int{
val result = operation(num1, num2)
return result
}
如果只想将其中一个高阶函数变成内联函数,可以使用noinline关键字
inline fun inlineTest(block1: () -> Unit, noinline block2: () -> Unit){
}
- 内联函数和非内联函数的区别
除了可以消除运行时带来的额外开销之外,内联函数中是可以使用retrun关键字来返回的,但是非内联函数只能进行局部返回。(return@函数名)