Those things about broadcasting in Android development

I remember when I was a child, everything in the village would be notified through the loudspeaker of the village radio station, which is still the case today. Broadcasting is a simple and effective way to communicate something. Similar broadcast mechanisms exist in Android systems and are more flexible to use. The following is a step-by-step introduction to the use of various broadcasts in Android.

1. Standard Broadcasting

Standard broadcasting is one of the most common broadcasting methods in Android. It is completely asynchronous. That is to say, once the broadcasting is issued, all programs registering the broadcaster will receive the broadcasting almost simultaneously. Its working principle is shown in the following figure.

In order to receive broadcasting, first of all, a broadcasting receiver should be registered. There are two ways of registration: dynamic registration and static registration. Either way, we must first create a receiving class that inherits BroadcastReceiver and overrides its onReceive() method. onReceive() is the logic to be performed after the program receives the broadcast.

class Demo1Recceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "onReceive:  this is demo1 receiver!");
        }
    }

Here, I create an internal class and print a line of logs in onReceive() to indicate that the program received the broadcast. Here's how to register this broadcaster into the system.

First look at dynamic registration, as the name implies, is the use of code for registration in the program, this registration method is simple and clear, but there is a disadvantage that we must be able to receive broadcasting after the application is launched. If we need to monitor system broadcasting, such as opening broadcasting, it is best not to use this registration method.

IntentFilter filter=new IntentFilter("com.example.demo1.BROADCAST");
Demo1Recceiver receiver=new Demo1Recceiver();
registerReceiver(receiver,filter);

As you can see, we first create an IntentFilter, which mainly explains which broadcast we want to receive. com.example.demo1.BROADCAST is a broadcast action that we specify. It needs to be consistent with the action filled in when sending the broadcast to receive the broadcast. At this time, the broadcaster has registered, so let's send a broadcast to check whether it can be received.
First, add a button to the main page and send a broadcast when we click the button, as shown in the following figure.

The code for adding buttons and clicking events is not posted here. Look directly at the method of sending broadcasts.

Intent intent=new Intent("com.example.demo1.BROADCAST");
sendBroadcast(intent);

Yes, it only takes two lines of code to send a broadcast. Here, the parameters passed in by Intent need to be consistent with those in IntentFilter when registering broadcasters and ensure that they are unique throughout the application. I believe that if you understand the use of Intent, you should be able to think that when sending broadcasts, we can pass in data in intent, and then get data in onReceive() for corresponding operations. To verify the validity of the above code, run the program, click the send broadcast button, and print such a log message in the background:
02-24 06:05:31.399 22997-22997/com.example.broadcastdemo1 D/BroadcastReceiverDemo: onReceive: this is demo1 receiver!
It can be seen that the broadcast has been sent and received successfully. It should be noted that broadcasters registered dynamically need to unregister in appropriate places to avoid confusion. For example, if we are a broadcaster registered in activity, we can unroll the registration in the onDestory() method.

@Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
    }

The above broadcasting mode can be received not only in the application itself, but also in other applications registered with the corresponding actions. Let's verify it below. Create a new Android application Broadcast Demo2, register a broadcast recipient in Broadcast Demo2 in the same way as above, and change the log information as follows:

class Demo2Receiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "onReceive: this is demo2 receiver!");
        }
    }

Install BroadcastDemo2 on your mobile phone, then open BroadcastDemo1 and click on the Send Broadcasting button. Note that when you return to BroadcastDemo1 from BroadcastDemo2, you must press the home key to return to BroadcastDemo1, and then open BroadcastDemo1, you can't exit directly by pressing the return key, because as mentioned earlier, the way of dynamic registration can receive broadcasting only when the program is running. Here are two results. The log information in case of press home and press return key to exit respectively:

