In The Android operating system, Broadcast is a mechanism to transmit data between components. These components can be located in different processes for inter-process communication

BroadcastReceiver is a component that filters, receives, and responds to Broadcast. First, the message to be sent and the information used for filtering (Action, Category) are loaded into an Intent object. The Intent object is then broadcast by calling context.sendbroadcast () and sendOrderBroadcast(). Once the broadcast is sent, the registered BroadcastReceiver checks whether the registered IntentFilter matches the Intent sent, and calls the BroadcastReceiver’s onReceiver() method if it does

So when we define a BroadcastReceiver, we need to implement the onReceiver() method. The BroadcastReceiver has a very short life cycle and is only valid when the onReceiver() method is executed. Once the execution is complete, the life cycle of the BroadcastReceiver ends

There are two types of broadcast in Android, standard broadcast and ordered broadcast

  • Standard broadcast. Standard broadcast is a completely asynchronous broadcast. After the broadcast is sent, all receivers receive the broadcast at the same time. It has high efficiency and cannot be truncated
  • Orderly broadcast. Orderly broadcast is a kind of synchronous execution, on the radio after the same time only a radio receiver can receive priority radio receiver would receive priority, when the high priority onReceiver radio receivers () method after the operation, the radio will continue to pass, and in front of the radio receiver can choose truncated radio, So the radio receiver behind can’t pick up the broadcast

One, static registration

Static registration is to register the BroadcastReceiver in the manifest file, declare it with the **< Receiver >** tag, and set the filter in the tag with the < intent-filter > tag. The life cycle of a BroadcastReceiver follows the entire application. If you are dealing with system broadcasts, the BroadcastReceiver can receive the broadcasts regardless of whether the application is running

1. Send standard broadcasts

First, inherit the BroadcastReceiver class to create a Receiver for receiving standard broadcasts, fetching the string passed by the Intent in the onReceive() method

public class NormalReceiver extends BroadcastReceiver {

    private static final String TAG = "NormalReceiver";

    public NormalReceiver(a) {}@Override
    public void onReceive(Context context, Intent intent) {
        String msg = intent.getStringExtra("Msg"); Log.e(TAG, msg); }}Copy the code

The BroadcastReceiver declared in the manifest file must contain the action attribute of the NORMAL_ACTION string in order for the BroadcastReceiver to receive the broadcasts issued in the following code

The sendBroadcast(Intent) method is invoked to send a standard broadcast

public class MainActivity extends AppCompatActivity {

    private final String NORMAL_ACTION = "com.example.normal.receiver";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void sendBroadcast(View view) {
        Intent intent = new Intent(NORMAL_ACTION);
        intent.putExtra("Msg"."Hi"); sendBroadcast(intent); }}Copy the code

Register the BroadcastReceiver in the manifest file

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <receiver android:name=".NormalReceiver">
            <intent-filter>
                <action android:name="com.example.normal.receiver" />
            </intent-filter>
        </receiver>
        
    </application>
Copy the code

2. Send an ordered broadcast

First, inherit the BroadcastReceiver class to create three receivers for receiving ordered broadcasts, named OrderReceiver_1, OrderReceiver_2, and OrderReceiver_3. In addition, since the Receiver receives broadcasts in a sequential order, in addition to receiving data from the Intent used to send the broadcast, the higher-priority Receiver can also transmit the processing results to the lower-priority Receiver after processing the operation

public class OrderReceiver_1 extends BroadcastReceiver {

    private final String TAG = "OrderReceiver_1";

    public OrderReceiver_1(a) {}@Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "OrderReceiver_1 was called");

        // Retrieve the data passed in the Intent
        String msg = intent.getStringExtra("Msg");
        Log.e(TAG, "Value received by OrderReceiver_1:" + msg);

        // Pass data to the Receiver of the next priority
        Bundle bundle = new Bundle();
        bundle.putString("Data"."(Hello)"); setResultExtras(bundle); }}Copy the code
public class OrderReceiver_2 extends BroadcastReceiver {

    private final String TAG = "OrderReceiver_2";

    public OrderReceiver_2(a) {}@Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "OrderReceiver_2 was called");

        // Fetch the data passed by the previous priority Receiver
        String data = getResultExtras(true).getString("Data");
        Log.e(TAG, "Data passed from the previous priority Receiver --" + data);

        // Pass data to the Receiver of the next priority
        Bundle bundle = new Bundle();
        bundle.putString("Data"."(Leaves should be leaves)"); setResultExtras(bundle); }}Copy the code
public class OrderReceiver_3 extends BroadcastReceiver {

    private final String TAG = "OrderReceiver_3";

    public OrderReceiver_3(a) {}@Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "OrderReceiver_3 was called");

        // Fetch the data passed by the previous priority Receiver
        String data = getResultExtras(true).getString("Data");
        Log.e(TAG, "Data passed from the previous priority Receiver --"+ data); }}Copy the code

