第一行代码 第三版 第六章 广播

第 6 章 广播

6.1 广播机制

类型:

  • 标准广播:广播发出所有的接收器同时接收
  • 有序广播:广播发出后,按照优先级先后接收,先接收的可以截断广播

6.2 接收系统广播

如:开机广播、电量变化广播、系统时间变化广播

6.2.1 动态注册监听时间变化

class MainActivity : AppCompatActivity() {

    lateinit var timeChangeReceiver: BroadcastReceiver

    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(p0: Context?, p1: Intent?) {
            Toast.makeText(p0, "Time has changed", Toast.LENGTH_SHORT).show()
        }
    }
}

完整的系统广播列表 SDK路径/platforms/<任意版本的 api>/data/broadcast_action.txt

动态注册缺点:应用启动后才能接收

6.2.2 静态注册的广播实现开机

安卓8.0后所有隐式广播不允许使用静态注册的方式来接收,特殊类型的广播除外

  1. new - other - BroadcastReceiver
class BootCompleteReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show()
    }
}
  1. 配置清单文件
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>

<uses-permission android:name="android.permission.RESTART_PACKAGES" />


 <receiver
            android:name=".BootCompleteReceiver"
            android:enabled="true"//表示此广播接收本程序以外的广播
            android:exported="true">//表示启用这个广播

            <intent-filter
                >
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
            //因为在Android studio3.4.1的程序是安装在sd卡上的,在android.intent.action.BOOT_COMPLETED的广播发送之后,sd卡才被挂载,所以广播接收器接收不到该广播
            <intent-filter >   //监听sd卡挂载
                <action android:name="android.intent.action.MEDIA_MOUNTED"/>
                <action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
                <data android:scheme="file">
                </data>
            </intent-filter>
        </receiver>
  1. 重启手机,需要等一会儿

6.3 发送自定义广播

6.3.1 发送标准广播

  1. new 一个广播
class MyBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show()
    }
}
  1. 配置清单文件
<receiver
    android:name=".MyBroadcastReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.broadcasttest.MY_BROADCAST"></action>
    </intent-filter>
</receiver>
  1. 发送广播

注:安卓8.0后静态注册的广播无法接收隐式广播,需要传入包名指定特定的程序接收把他变成一条显式广播

补充:可以在intent里传递一些数据

button.setOnClickListener{
    val intent = Intent("com.example.broadcasttest.MY_BROADCAST")//指定发送的广播类型
    intent.setPackage(packageName)
    sendBroadcast(intent)
}

6.3.2 发送有序广播

  1. new 另一个广播
class AnotherBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show()
    }
}
  1. 配置清单文件,指定接收的广播类型,同上面那个
  2. 发送有序广播
button.setOnClickListener{
    val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
    intent.setPackage(packageName)
    sendOrderedBroadcast(intent,null)
}
  1. 设置优先级
<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>
  1. 截断广播
class MyBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show()
        abortBroadcast()
    }
}

注:广播的onReceive方法不能添加过多的逻辑或进行任何耗时操作,因为广播不允许开启线程,当进行太长时间而没有结束就会出错

6.4 广播的最佳实践:强制下线功能

  1. 关闭所有活动功能创建 ActivityConllector类和BaseActivity类
  2. 创建登录布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textSize="18sp"
            android:text="Account:" />

        <EditText
            android:id="@+id/accountEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical" />
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textSize="18sp"
            android:text="Password:" />

        <EditText
            android:id="@+id/passwordEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical"
            android:inputType="textPassword" />
    </LinearLayout>

    <Button
        android:id="@+id/login"
        android:layout_width="200dp"
        android:layout_height="60dp"
        android:layout_gravity="center_horizontal"
        android:text="Login" />

</LinearLayout>
  1. 登录代码
class LoginActivity : BaseActivity(){
    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 == "123456"){
                val intent = Intent(this, MainActivity::class.java)
                startActivity(intent)
                finish()
            }else{
                Toast.makeText(this,"account or password is invalid",Toast.LENGTH_SHORT).show()
            }
        }
    }
}
  1. 修改主活动代码,触发强制下线功能
class MainActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        forceOffline.setOnClickListener {
            val intent = Intent("com.example.broadcastbestpractice.FORCE_OFFLINE")
            sendBroadcast(intent)
        }
    }
}
  1. 修改BaseActivity
