当前位置:主页 > 移动开发 > Android代码 >

Android 零基础到精通之广播机制

时间:2021-12-04 09:32:39 | 栏目:Android代码 | 点击:

广播机制简介

Android 中的广播主要分为两种类型:

接收系统广播

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()
        }
    }
}

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 将无法再接收这条广播

您可能感兴趣的文章:

相关文章