Register three receivers in the manifest file and specify the same action attribute value. The priority between receivers is determined using the priority attribute. The higher the value, the higher the priority

	<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

		
        <receiver android:name=".OrderReceiver_1">
            <intent-filter android:priority="100">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
		
        <receiver android:name=".OrderReceiver_2">
            <intent-filter android:priority="99">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
		
        <receiver android:name=".OrderReceiver_3">
            <intent-filter android:priority="98">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
		
    </application>
Copy the code

The sendOrderedBroadcast(Intent, String) method is invoked to send an ordered broadcast. The value of String is used when you define permissions, as described below

public class MainActivity extends AppCompatActivity {

    private final String NORMAL_ACTION = "com.example.normal.receiver";

    private final String ORDER_ACTION = "com.example.order.receiver";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void sendBroadcast(View view) {
        Intent intent = new Intent(NORMAL_ACTION);
        intent.putExtra("Msg"."Hi");
        sendBroadcast(intent);
    }

    public void sendOrderBroadcast(View view) {
        Intent intent = new Intent(ORDER_ACTION);
        intent.putExtra("Msg"."Hi");
        sendOrderedBroadcast(intent, null); }}Copy the code

The run result is

02-20 22:52:30.135 6714-6714/ / com. Example. Zy. Myapplication E OrderReceiver_1: OrderReceiver_1 is invoked02-20 22:52:30.135 6714-6714/ / com. Example. Zy. Myapplication E OrderReceiver_1: OrderReceiver_1 receives the value: Hi02-20 22:52:30.143 6714-6714/ / com. Example. Zy. Myapplication E OrderReceiver_2: OrderReceiver_2 is invoked02-20 22:52:30.143 6714-6714/ com. Example. Zy. Myapplication E/OrderReceiver_2: from a priority on the Receiver transmit data - (Hello)02-20 22:52:30.150 6714-6714/ / com. Example. Zy. Myapplication E OrderReceiver_3: OrderReceiver_3 is invoked02-20 22:52:30.150 6714-6714/ / com. Example. Zy. Myapplication E OrderReceiver_3: from a priority on the Receiver transmit data - (leaf should be)Copy the code

It can be seen that the Receiver receives broadcasts not only because of the order of “priority” attribute, but also because of data transfer between receivers

In addition, the BroadcastReceiver can call the abortBroadcast() method to truncate the broadcast so that lower priority broadcast receivers cannot receive the broadcast

Second, dynamic registration

To register a BroadcastReceiver dynamically, you define and set up an IntentFilter object in your code, and then call the context.registerReceiver () method where you want to register it. Calling Context. UnregisterReceiver () method to cancel the registration, at the moment there is no need to register the Receiver in the manifest file

This takes the form of registering a broadcast receiver in the Service and outputs logs when registering a broadcast receiver, unregistering a broadcast receiver, and receiving a broadcast

public class BroadcastService extends Service {

    private BroadcastReceiver receiver;

    private final String TAG = "BroadcastService";

    public BroadcastService(a) {}@Override
    public void onCreate(a) {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(MainActivity.ACTION);
        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Log.e(TAG, "BroadcastService receives broadcast"); }}; registerReceiver(receiver, intentFilter); Log.e(TAG,"BroadcastService registered receiver");
        super.onCreate();
    }

    @Override
    public void onDestroy(a) {
        super.onDestroy();
        unregisterReceiver(receiver);
        Log.e(TAG, "BroadcastService unregister receiver");
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null; }}Copy the code

Provides methods for starting, stopping, and broadcasting services

public class MainActivity extends AppCompatActivity {

    public final static String ACTION = "com.example.receiver";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void startService(View view) {
        Intent intent = new Intent(this, BroadcastService.class);
        startService(intent);
    }

    public void sendBroadcast(View view) {
        Intent intent = new Intent(ACTION);
        sendBroadcast(intent);
    }

    public void stopService(View view) {
        Intent intent = new Intent(this, BroadcastService.class); stopService(intent); }}Copy the code

The result is as follows

02-20 23:55:20.967 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService registered receivers02-20 23:55:22.811 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService received radio02-20 23:55:23.179 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService received radio02-20 23:55:23.461 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService received radio02-20 23:55:23.694 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService received radio02-20 23:55:23.960 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService received radio02-20 23:55:24.282 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService received radio02-20 23:55:24.529 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService received radio02-20 23:55:24.916 22784-22784/ / com. Example. Zy. Myapplication E BroadcastService: BroadcastService cancel registered receiversCopy the code

Local broadcasting

All broadcasts sent and received are global broadcasts of the system. That is, broadcasts sent and received by other applications can be received by other applications, which may cause insecurity

