Exploring the broadcasting of the four major components of Android

Table of contents

1. The mechanism of broadcasting

2. Explore broadcasting

1. Dynamic Registration

2. Static registration and sending standard broadcast

3. Send orderly broadcast

3. Ask questions

first question

second question

third question

4. Exploring Questions

first question

second question

third question


Broadcasting is a good thing. Our program can send and receive broadcasts to notify other programs of some news, or receive messages from other programs.

1. The mechanism of broadcasting

There are two types of broadcasts:

  1. standard broadcast

    This is an asynchronous broadcast. After the broadcast is sent out, all corresponding broadcast Receivers can receive the broadcast message. Such broadcasts are more efficient, but cannot be intercepted once sent.

  2. orderly broadcast

    As the name implies, this kind of broadcast is sent sequentially and is a synchronous broadcast. According to the order in which the weights are broadcasted. And the program that receives the broadcast first can truncate the broadcast, making it impossible for subsequent receivers to receive it.

There are two ways to accept broadcasting:

  1. dynamic registration

    In this way, we can register or unregister broadcast receivers at any time in the program. For example, we register the broadcast receiver in onResume() of Activity, and cancel the broadcast receiver in onPause(). The advantage of this is that it is very flexible, but the limitation is that the Activity must be started before it can be received.

  2. static registration

    We can write a class that inherits from BroadcastReceiver, register this broadcast receiver in AndroidManifest.xml, and specify the broadcast type to accept.

2. Explore broadcasting

1. Dynamic Registration

Let's first try to use dynamic registration to receive a broadcast from a system.

Create a new Activity and create an inner class inside:

    inner class TimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Toast.makeText(context, "the time has changed!", Toast.LENGTH_SHORT).show()
        }
    }

This class inherits BroadcastReceiver, which is a broadcast receiver. Then rewrite the onReceive() method, and process the logic to be processed when the broadcast is received.

Then we dynamically register the broadcast receiver in onCreate().

    lateinit var timeChangeReceiver: TimeChangeReceiver
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dynamic_broadcast)
        timeChangeReceiver = TimeChangeReceiver()
        val intentFilter = IntentFilter()
        // 系统自带的时间改变的广播
        intentFilter.addAction("android.intent.action.TIME_TICK")
        registerReceiver(timeChangeReceiver, intentFilter)
    }

Here registerReceiver() is called to register the broadcast receiver. The first parameter is to specify the broadcast receiver, and the second parameter is an IntentFilter, which is used to specify which broadcasts to accept. android.intent.action.TIME_TICK is the broadcast that comes with the Android system. This broadcast will be sent when the time changes (in minutes).

Remember, the broadcast has to be unregistered after registration

    // 记得要注销掉接收器
    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(timeChangeReceiver)
    }

Now run the program and a prompt will pop up every minute.

 

2. Static registration and sending standard broadcast

Like the previous dynamic registration, if the Activity does not start, the broadcast receiver cannot be registered. But static registration has no such restrictions.

Here we can create a broadcast receiver BroadcastReceiver.

 

  • Exported indicates whether it is allowed to accept broadcasts other than this program

  • Enabled indicates whether the Receiver is started.

After it is created, we just need to restart the onReceiv() method. However, in Androidmanifest.xml, Android Studio has already registered this Receiver for us.

        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
        </receiver>

Then we specify which broadcast method this Receiver receives, which is also defined in Adroidmanifest.xml.

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

Here we define that we will receive the com.bingbin.MY_BROADCAST broadcast, and now we are going to implement sending a broadcast.

Create a new Activity and add a Button in it to send broadcasts.

        btn_sendBroadcast.setOnClickListener {
            val intent = Intent("com.bingbin.MY_BROADCAST")
            intent.setPackage(packageName)
            sendBroadcast(intent)
        }

The method of sending a broadcast is sendBoradcast(), which needs to pass in an Intent(). When we created it, we specified that the broadcast should be sent com.bingbin.MY_BROADCAST.

Here is an explanation of why there is the line intent.setPackage(packageName): Implicit broadcasts refer to those broadcasts that do not specify a sending program, such as power-on broadcasts and time change broadcasts. Statically registered Receivers can receive these broadcasts, but this will cause some programs to maliciously receive broadcasts to consume resources, so Android has now prohibited statically registered Receivers from receiving implicit broadcasts. So here we need to specify the program under the same package name to receive this broadcast.

After clicking the button, a prompt message will appear:

 

In order to verify the standard broadcast, here we create another BroadcastReceiver to also receive the com.bingbin.MY_BROADCAST broadcast

class AnotherReceiver : BroadcastReceiver() {
​
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "another receiver also has received my broadcast",
            Toast.LENGTH_SHORT).show()
    }
}
        <receiver
            android:name=".AnotherReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter >
                <action android:name="com.bingbin.MY_BROADCAST" />
            </intent-filter>
        </receiver>

At this time, press the button again, and two prompt messages will appear.

 

 

3. Send orderly broadcast

The method to send a standard broadcast is sendBroadcast(), and the method to send an ordered broadcast is sendOrderBoradcast().

Let's add another button to send an ordered broadcast.
 

       btn_sendOrderBroadcast.setOnClickListener {
            val intent = Intent("com.bingbin.MY_BROADCAST")
            intent.setPackage(packageName)
            sendOrderedBroadcast(intent, null)
        }

The second parameter can be set to null.

Since it is an ordered broadcast, how should the order be decided? We can determine the weight in the <intent-filter> tag registered by Receiver in AndroidManifest.xml.

        <receiver
            android:name=".AnotherReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="10">
                <action android:name="com.bingbin.MY_BROADCAST" />
            </intent-filter>
        </receiver>
        <receiver
            android:name=".MyBroadcastReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="100">
                <action android:name="com.bingbin.MY_BROADCAST" />
            </intent-filter>
        </receiver>