02-24 06:14:20.505 13084-13084/com.example.broadcastdemo2 D/BroadcastReceiverDemo: onReceive: this is demo2 receiver!
02-24 06:14:20.506 22997-22997/com.example.broadcastdemo1 D/BroadcastReceiverDemo: onReceive:  this is demo1 receiver!
02-24 06:20:00.978 22997-22997/com.example.broadcastdemo1 D/BroadcastReceiverDemo: onReceive:  this is demo1 receiver!

As you can see, Broadcast Demo2 does receive broadcasts, but when registered dynamically, Broadcast Demo2 must be kept running.

After dynamic registration, let's talk about static registration. The greatest advantage of static registration is that it can receive broadcasting and trigger corresponding operations when the application is not running, such as receiving boot-up broadcasting to start a service. Static registration requires adding code to the Android Manifest. XML file. Next, we annotate the dynamic registration code in Broadcast Demo2, change the original internal class recipient to the external class, and add the following code under the application tag in its Android Manifest. XML file:

<receiver android:name=".Demo2Receiver"
      android:enabled="true"
      android:exported="true">
      <intent-filter>
           <action android:name="com.example.demo1.BROADCAST"/>
      </intent-filter>
</receiver>

The name attribute indicates which broadcaster we want to register, and the action in the intent-filter tag is the action passed in when we send the broadcast.
Next, we press the return key to turn off Broadcast Demo2, and then click the Send Broadcast button. The log information is as follows:

02-24 06:39:39.686 4126-4126/com.example.broadcastdemo2 D/BroadcastReceiverDemo: onReceive: this is demo2 receiver!
02-24 06:39:39.690 22997-22997/com.example.broadcastdemo1 D/BroadcastReceiverDemo: onReceive:  this is demo1 receiver!

Obviously, even if Broadcast Demo2 does not run, it can still receive Broadcast Demo1 broadcasts.

2. Standard Broadcasting with Authority

From the above examples, you may think that, since other applications can receive broadcasts from our applications, will that cause security risks? Yes, there are security problems in the above-mentioned general broadcasting, which are mainly reflected in two aspects:
1. If other applications listen to our broadcasting, it will result in data leakage of our applications.
2. If other applications impersonate our applications to send broadcasts, then our broadcasting acceptance programs will be activated frequently, resulting in confusion or even collapse of our applications.
Fortunately, Android has already considered this issue and provided us with a privilege mechanism. Firstly, for the first problem, when we send a broadcast, we can specify a permission for it. Only the application with this permission can receive the broadcast, as follows:

Intent intent=new Intent("com.example.demo1.BROADCAST");  sendBroadcast(intent,"com.example.demo1.BROADCAST_PERMISSION");

Among them, com.example.demo1.BROADCAST_PERMISSION is our designated permission, which can be arbitrary, but its uniqueness must be guaranteed. After making the above changes to the code, running the program and clicking the send button, you can see that there is no log output, that is to say, neither Broadcast Demo1 nor Broadcast Demo2 received broadcast. Why? Since neither of the above applications adds the permissions we specified when sending broadcasts, we can't receive them. Let's first add the following code in Android Manifest. XML of Broadcast Demo1:

<permission android:name="com.example.demo2"/>
<uses-permission android:name="com.example.demo1.BROADCAST_PERMISSION"/>

Running program sends broadcast, log output as follows:

02-24 08:08:58.703 21027-21027/com.example.broadcastdemo1 D/BroadcastReceiverDemo: onReceive:  this is demo1 receiver!

As you can see, BroadcastDemo1 can receive broadcasts, but BroadcastDemo2 still has not received. Add the above permissions for BroadcastDemo2 again, run the program, and the log output is as follows:

02-24 08:08:58.703 21027-21027/com.example.broadcastdemo1 D/BroadcastReceiverDemo: onReceive:  this is demo1 receiver!
02-24 08:08:58.706 3338-3338/com.example.broadcastdemo2 D/BroadcastReceiverDemo: onReceive: this is demo2 receiver!

Through two comparisons, we can see that only applications with specified permissions can receive broadcasts.
The second problem of insecurity can also be solved by the privilege mechanism. That is to say, when registering broadcasting recipients, a privilege is also specified. Only when the application with the specified privilege sends the broadcasting, and the action matches, can it be received by me. Dynamic and static registration methods specify permissions as follows:

IntentFilter filter=new IntentFilter("com.example.demo1.BROADCAST");
receiver=new Demo2Recceiver();
registerReceiver(receiver,filter,"com.example.demo2",null);
<receiver
    android:name=".Demo2Receiver"
    android:permission="com.example.demo2"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.demo1.BROADCAST" />
     </intent-filter>
</receiver>

In both ways, we assign com.example.demo2 to the broadcaster of BroadcastDemo2, run the BroadcastDemo2 program, and then send the broadcast from BroadcastDemo1. You can see that the log output is as follows:

02-24 08:15:57.105 21027-21027/com.example.broadcastdemo1 D/BroadcastReceiverDemo: onReceive:  this is demo1 receiver!

Broadcast Demo2 did not receive broadcasting because Broadcast Demo1 does not have the right to com.example.demo2, so Broadcast Demo2 sent by Broadcast Demo1 can not receive. Here we add the following rights in Broadcast Demo1:

<permission android:name="com.example.demo2"/>
<uses-permission android:name="com.example.demo2"/>

Run Broadcast Demo1 again to send the broadcast. The log output is as follows:

02-24 08:22:51.673 21027-21027/com.example.broadcastdemo1 D/BroadcastReceiverDemo: onReceive:  this is demo1 receiver!
02-24 08:22:51.673 3338-3338/com.example.broadcastdemo2 D/BroadcastReceiverDemo: onReceive: this is demo2 receiver!

Broadcast Demo2 has successfully received broadcasts.
From above, the security problem of standard broadcasting has been perfectly solved, not only standard broadcasting, but also orderly broadcasting.

3. Orderly broadcasting

In addition to standard broadcasting in asynchronous mode, Android also provides an orderly broadcasting in synchronous mode. In orderly broadcasting, the high priority broadcasting receiver will receive the broadcasting first, and can add its own processing results to the broadcasting to continue downward transmission. At the same time, it can terminate the broadcasting. After termination, the low priority broadcasting receiver will not receive the broadcasting. Its working principle is as follows:

The method of sending orderly broadcasting is as follows:

Intent orderedIntent=new Intent("com.example.demo1.ORDERED_BROADCAST");
sendOrderedBroadcast(orderedIntent,null);

null denotes the specified permissions. For simplicity, we do not have permissions here. Next, we will create three different levels of broadcasters from high to low. The key codes are as follows:

public class HighPriorityReceiver extends BroadcastReceiver {
    private static final String TAG="HighPriorityReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive: this is a high priority receiver");
    }
}
public class MiddlePriorityReceiver extends BroadcastReceiver {
    private static final String TAG="MiddlePriorityReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive: this is a middle priority receiver");
    }
public class LowPriorityReceiver extends BroadcastReceiver {
    private static final String TAG="LowPriorityReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive: this is a low priority receiver"+getResultCode());
    }
}

After the recipients are created, they are registered, and different priorities can be assigned to them when they are registered. The same registration method can be divided into dynamic registration and static registration. First of all, look at the way of assigning priority in dynamic registration.

        //Register high-privileged recipients
        IntentFilter highFilter=new IntentFilter("com.example.demo1.ORDERED_BROADCAST");
        highFilter.setPriority(100);
        highReceiver=new HighPriorityReceiver();
        registerReceiver(highReceiver,highFilter);
        //Register medium-privileged recipients
        IntentFilter midFilter=new IntentFilter("com.example.demo1.ORDERED_BROADCAST");
        midFilter.setPriority(50);
        midReceiver=new MiddlePriorityReceiver();
        registerReceiver(midReceiver,midFilter);
        //Register low-privileged recipients
        IntentFilter lowFilter=new IntentFilter("com.example.demo1.ORDERED_BROADCAST");
        lowFilter.setPriority(10);
        lowReceiver=new LowPriorityReceiver();
        registerReceiver(lowReceiver,lowFilter);