Therefore, in some cases, a local broadcast mechanism can be used, where broadcasts issued by this mechanism can only be transmitted within the application, and the broadcast receiver can only receive broadcasts issued within the application itself

LocalBroadcastManager is used to manage broadcasts

function role
LocalBroadcastManager.getInstance(this).registerReceiver(BroadcastReceiver, IntentFilter) Registered Receiver
LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver); The cancellation of the Receiver
LocalBroadcastManager.getInstance(this).sendBroadcast(Intent) Sending asynchronous broadcast
LocalBroadcastManager.getInstance(this).sendBroadcastSync(Intent) Send synchronous broadcast

First, you create a BroadcastReceiver to receive local broadcasts

public class LocalReceiver extends BroadcastReceiver {

    private final String TAG = "LocalReceiver";

    public LocalReceiver(a) {}@Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "Local broadcast received."); }}Copy the code

Then it is time to register and unregister LocalReceiver using LocalBroadcastManager

	private LocalBroadcastManager localBroadcastManager;

    private LocalReceiver localReceiver;

    private final String LOCAL_ACTION = "com.example.local.receiver";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localReceiver = new LocalReceiver();
        IntentFilter filter = new IntentFilter(LOCAL_ACTION);
        localBroadcastManager.registerReceiver(localReceiver, filter);
    }

    @Override
    protected void onDestroy(a) {
        super.onDestroy();
        unregisterReceiver(batteryReceiver);
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
	
	public void sendLocalBroadcast(View view) {
        Intent intent = new Intent(LOCAL_ACTION);
        localBroadcastManager.sendBroadcast(intent);
    }

Copy the code

It is important to note that local broadcasts cannot be received through static registration, because static registration is mainly used to receive broadcasts without the application being started, while local broadcasts are sent by the application itself, which must be started

Use private permissions

The problem with using a dynamically registered broadcast Receiver is that any application within the system can listen and trigger our Receiver. Normally we don’t want that

One solution is to add an Android: Exported =”false” attribute to the < Receiver > tag in the manifest file indicating that the Receiver is for internal use only. In this way, other applications in the system cannot access the Receiver

Alternatively, you can choose to create your own usage permissions by adding a < Permission > tag to the manifest file to declare custom permissions

    <permission
        android:name="com.example.permission.receiver"
        android:protectionLevel="signature" />
Copy the code

The protectionLevel attribute value must be specified at the same time. The system determines the user mode of the user-defined permission based on this attribute value

Attribute values Limited way
normal The default value. Low-risk permissions that pose the least risk to other applications, systems, and users. The system automatically grants this type of permission to applications when they are installed and does not require explicit user approval (although users can always choose to view these permissions before installation).
dangerous High-risk permissions. Applications requesting this type of permission can access users’ private data or control devices, which may adversely affect users. Because this type of license introduces potential risks, the system may not automatically grant it to the requested application. For example, the system could show the user any hazardous licenses requested by the application and require confirmation before proceeding, or it could take some other approach to avoid automatic permission by the user
signature The system grants the permission only when the application requesting the permission uses the same certificate signature as the application claiming the permission. If the certificate matches, the system automatically grants permission without notifying the user or requiring explicit approval
signatureOrSystem The system only grants applications in the Android system image that use the same certificate signature as the applications that claim permissions. Avoid this option. The “Signature” level is sufficient for most requirements, and the “signatureOrSystem” permission is used for some special cases

First, create a new project, create a custom permission in its manifest file, and declare it. ProtectionLevel property value set to “signature”

    <permission
        android:name="com.example.permission.receiver"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.example.permission.receiver" />
Copy the code

Then, send a Broadcast with the permission declaration. In this way, only applications that use the same certificate signature and claim the permission can receive the Broadcast

    private final String PERMISSION_PRIVATE = "com.example.permission.receiver";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void sendPermissionBroadcast(View view) {
        sendBroadcast(new Intent("Hi"), PERMISSION_PRIVATE);
    }
Copy the code

Go back to the previous project and first declare permissions in the manifest file

<uses-permission android:name="com.example.permission.receiver" />
Copy the code

Create a BroadcastReceiver

public class PermissionReceiver extends BroadcastReceiver {

    private final String TAG = "PermissionReceiver";

    public PermissionReceiver(a) {}@Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "Private privilege broadcast received."); }}Copy the code

Then register the broadcast receiver. Since Android Studio uses the same certificate to sign each App during debugging, the PermissionReceiver outputs a Log after a previously newly installed App sends a broadcast

	private final String PERMISSION_PRIVATE = "com.example.permission.receiver";

    private PermissionReceiver permissionReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        IntentFilter intentFilter1 = new IntentFilter("Hi");
        permissionReceiver = new PermissionReceiver();
        registerReceiver(permissionReceiver, intentFilter1, PERMISSION_PRIVATE, null);
    }

    @Override
    protected void onDestroy(a) {
        super.onDestroy();
        unregisterReceiver(permissionReceiver);
    }
