[TOC]

preface

Broadcasts are registered in two ways: at manifest, also known as static registration, or at run time by calling the registerReceiver() method, also known as dynamic registration.

There are two different types of broadcast transmission according to the order of arrival: one is normal broadcast and the other is ordered broadcast.

The transmission of broadcast can also be divided into two different types according to whether or not the receiver is specified: one is explicit broadcast and the other is implicit broadcast.

Note: If our broadcast is only for internal application use, we can use local broadcast. This implementation is much more efficient (no interprocess communication is required), and there are no security concerns related to other applications being able to receive and send our broadcasts.

BroadcastReceiver life cycle

Life cycle: We send a broadcast through the component, and the system will automatically match the corresponding registered broadcast receiver and call the onReceiver() method of the broadcast receiver. After the onReceiver() method is run, the system will consider this broadcast receiver object is no longer an active object. I’m going to finished it.

The onReceiver() method cannot perform time-consuming operations because the BroadcastReceiver runs on the UI thread, otherwise the ANR(Application No Response) dialog will appear. Also do not start the thread in the onReceiver() method and then return from the function, because once it returns, the system will assume that the BroadcastReceiver is not active and therefore no longer needs its host process (unless the other application components in it are active). Therefore, the system may terminate the reclaimed memory of a process at any time, and doing so terminates spawned threads running in the process.

Is there a way to complete a time-consuming operation in BroadcastReceiver? There are two ways to do this:

1. CallgoAsync()Method,

Call goAsync onReceiver () the receiver () method and the BroadcastReceiver. PendingResult give a background thread. This keeps the broadcast active onReceive() after it returns. But even with this approach, the system expects you to be able to broadcast very quickly (less than 10 seconds). It runs you to move work to another thread to avoid blocking the main thread. The following is an example:

    class IBroadcastReceiverTask : BroadcastReceiver() {
    override fun onReceive(context: Context? , intent:Intent?). {
        KLog.i("IBroadcastReceiverTask")
        
        // Avoid jamming
        val pendingResult = goAsync()
        val task = Task(pendingResult, intent)
        // Perform asynchronous tasks
        task.execute()
     
    }



    private class Task(private val pendingResult:PendingResult,
            private valintent: Intent? ) :AsyncTask<String,String,String>(){override fun doInBackground(vararg params: String?).: String {
            KLog.i("doInBackground")
            ssss()
            return toString()
        }

        override fun onPostExecute(result: String?). {
            super.onPostExecute(result)
            // You must call Finish () for the BroadcastReceiver to be reclaimed
            pendingResult.finish()
            KLog.i("Completed time-consuming operation")}}}fun ssss(a){
    var time = 30
    do {
        Thread.sleep(1000)
        KLog.i(time)
        time--
    }while(time! =0)}Copy the code

Above is an example of the first approach

2. Use the JobService

The JobScheduler JobService is used so that the system knows that there is active work to be done during the process. The following is an example:

class IBroadcastReceiverJobService: BroadcastReceiver() {

    var JOB_TEST =10001
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onReceive(context: Context? , intent:Intent?). {
        KLog.i("IBroadcastReceiverJobService")
        valjobScheduler:JobScheduler = context? .getSystemService(Context.JOB_SCHEDULER_SERVICE)as JobScheduler

        val jobInfo = JobInfo.Builder(JOB_TEST, ComponentName(context.packageName, MyJobService::class.java.getName()))
                .setPeriodic(AlarmManager.INTERVAL_FIFTEEN_MINUTES)
                .setPersisted(false)
                .build()
        jobScheduler.schedule(jobInfo)
        KLog.i("IBroadcastReceiverJobService finish")}}@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class MyJobService : JobService() {override fun onStopJob(params: JobParameters?).: Boolean {
        KLog.i("OnStopJob ===> End")
        return false
    }

    override fun onStartJob(params: JobParameters?).: Boolean {
        KLog.i("OnStartJob ===> Start")

       object : AsyncTask<String, String, String>() {
            override fun doInBackground(vararg params: String?).: String {
                ssss()
                return toString()
            }

           override fun onPostExecute(result: String?). {
               super.onPostExecute(result)
               KLog.i("OnPostExecute ===> Start")
               this@MyJobService.jobFinished(params,false)
           }

        }.execute()


        KLog.i("OnStartJob ===> End")
        return false}}Copy the code

Android.permission.BIND_JOB_SERVICE: android.permission.BIND_JOB_SERVICE: android.permission. The statement is as follows:

<service
        android:permission="android.permission.BIND_JOB_SERVICE"
        android:name=".MyJobService"/>