Invoke the setPriority (int priority) method of IntentFilter to set the priority. The parameter values can range from - 1000 to 1000. The higher the value, the higher the priority. Similarly, in static registration, priority is set by setting the priority attribute of intent-filter tag. The code is as follows:

        <receiver
            android:name=".HighPriorityReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="100">
                <action android:name="com.example.demo1.ORDERED_BROADCAST" />
            </intent-filter>
        </receiver>

Let's verify the validity of the above code. Add a button to the MainActivity of Broadcast Demo1 to send an orderly broadcast. After sending, you can see the output of the log as follows:

02-24 09:57:24.088 18935-18935/com.example.broadcastdemo1 D/HighPriorityReceiver: onReceive: this is a high priority receiver
02-24 09:57:24.098 18935-18935/com.example.broadcastdemo1 D/MiddlePriorityReceiver: onReceive: this is a middle priority receiver
02-24 09:57:24.102 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: onReceive: this is a low priority receiver

Looking at the time each receiver receives the broadcast, you can see that it is in order of priority. It should be pointed out that neither standard broadcasting nor orderly broadcasting should be time-consuming in the receiver's onReceive() method, otherwise it may lead to application crash.
Next, let's look at how to pass the processing results of high priority recipients to low priority recipients:

public class HighPriorityReceiver extends BroadcastReceiver {
    private static final String TAG="HighPriorityReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle=new Bundle();
        bundle.putString("name","Alice");
        bundle.putInt("age",25);
        setResultExtras(bundle);
        Log.d(TAG, "onReceive: this is a high priority receiver");
    }
}

public class MiddlePriorityReceiver extends BroadcastReceiver {
private static final String TAG="MiddlePriorityReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle=getResultExtras(false);
String name=bundle.getString("name");
int age=bundle.getInt("age");
Log.d(TAG, "onReceive: this is a middle priority receiver");
Log.d(TAG, "The name is "+name+",The age is "+age);
bundle.putString("name","Tom");
setResultExtras(bundle);
}
}

public class LowPriorityReceiver extends BroadcastReceiver {
    private static final String TAG="LowPriorityReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        String name=getResultExtras(false).getString("name");
        int age=getResultExtras(false).getInt("age");
        Log.d(TAG, "onReceive: this is a low priority receiver");
        Log.d(TAG, "The name is "+name+", The age is "+age);
    }
}

Here, we add some data to the onReceive() method of HighPriorityReceiver, then read it in the MiddlePriorityReceiver, modify some data, and finally read it in LowPriorityReceiver. The results of the program are as follows:

02-24 11:31:43.403 18935-18935/com.example.broadcastdemo1 D/HighPriorityReceiver: onReceive: this is a high priority receiver
02-24 11:31:43.415 18935-18935/com.example.broadcastdemo1 D/MiddlePriorityReceiver: onReceive: this is a middle priority receiver
02-24 11:31:43.415 18935-18935/com.example.broadcastdemo1 D/MiddlePriorityReceiver: The name is Alice,The age is 25
02-24 11:31:43.417 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: onReceive: this is a low priority receiver
02-24 11:31:43.417 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: The name is Tom, The age is 25

As you can see, the data has been successfully transferred from high priority recipients to low priority recipients, and the data can be changed during the transfer process. Of course, for the data transmitted through Intent when broadcasting, this part of the data can not be changed, and all receivers can get it through the intent parameter of onReceive() method, unless the high priority receiver terminates the broadcasting.
For the termination of broadcasting, just add a line of code to onReceive(), and change the code of HighPriorityReceiver as follows:

    public void onReceive(Context context, Intent intent) {
        Bundle bundle=new Bundle();
        bundle.putString("name","Alice");
        bundle.putInt("age",25);
        setResultExtras(bundle);
        Log.d(TAG, "onReceive: this is a high priority receiver");
        abortBroadcast();
    }