Copy the code

Five, actual combat

1. Monitor network status changes

First you need a utility class to monitor the current state of the network

/** * Created by leaves should be leaves on 2017/2/21. */

public class NetworkUtils {

    /** * Marks the current network status, namely: mobile data, Wifi, unconnected, network status announced */
    public enum State {
        MOBILE, WIFI, UN_CONNECTED, PUBLISHED
    }

    /** * A flag bit that is used to cache the status of the network before a new broadcast is received */
    private static State tempState;

    /** * Get the current network connection status **@param context Context
     * @returnNetwork status */
    public static State getConnectState(Context context) {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        State state = State.UN_CONNECTED;
        if(networkInfo ! =null && networkInfo.isAvailable()) {
            if (isMobileConnected(context)) {
                state = State.MOBILE;
            } else if(isWifiConnected(context)) { state = State.WIFI; }}if (state.equals(tempState)) {
            return State.PUBLISHED;
        }
        tempState = state;
        return state;
    }

    private static boolean isMobileConnected(Context context) {
        return isConnected(context, ConnectivityManager.TYPE_MOBILE);
    }

    private static boolean isWifiConnected(Context context) {
        return isConnected(context, ConnectivityManager.TYPE_WIFI);
    }

    private static boolean isConnected(Context context, int type) {
        //getAllNetworkInfo() is deprecated in API 23
        //getAllNetworks() was added in API 21
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            NetworkInfo[] allNetworkInfo = manager.getAllNetworkInfo();
            for (NetworkInfo info : allNetworkInfo) {
                if (info.getType() == type) {
                    returninfo.isAvailable(); }}}else {
            Network[] networks = manager.getAllNetworks();
            for (Network network : networks) {
                NetworkInfo networkInfo = manager.getNetworkInfo(network);
                if (networkInfo.getType() == type) {
                    returnnetworkInfo.isAvailable(); }}}return false; }}Copy the code

Then declare a BroadcastReceiver and output the current network state with Log in the onReceive() method

public class NetworkReceiver extends BroadcastReceiver {

    private static final String TAG = "NetworkReceiver";

    public NetworkReceiver(a) {}@Override
    public void onReceive(Context context, Intent intent) {
        switch (NetworkUtils.getConnectState(context)) {
            case MOBILE:
                Log.e(TAG, "Currently connected to mobile data");
                break;
            case WIFI:
                Log.e(TAG, "Currently connected to Wifi");
                break;
            case UN_CONNECTED:
                Log.e(TAG, "No network connection at present");
                break; }}}Copy the code

Registered in the manifest file radio receiver, “android.net.conn.CONNECTIVITY_CHANGE” is the predefined action value system, as long as the system state changes, network NetworkReceiver will receive the broadcast

        <receiver android:name=".NetworkReceiver">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>
Copy the code

Also, ask for permission to view network status

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Copy the code

2. Monitor the change of electric quantity

Since the system states that broadcast receivers that listen for power changes cannot be registered statically, dynamic registration is the only option

private final String TAG = "MainActivity";

    private BroadcastReceiver batteryReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
        batteryReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // Current power
                int currentBattery = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
                / / total power
                int totalBattery = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);         
                Log.e(TAG, "Current battery :" + currentBattery + "- Total electric quantity:"+ totalBattery); }}; registerReceiver(batteryReceiver, intentFilter); }@Override
    protected void onDestroy(a) {
        super.onDestroy();
        unregisterReceiver(batteryReceiver);
    }
Copy the code

The Intent value in onReceive(Context, Intent) contains some additional information to extract the current and total charge

To make it easy to see the power changes, you can actively change the simulator’s power in the “Extended Controls” panel of the simulator to view the Log output

3, application installation update uninstall listener

First, create the BroadcastReceiver

public class AppReceiver extends BroadcastReceiver {

    private final String TAG = "AppReceiver";

    public AppReceiver(a) {}@Override
    public void onReceive(Context context, Intent intent) {
        // Determine the type of broadcast
        String action = intent.getAction();
        // Get the package name
        Uri appName = intent.getData();
        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
            Log.e(TAG, "Installed:" + appName);
        } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
            Log.e(TAG, "Updated:" + appName);
        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
            Log.e(TAG, "Unloaded:"+ appName); }}}Copy the code

Register broadcast receiver

	<receiver android:name=".train.AppReceiver">
            <intent-filter>
                <! -- Install the app -->
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <! Update app -->
                <action android:name="android.intent.action.PACKAGE_REPLACED" />
                <! -- Uninstall the app -->
                <action android:name="android.intent.action.PACKAGE_REMOVED" />
                <! -- Carrying bag name -->
                <data android:scheme="package" />
            </intent-filter>
        </receiver>
Copy the code