open class BaseActivity : AppCompatActivity() {

    lateinit var receiver: ForceOfflineReceiver

    override fun onResume() {
        super.onResume()
        val intentFilter = IntentFilter()
        intentFilter.addAction("com.example.broadcastbestpractice.FORCE_OFFLINE")
        receiver = ForceOfflineReceiver()
        registerReceiver(receiver,intentFilter)
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(receiver)
    }

    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        ActivityCollector.addActivity(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        ActivityCollector.removeActivity(this)
    }

    inner class ForceOfflineReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, p1: Intent) {
            AlertDialog.Builder(context).apply {
                setTitle("Warning")
                setMessage("You are forced to be offline. Please try to login again.")
                setCancelable(false)
                setPositiveButton("OK"){_,_->
                    ActivityCollector.finishAll()
                    val i = Intent(context, LoginActivity::class.java)
                    context.startActivity(i)
                }
                show()
            }
        }

    }
}

注:如果没有取消注册会报错

  1. 更换LoginActivity为主活动

6.5 Kotlin 课堂

6.5.1 定义高阶函数:函数的参数是个函数或返回值是个函数,类似于java中的接口

  • 函数类型:(String , Int) -> Unit 左边是参数右边是返回值,Unit说明返回值为空
fun num1AndNum2(num1: Int,num2: Int,operation:(Int,Int)->Int):Int{
    val result = operation(num1,num2)
    return result
}

fun plus(num1: Int, num2: Int):Int {
    return num1 + num2
}

fun minus(num1: Int, num2: Int):Int {
    return num1 - num2
}

fun main() {
    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1,num2,::plus)//引用函数的写法 表示获得函数类型的对象
    val result2 = num1AndNum2(num1,num2,::minus)
    println("result1 is $result1")
    println("result2 is $result2")
}
  • 利用Lambda优化
    • 语义结构:{参数名 1:参数类型,参数名2:参数类型 -> 函数体}
      • -> 表示参数列表结束和函数体的开始,函数体最后一行作为返回值
    • 当 Lambda 是函数的最后一个参数,可以移到括号外面
    • Lambda 有优秀的类型推到机制,所以可以不用写参数类型
fun num1AndNum2(num1: Int,num2: Int,operation:(Int,Int)->Int):Int{
    val result = operation(num1,num2)
    return result
}


fun main() {
    val num1 = 100
    val num2 = 80
    val result1 = num1AndNum2(num1,num2){ n1,n2 -> n1+n2}
    val result2 = num1AndNum2(num1,num2){ n1,n2 -> n1-n2}
    println("result1 is $result1")
    println("result2 is $result2")
}
  • 利用高阶函数实现一个StringBuilder用apply的功能
fun StringBuilder.build(block: StringBuilder.() -> Unit):StringBuilder{
    block()
    return this
}
//StringBuilder.表示在这个类里声明的函数,然后调用函数时就会自动拥有StringBuilder的上下文

fun main() {
    val list = listOf("Apple","Banana","Orange","Pear","Grape")
    val result = StringBuilder().build{
        append("Start eating fruits.\n")
        for (fruit in list) {
            append(fruit).append("\n")
        }
        append("Ate all fruits.")
    }
    println(result.toString())
}

缺点:这种写法在编译的时候每个Lambda表达式都会转换为java的接口形式,调用的时候会创建接口的匿名实现,会占用很多空间,下面是解决办法

6.5.2 内联函数的作用

在代码编译的时候自动替换到调用它的地方

在高阶函数前面加上 inline 关键字就行

相当于c++里面的宏定义#define

6.5.3 noinline 与 crossinline 暂时不会

6.6 Git

6.6.1 安装 Git

mac电脑 在安卓Homebrew的基础上,在命令行里敲:brew install git

6.6.2 创建代码仓库

//配置身份
git config --global user.name "自己写"
git config --global user.email "自己写"

//查看是否配置成功
git config --global user.name
git config --global user.email

//创建代码仓库
git init

//然后在这个目录下会自动创建.git文件:用来保存本地所有Git操作
//查看
ls -al

//如果想删除仓库,删除这个目录就行

6.6.3 提交本地代码

//把想提交的代码添加进来
git add 文件名/目录名/.所有文件

//真正执行提交操作
git commit -m "描述信息"
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小蒋的学习笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值