Copy the code

JobService was added in Android 5.0 (API level 21).

Want to know more detailed JobScheduler usage Please check the panzeyong.com/2017/05/21/…

Register in the manifest (static register)

Manifest registration: Also known as static registration, it declares a broadcast receiver in androidmanifest.xml. This type of broadcast can receive the corresponding broadcast before the application is started. How to register, as follows:

// Create a class that extends BroadcastReceiver
class IBroadcastReceiver1: BroadcastReceiver() {
    override fun onReceive(context: Context? , intent:Intent?). {
        KLog.i("onReceive ===>  ${javaClass.simpleName}")
        StringBuilder().apply {
            append("Action: ${intent? .action}\n")
            append("URI: ${intent? .toUri(Intent.URI_INTENT_SCHEME)}\n")
            toString().also { log ->
                KLog.d(TAG, log)
                Toast.makeText(context, log, Toast.LENGTH_LONG).show()
            }
        }
    }

    companion object {
        private const val TAG = "IBroadcastReceiver1"}}Copy the code

Classes created with declarations in Androidmanifest.xml

     <receiver android:name=".IBroadcastReceiver1" >
            <intent-filter android:priority="666">
                <action android:name="com.hugo.IBroadcastReceiver1"/>
                
            </intent-filter>
        </receiver>
        
Copy the code

So we registered a broadcast called IBroadcastReceiver1 through the manifest.

Dynamic registration

Dynamic registration: Registers broadcast receivers with context.registerReceiver () in a component such as a Service or Activity. Such broadcast receivers are registered by code after the application has been started. The following is an example:

// Create an action filter
    val filter = IntentFilter()
    // You can set multiple listening actions
    filter.addAction(Constants.FILTER_NAME2)
    // Set priority Value range -1000 to 1000 A larger value indicates a higher priority
    filter.priority = 1000
    // Register a broadcast listener
    registerReceiver(IBroadcastReceiver2(),filter)
Copy the code

So we dynamically register a broadcast receiver.

Note: The unregisterReceiver() method must be called to unregister the components that exit or close the broadcast registration. It is not easy to cause the broadcast receiver to leak.

Ordinary radio