Run the program again, and the log output is as follows:

02-24 11:37:16.623 18935-18935/com.example.broadcastdemo1 D/HighPriorityReceiver: onReceive: this is a high priority receiver

Because the high-priority HindPriority Receiver terminates broadcasting, the low-priority MidlePriority Receiver and Low Priority Receiver will no longer receive broadcasting. But Android also provides a mechanism to ensure that if a broadcast is terminated during delivery, a final receiver will receive the broadcast and process it accordingly. This mechanism requires the designation of a final broadcaster when sending orderly broadcasts, as follows:

sendOrderedBroadcast(orderedIntent,null,new LowPriorityReceiver(),null,RESULT_OK,null,null);

We designate LowPriorityReceiver() as the final broadcaster and keep other program codes unchanged to run the program again:

02-24 11:44:19.848 18935-18935/com.example.broadcastdemo1 D/HighPriorityReceiver: onReceive: this is a high priority receiver
02-24 11:44:19.850 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: onReceive: this is a low priority receiver
02-24 11:44:19.850 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: The name is Alice, The age is 25

As you can see, MiddlePriority Receiver did not receive broadcasts, while LowPriority Receiver received broadcasts with lower priority, and the data parsed was the data transmitted by High Priority Receiver. However, it should also be pointed out that the designated final receiver is performed after the normal broadcast transmission, that is to say, although LowPriority Receiver is executed in the above code, this does not change the nature of its low priority. We do the following experiments, annotate the code in HighPriority Receiver to stop broadcasting, and then run the program. The results are as follows:

02-24 11:49:20.986 18935-18935/com.example.broadcastdemo1 D/HighPriorityReceiver: onReceive: this is a high priority receiver
02-24 11:49:20.997 18935-18935/com.example.broadcastdemo1 D/MiddlePriorityReceiver: onReceive: this is a middle priority receiver
02-24 11:49:20.997 18935-18935/com.example.broadcastdemo1 D/MiddlePriorityReceiver: The name is Alice,The age is 25
02-24 11:49:20.998 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: onReceive: this is a low priority receiver
02-24 11:49:20.998 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: The name is Tom, The age is 25
02-24 11:49:21.002 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: onReceive: this is a low priority receiver
02-24 11:49:21.002 18935-18935/com.example.broadcastdemo1 D/LowPriorityReceiver: The name is Tom, The age is 25

As you can see, LowPriorityReceiver's onReceive() method is executed twice, which fully demonstrates that the final broadcast receiver's execution runs after the end of the normal orderly broadcast transmission.

3. Local Broadcasting

Both standard broadcasting and orderly broadcasting can be received by third-party applications if no permissions are set. Even if permissions are set, security problems still exist if permissions are obtained by third-party applications. Therefore, if the broadcast we send only needs to be delivered within the application, then local broadcasting can be used. The method of sending local broadcasting is similar to that of standard broadcasting.
Send broadcasting:

LocalBroadcastManager localManager=LocalBroadcastManager.getInstance(this);
Intent localIntent=new Intent("com.example.demo1.LOCAL_BROADCAST");
localManager.sendBroadcast(localIntent);

Registered Broadcasting:

public class LocalReceiver extends BroadcastReceiver {
    private static final String TAG="LocalReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "onReceive: this is local receiver!");
    }
}
IntentFilter localFilter=new IntentFilter("com.example.demo1.LOCAL_BROADCAST");
LocalReceiver localReceiver=new LocalReceiver();
localManager.registerReceiver(localReceiver,localFilter);

It should be noted that local broadcasting can only be registered dynamically. In fact, it is well understood that local broadcasting is transmitted within the application. When sending broadcasting, the application must have been started, so there is no need for static registration.

Welcome to reproduce, reproduce please indicate the source: http://blog.csdn.net/tianweitao/article/details/57074933

Tags: Android xml Attribute Mobile

Posted on Fri, 05 Apr 2019 19:33:30 -0400 by Kifebear