Android 广播机制 Android 零基础到精通之广播机制
低吟不作语 人气:0广播机制简介
Android 中的广播主要分为两种类型:
- 标准广播:一种异步执行的广播,广播发出后,所有的 BroadcasterReceiver 几乎会在同一时刻受到这条广播消息,没有任何时间顺序
- 有序广播:一种同步执行的广播,广播发出后,同一时刻只有一个 BroadcasterReceiver 能够接受这条广播消息,当该 BroadcasterReceiver 中的逻辑执行完毕后,广播才会继续传递
接收系统广播
Android 内置了很多系统级别的广播,我们可以在应用程序中通过监听这些广播来得到各种系统的状态信息
1. 动态注册监听时间变化
新建一个类,继承自 BroadcasterReceiver,并重写父类的 onReceive() 方法,这样当有广播来时,onReceive() 方法就会得到执行
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) } inner class TimeChangeReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { Toast.makeText(context, "Time has changed", Toast.LENGTH_SHORT).show() } } }
- 定义了一个内部类 TimeChangeReceiver,继承自 BroadcasterReceiver 并重写父类的方法,这样每当系统时间发生变化时,就会使用 Toast 提示一段文本信息
- 在 onCreate() 方法创建一个 IntentFilter 实例,添加值为 android.intent.action.TIME_TICK 的 action,这也是系统时间发生变化时,系统会发出的广播的值
- 创建 TimeChangeReceiver 实例,然后调用 registerReceiver() 方法进行注册,将 IntentFilter 实例和 TimeChangeReceiver 实例传进去,完成监听功能
2. 静态注册实现开机启动
动态注册的 BroadcasterReceiver 相对灵活,但必须在程序启动后才能接收广播,要想让程序在未启动的情况下也能接收广播,就需要使用静态注册的方式
创建类 BootCompleteReceiver,Exported 属性表示是否允许这个 BroadcasterReceiver 接收本程序以外的广播,Enabled 属性表示是否启用这个 BroadcasterReceiver,勾选这两个属性,点击 Finish 完成创建
class BootCompleteReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show() } }
静态的 BroadcasterReceiver 要在 AndroidManifest.xml 文件中注册才可以使用。由于 Android 系统启动完成后会发出一条值为 android.intent.action.BOOT_COMPLETED 的广播,所以我们在 <receiver> 标签中添加一个 <intent-filter> 标签,并在里面声明相应的 action。另外,还必须在 AndroidManifest.xml 中进行权限声明。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.fragmenttest"> <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.FragmentTest"> ... <receiver android:name="com.example.broadcasttest.BootCompleteReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> </application> </manifest>
发送自定义广播
1. 发送标准广播
新建一个 MyBroadcasterReceiver,并在 onReceive() 方法中加入如下代码:
class MyBroadcasterReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context, "received in MyBroadcasterReceiver", Toast.LENGTH_SHORT).show() } }
然后在 AndroidManifest.xml 中对这个 BroadcasterReceiver 进行修改
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.fragmenttest"> ... <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.FragmentTest"> ... <receiver android:name="com.example.broadcasttest.MyBroadcasterReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.example.broadcasttest.MyBroadcasterReceiver" /> </intent-filter> </receiver> </application> </manifest>
接下来修改 activity_main.xml,定义一个按钮,作为发送广播的触发点
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Send Broadcast" /> </LinearLayout>
修改 MainActivity 中的代码
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button.setOnClickListener { val intent = Intent("com.example.broadcasttest.MyBroadcasterReceiver") intent.setPackage(packageName) sendBroadcast(intent) } } }
这里对 setPackage() 方法做个说明,静态注册的 BroadcasterReceiver 是无法接收隐式广播的,而默认情况下我们发出的自定义广播都是隐式广播,因此这里要调用 setPackage() 方法,指定这条广播是发送给哪个应用程序的,从而让它变成一条显式广播
2. 发送有序广播
有序广播是一种同步执行的广播,并且可以被截断,我们再新建 AnotherBroadcasterReceiver
class AnotherBroadcasterReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context, "received in AnotherBroadcasterReceiver", Toast.LENGTH_SHORT).show() } }
然后在 AndroidManifest.xml 中进行修改
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.fragmenttest"> ... <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.FragmentTest"> ... <receiver android:name="com.example.broadcasttest.AnotherBroadcasterReceiver" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.example.broadcasttest.MyBroadcasterReceiver" /> </intent-filter> </receiver> ... </application> </manifest>
到目前为止,程序发出的都是标准广播,要发送有序广播,就要重新回到 BroadcasterTest 项目,然后修改 MainActivity 中的代码
class MainActivity : AppCompatActivity() { ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button.setOnClickListener { val intent = Intent("com.example.broadcasttest.MyBroadcasterReceiver") intent.setPackage(packageName) sendOrderedBroadcast(intent, null) } ... } }
sendOrderedBroadcast() 方法接收两个参数:第一个参数仍然是 Intent;第二个参数是一个与权限相关的字符串,这里传入 null 即可
接下来设定 BroadcasterReceiver 的先后顺序,修改 AndroidManifest.xml 中的代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.fragmenttest"> ... <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.FragmentTest"> <receiver android:name="com.example.broadcasttest.MyBroadcasterReceiver" android:enabled="true" android:exported="true"> <intent-filter android:priority="100"> <action android:name="com.example.broadcasttest.MyBroadcasterReceiver" /> </intent-filter> </receiver> ... </application> </manifest>
通过 android:priority 属性给 BroadcasterReceiver 设置优先级,优先级比较高的 BroadcasterReceiver 就可以先收到广播。既然 MyBroadcasterReceiver 获得了接收广播的优先级,那么 MyBroadcasterReceiver 就可以选择是否允许广播继续传递了
class MyBroadcasterReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Toast.makeText(context, "received in MyBroadcasterReceiver", Toast.LENGTH_SHORT).show() abortBroadcast() } }
在 onReceive() 方法中调用 abortBroadcaster() 方法,就表示将这条广播截断,后面的 BroadcasterReceiver 将无法再接收这条广播
加载全部内容