In this way, MyBroadcastReceiver will receive the broadcast first, and AnotherReceiver will receive the broadcast later.

If the abortBroadcast() method is called in the logic that MyBroadcastReceiver receives the broadcast , the broadcast can be cut off so that AnotherReceiver cannot receive the broadcast.

In this way, only the message of MyBroadcastReceiver will pop up.

3. Ask questions

first question

Is the android.intent.action.TIME_TICK broadcast of the previously used system a standard broadcast or an ordered broadcast? How to set priority for dynamically registered BroadcastReceiver?

second question

Can a BroadcastReceiver receive multiple broadcasts? If not, if there is a requirement that a logic will be triggered only when two different specified broadcasts are received within a period of time, how should it be implemented?

third question

What is the life cycle of a statically registered Receiver? If several broadcasts are received in a row, is it a new Receiver or the same instance each time?

4. Exploring Questions

first question

First of all, because android.intent.action.TIME_TICK is an implicit broadcast, we cannot use the static registration method to test, we can only use the dynamic registration BroadcastReceiver.

Then we write two internal BroadcastReceivers in one class, set priorities for them, and the BroadcastReceiver with higher priority tries to intercept the broadcast to see if the one with lower priority will still receive it.

Here, by the way, the problem of how to set the priority of the dynamically registered BroadcastReceiver is solved. We need to pass in the IntentFilter() parameter when registering the broadcast, and the priority can be set in the IntentFilter.

class DynamicBroadcastActivity : AppCompatActivity() {
​
    lateinit var timeChangeReceiver: TimeChangeReceiver
    lateinit var anotherReceiver : AnotherTimeChangeReceiver
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dynamic_broadcast)
        timeChangeReceiver = TimeChangeReceiver()
        val filter1 = IntentFilter()
        // 系统自带的时间改变的广播
        filter1.addAction("android.intent.action.TIME_TICK")
        filter1.priority = 100
        registerReceiver(timeChangeReceiver, filter1)
​
        anotherReceiver = AnotherTimeChangeReceiver()
        val filter2 = IntentFilter()
        // 系统自带的时间改变的广播
        filter2.addAction("android.intent.action.TIME_TICK")
        filter2.priority = 10
        registerReceiver(anotherReceiver, filter2)
    }
​
    // 记得要注销掉接收器
    override fun onDestroy() {
        super.onDestroy()
        unregisterReceiver(timeChangeReceiver)
        unregisterReceiver(anotherReceiver)
    }
​
    inner class TimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Toast.makeText(context, "Time has changed!", Toast.LENGTH_SHORT).show()
            // 尝试拦截
            abortBroadcast()
        }
    }
​
    inner class AnotherTimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Toast.makeText(context, "AnotherTimeChangeReceiver has received the broadcast",
                Toast.LENGTH_SHORT).show()
        }
    }
}

After testing, it was found that AnotherTimeChangeReceiver still received the broadcast. It shows that this system broadcast is a standard broadcast.

Presumably, if the broadcast of the system is an orderly broadcast, then a malicious program registers a BroadcastReceiver with a high priority to intercept it, and problems are likely to occur.

second question

To test this, let's define a button that sends two broadcasts.

    btn_sendTwoBroadcast.setOnClickListener { 
            val broad1 = Intent("com.bingbin.BROADCAST1")
            broad1.setPackage(packageName)
            val broad2 = Intent("com.bingbin.BROADCAST2")
            broad2.setPackage(packageName)
            sendBroadcast(broad1)
            sendBroadcast(broad2)
        }

Then try to receive two broadcasts in the statically registered Receiver.

        <receiver
            android:name=".StrongReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.bingbin.BROADCAST1"/>
                <action android:name="com.bingbin.BROADCAST2"/>
            </intent-filter>
        </receiver>

Then add the processing logic

class StrongReceiver : BroadcastReceiver() {
​
    var i = 0
​
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "I have received a broadcast $i", Toast.LENGTH_SHORT).show()
        i += 1
    }
}

In order to distinguish how many times it was called, I added a global variable.

Checked the log

 

It was found that the output was output twice, but i did not change. This is a bit strange.

Later I put i in a static class

 

The output is found to be correct.

So it can be determined here that a BroadcastReceiver can specify to receive multiple broadcasts.

third question

In fact, we can guess from the practice of the second question that every time a Receiver receives a broadcast, it should be a new instance.

Let's verify again

class MyBroadcastReceiver : BroadcastReceiver() {
​
    init {
        Log.d("MyBroadcastReceiver", "创建了一个新的实例")
    }
​
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "I have received my own broadcast!",
            Toast.LENGTH_SHORT).show()
    }
}

Here I added a sentence of log output to the constructor of MyBroadcastReceiver.

 

You will find that every time a broadcast is received, the statically registered Receiver will create a new instance.

Then the question comes, will the previously created instance always exist in memory? When is it destroyed? Will it cause OOM if it is not destroyed?

Then I found such a comment in the source code of onReceive()

If this BroadcastReceiver was launched through a <receiver> tag, then the object is no longer alive after returning from this function. This means you should not perform any operations that return a result to you asynchronously.

So we can see that if it is a statically registered Receiver, it will be destroyed after calling onReceive(). There is no memory risk.

When we register dynamically, we must also pay attention to actively calling unregisterReceiver() to cancel the Receiver, otherwise there may be a risk of memory leaks.

Guess you like

Origin blog.csdn.net/qq_43478882/article/details/122262862