首页 > 技术文章 > Android 广播的使用 文档使用的是 kotlin版本

inthecloud 2020-08-09 20:28 原文

标准广播 

 标准广播 (normal broadcasts)是一个完全异步的广播,在广播发出后,所有的广播接收器几乎在同一时刻接收到消息。因为他们没有任何先后之分。这种广播效率高,但同时也是无法阻断的。

有序广播

  有序广播(ordered broadcasts)则是一种同步执行的广播 ,在广播发出后同一时刻会有一个广播接收器能够接收到这条广播。当这个接收器的逻辑执行完毕才会继续传递。此刻的广播是有先后顺序,优先级高的接收器可以先接收到消息。并且可以截断正在传递的广播。相对会安全些。

接收系统广播

Android中内置了很多系统级的广播,我们可以通过监听这些广播来获取手机的状态信息。来看看怎么使用吧

动态注册监听时间的变化

开始代码部分,注意一下在使用 inner的时候会有一行 TODO("Not yet implemented") 记得把它删了,否则会报错。

这里我们在MainActivty中定义了一个TimeChangeReceiver的内部类。这个类继承了一个广播,并重写了onReceive方法。每当时间发生变化都会得到执行,系统每分钟发出一个这样的广播。

观察onCreat方法。我们班创建了一个IntentFilter的实例。给它添加了一个值 android.intent.action.TIME_TICK 这个值是系统时间发生改变时,发出的广播的值就是它。也就是说我们想监听什么广播,就在这里添加对应的action。

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(p0: Context?, p1: Intent?) {
            Toast.makeText(p0,"时间发生改变",Toast.LENGTH_LONG).show()
        }
    }
}

 

 静态注册开机启动

 动态注册可以自由控制注册和注销,有很大的灵活性,但是只能在程序启动时才可以接收广播。因为逻辑是写在onCreate中,如果想在程序未启动前接收广播就要用到静态注册了。

在Android8.0之后所有的隐式广播都不允许使用静态注册方式来接收了,隐式广播值得是那些没有具体指定发送给哪个应用程序的广播,大多数系统广播都属于隐式广播。但是仍有部分特殊的系统广播是可以用隐式广播注册方式接收。详情可看连接

 

建一个广播,命名MyReceiver,修改代码如下:

class MyReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,"系统开机",Toast.LENGTH_SHORT).show()
    }
}

随后打开AndroidManifest.xml文件,修改代码:

<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/AppTheme">
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
<!--添加intente-file如果你是用as创建的广播,那外面一层应该会自己添加-->
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

编辑好所有代码后重新运行,然后重启模拟器,就会接收到一个弹窗提升,不要问我为什么用模拟器,因为我用了三台真机都看不到这个toast 

    

真机需要设置电池不优化   

 

发送自定义广播

标准广播

创建一个 广播接收器,用于接收用户发出的广播,当接收到广播时,弹出一个Toast

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(p0: Context?, p1: Intent?) {
        Toast.makeText(p0,"接收到一个自定义广播",Toast.LENGTH_SHORT).show()
    }
}

在AndroidManifest中做出如下修改,在静态注册的广播注册这里我们添加一个 intent-filter,接收一条值为 com.example.broadcasttest.MY_BROADCAST 的广播。

        <receiver android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.broadcasttest.MY_BROADCAST"/>
            </intent-filter>
        </receiver>

 

在android_main.xml中添加一个按钮

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn"
        android:text="发送广播"
        tools:ignore="MissingConstraints" />

最后,在MainActiyty.kt中写它的点击事件,代码很简单

构建一个 Intent,传入一个广播值 com.example.broadcasttest.MY_BROADCAST

调用 Intent 的setPackage()方法,传入当前应用的包名

最后调用 setBroadcast将广播发送出去。

 

之所以需要调用 setPackage()方法,是因为Android 8之后,静态注册的广播无法接收隐式广播,而我们发送的广播默认情况下又都是隐式广播

,因此一定要调用setPackage方法,指定这是哪个程序发出的。

        btn.setOnClickListener {
            val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
            intent.setPackage(packageName)
            sendBroadcast(intent)
        }

运行项目,点击发送广播按钮,效果如下:

 

 

有序广播

发送有序广播

前面说过,有序广播是同步执行的广播,且可以截断。我们来验证一下,创建一个 MyBroadcastReceiver2

编辑如下代码:

class MyBroadcastReceiver2 : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,"这是第二个广播接收器 接收到的自定义广播", Toast.LENGTH_SHORT).show()
    }
}

在AndroidManifest和刚才的步骤一样,注册一个监听对象

        <receiver
            android:name=".MyBroadcastReceiver2"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.broadcasttest.MY_BROADCAST" />
            </intent-filter>
        </receiver>

好,然后来运行一下

    

 

不过到现在我们发出的都是标准广播,现在我们来尝试发一个有序广播。

在MainActivty中修改代码

        btn.setOnClickListener {
            val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
            intent.setPackage(packageName)
            sendOrderedBroadcast(intent,null) 
}

可以看到,发送有序广播只用改变一行代码,将 sendBroadcast方法改为 sendOrderedBroadcast,这个方法接收两个参数,第一个仍然是Intent第二个则是一个与权限相关的字符串,这里传入一个nul就可以了。不过现在运行项目后你会发现还是两个广播接收器都能接收到这条广播。好像没啥区别。不过别忘了他们是又优先级的

 

设置优先级的方法是在注册的位置设定。如下,我们在第二个广播注册位置 添加 android:priority  将优先级设置为100。确保它一定在 MyBroadcastReceiver 前收到消息。

        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.broadcasttest.MY_BROADCAST" />
            </intent-filter>
        </receiver>

        <receiver
            android:name=".MyBroadcastReceiver2"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="100">
                <action android:name="com.example.broadcasttest.MY_BROADCAST" />
            </intent-filter>
        </receiver>

获得优先级后,我们就可以阻断广播继续传递了。修改MyBroadcastReceiver2中的代码,如下

class MyBroadcastReceiver2 : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,"这是第二个广播接收器 接收到的自定义广播", Toast.LENGTH_SHORT).show()
        abortBroadcast()
    }
}

再次运行项目,你会发现只弹出一个Toast

 

好了,关于kotlin的广播的基本用法就到这里了,不会再更了

 

推荐阅读