Ordinary broadcasts are completely asynchronous and can be received (logically) by all receivers at the same time. The message delivery efficiency is relatively high, but the disadvantage is that the receiver cannot process the result to the next receiver and cannot terminate the delivery of the broadcast Intent. How do we send a broadcast? Here’s an example:

        val intent = Intent()
        // Set the broadcast action
        intent.action = Constants.FILTER_NAME2
        // Send a broadcastSendBroadcast (Intent) log iBroadcastReceiver2. kt: [(iBroadcastReceiver2. kt:16)#OnReceive ] onReceive ===>  IBroadcastReceiver2
        IBroadcastReceiver2.kt: [ (IBroadcastReceiver2.kt:21)#OnReceive ] Action: IBroadcastReceiver2
        IBroadcastReceiver3.kt: [ (IBroadcastReceiver3.kt:16)#OnReceive ] onReceive ===>  IBroadcastReceiver3
        IBroadcastReceiver3.kt: [ (IBroadcastReceiver3.kt:24)#OnReceive ] Action: IBroadcastReceiver2
Copy the code

As shown in the preceding example, the context.sendbroadcast () method is called to send a normal broadcast, as long as the broadcast receiver matches the action specified in the Intent.

Conclusion:

  1. Regardless of priority, dynamic broadcast receivers take precedence over static broadcast receivers.
  2. List registration: those scanned first take precedence over those scanned later;
  3. Dynamic registration: registration first takes precedence over registration later.

Orderly broadcast

Orderly broadcast: Broadcasts are received in sequence according to the priorities set by broadcast receivers when they register. A higher-priority receiver can modify the data or interrupt the broadcast according to different needs.

The value of priority ranges from -1000 to 1000. The default value is 0.

Send a broadcast as follows:

     val intent = Intent()
    intent.action = Constants.FILTER_NAME2
    sendOrderedBroadcast(intent,nullIbroadcastreceiver2. kt: [(iBroadcastReceiver2. kt:17)#OnReceive ] onReceive ===>  IBroadcastReceiver2
IBroadcastReceiver2.kt: [ (IBroadcastReceiver2.kt:25)#OnReceive ] Action: IBroadcastReceiver2
IBroadcastReceiver3.kt: [ (IBroadcastReceiver3.kt:16)#OnReceive ] onReceive ===>  IBroadcastReceiver3
IBroadcastReceiver3.kt: [ (IBroadcastReceiver3.kt:18[(ibroadcastReceiver3.kt: ===> ibroadcastReceiver3.kt: ===> ibroadcastReceiver3.kt:25)#OnReceive ] Action: IBroadcastReceiver2
Copy the code

As shown in the sample Send radio call Context, sendOrderedBroadcast () method, the system will according to the priority level of radio receivers in sequence, in front of the right to terminate the radio, so that the back of the receiver can not receive the radio, the radio can also send data to the back of the radio receiver.

The broadcast receiver calls abortBroadcast() to terminate the broadcast.

There are two methods used by broadcast receivers to send data back: setResultExtras() and setResult().

The broadcast receiver retrieves the previously sent data using the getResultExtras(true) method.

Note: all of the above methods are called in the onReceive() method.

Conclusion:

  1. Those with a higher priority are received first.
  2. Dynamic registration with the same priority takes precedence over static registration.
  3. Same priority same type of broadcast receiver, static registration: scan first takes precedence over scan later, dynamic registration: register first takes precedence over register later.

Local radio

Local broadcast: As the name implies, this broadcast is only useful within the application.

Local broadcast uses methods in the LocalBroadcastManager class:

  1. RegisterReceiver (): This is used to register broadcast receivers.
  2. SendBroadcast (): This is used to send broadcasts.
  3. SendBroadcastSync (): This is similar to the sendBroadcast() method, but if there are receivers for the Intent, the function will block and dispatch them immediately before returning.
  4. UnregisterReceiver (): This method is used to unregister.

That’s all for local broadcasting. The use method is basically the same as the previous introduction.

According to radio

Display broadcast: An Intent sent is a broadcast that displays an Intent, as specified by the name of the Intent component. It is typically used in an application where the name of the target component is known, the Intent is clear, and the receiver of the active broadcast is specified.

The following is an example:


val intent = Intent()    
intent.setClass(this,IBroadcastReceiver1::class.java)
sendBroadcast(intent)


val intent = Intent(this,IBroadcastReceiver1::class.java)
sendBroadcast(intent)


val intent = Intent() 
intent.setClassName(this,IBroadcastReceiver1::class.java.name)
sendBroadcast(intent)


// The internal implementation of the above three methods creates the ComponentName object and assigns it to Intent.component.ponent
val intent = Intent() 
intent.component = ComponentName(this,IBroadcastReceiver1::class.java)
sendBroadcast(intent)
    
Copy the code

This is how to use display broadcast.

Implicit radio

Implicit broadcast: implemented with an Intent Filter that does not specify the name of the broadcast receiver. The Android system matches the action (action), category (category), and data (URL and data type) set in the implicit intent to find the most suitable recipient to process the intent. This is generally used between applications.

The following is an example:

// Register the receiver
        val filter = IntentFilter()
  // Set the priority
    filter.priority = Awesome!
    // Set the data type
    filter.addDataType("text/plain")
    / / set the URL
    filter.addDataScheme("https")
    // Set the category
    filter.addCategory("android.intent.category.DEFAULT")
    registerReceiver(IBroadcastReceiver4(),filter)

// Emit an implicit broadcast
     val intent = Intent()
    intent.action = Constants.FILTER_NAME2
    // Set the category
    intent.addCategory("android.intent.category.DEFAULT")
    // Set the data type
    //intent.type = "text/plain"
    / / set the URL
    //intent.data = Uri.parse("https://www.baidu.com")
    // Set the data type and URL
     intent.setDataAndType(Uri.parse("https://www.baidu.com"),"text/plain")
            sendBroadcast(intent)
Copy the code

So that’s implicit broadcast.

Note:

  1. Starting with Android 7.0 (API level 24) the system places some restrictions on broadcasting:
    1. ACTION_NEW_PICTURE and ACTION_NEW_VIDEO broadcasts cannot be sent.
    2. Must use registerReceiver (BroadcastReceiver IntentFilter) registered CONNECTIVITY_ACTION broadcasting. Using a static declaration receiver is invalid.
  2. Since Android 8.0(API level 26), the system has further enhanced the restrictions on receivers declared by manifest. All but a few implicit broadcasts cannot be declared in Androidmanifest.xml, but can be invoked by display. The official recommendation is to use dynamic registration to register broadcasts.
  3. As of Android 9.0(API level 28), NETWORK_STATE_CHANGED_ACTION broadcasts do not receive information about the user’s location or personally identifiable data. Additionally, when the app is installed on a device running Android9.0 or higher, the wi-fi system broadcast no longer contains the SSID, BSSID, connection information or scan results. To get this information, call getConnectionInfo().

Implicit broadcast exception

Exceptions to listing registration for implicit broadcasting: official website

    // Unrestricted implicit broadcast on Android 8.0
ACTION_LOCKED_BOOT_COMPLETED Intent.ACTION_BOOT_COMPLETED */
"Retention reason: These broadcasts are sent only once when first started, and many applications need to receive them for scheduling jobs, alarms, and so on."

/ * * add or delete user Intent. ACTION_USER_INITIALIZE "android. Intent. Action. USER_ADDED" "android. Intent. Action. USER_REMOVED" * /
"Retention reason: These broadcasts can only be listened to by apps with specific system permissions, so most normal apps cannot receive them."
    
/ * * time zone changes, ALARM "android. Intent. Action. TIME_SET" intent. ACTION_TIMEZONE_CHANGED AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED */
"Reason for retention: The clock application may need to receive these broadcasts in order to update the alarm when time or time zone changes."

ACTION_LOCALE_CHANGED */ intent.action_locale_changed */
"Retention reason: send only when the locale changes, infrequently. Applications may need to update their data as the locale changes.

ACTION_USB_ACCESSORY_ATTACHED USbManager.action_usb_accessory_detached UsbManager.ACTION_USB_DEVICE_ATTACHED UsbManager.ACTION_USB_DEVICE_DETACHED */
"Retention reason: There is currently no viable alternative to registering broadcasts if applications need to know about these USB-related events."

/** Bluetooth status-related BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED bluetootha2dp. ACTION_CONNECTION_STATE_CHANGED BluetoothDevice.ACTION_ACL_CONNECTED BluetoothDevice.ACTION_ACL_DISCONNECTED */
"Reason for retention: Applications receiving broadcasts of these Bluetooth events are unlikely to affect the user experience."

/ * * a Telephony related CarrierConfigManager ACTION_CARRIER_CONFIG_CHANGED TelephonyIntents. ACTION_ * _SUBSCRIPTION_CHANGED TelephonyIntents.SECRET_CODE_ACTION TelephonyManager.ACTION_PHONE_STATE_CHANGED TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED */
"Retention Reason: Equipment manufacturer (OEM) telephony applications may need to receive these broadcasts"

/** AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION */
"Retention reason: Some applications need to know about login account changes in order to schedule actions for new and changed accounts."

ACTION_PACKAGE_DATA_CLEARED */
"Retention reason: only sent when the user has explicitly cleared their data from Settings, so the broadcast receiver is unlikely to significantly affect the user experience."
    
Intent.action_package_fully_removed */
"Retention reasons: Some applications may need to update their stored data when another package is removed; No viable alternative to registering this broadcast has been found for these applications."

Intent.action_new_outgoing_call */
"Reserved reason: Applications that perform operations in response to user calls need to receive this broadcast."
    
/ * * when equipment owner is set up, change or remove a DevicePolicyManager. ACTION_DEVICE_OWNER_CHANGED * /
"Reserved reason: This broadcast is not sent very often; Some applications need to receive it to know that the security status of the device has changed."
    
/** CalendarDraft.action_event_reminder */
"Reserved Reason: Sent by the Calendar provider to publish event alerts to the calendar application. Because the calendar provider does not know what the calendar application is, this broadcast must be implicit.
    
ACTION_MEDIA_MOUNTED Intent.ACTION_MEDIA_CHECKING Intent.ACTION_MEDIA_EJECT Intent.ACTION_MEDIA_UNMOUNTED Intent.ACTION_MEDIA_UNMOUNTABLE Intent.ACTION_MEDIA_REMOVED Intent.ACTION_MEDIA_BAD_REMOVAL */
"Reason for retention: These broadcasts are sent as a result of the user's physical interaction with the device: installation or removal of storage volumes or as part of initiation initialization (when available volumes are loaded), so they are not very common and are usually under the user's control."

/ * * Sms, WAP PUSH relevant Telephony in Sms. The Intents. SMS_RECEIVED_ACTION Telephony. Sms. The Intents. WAP_PUSH_RECEIVED_ACTION note: Need to apply for permission to receive "android. Permission. RECEIVE_SMS" "android. Permission. RECEIVE_WAP_PUSH" * /
"Reserved reason: THE SMS messaging application needs to receive these broadcasts"

Copy the code

Refer to the link

Panzeyong.com/2017/05/21/…

Juejin. Cn/post / 684490…

Developer.android.com/guide/compo…

The demo link to this article is github.com/tao11122233…