On the fifth day of Kotlin development, broadcast BroadcastReceiver

Keywords: Android kotlin

Full code Gitee address: Kotlin Demo: 15 day kotlin learning plan

Day 5 learning content code: Chapter5

catalogue

preface

definition

Application scenario

Knowledge point 1: static registration broadcast

Knowledge point 2: dynamic registration broadcast

Knowledge point 3: Customize global broadcasting

preface

  • BroadcastReceiver is one of the four components of Android
  • In Android development, there are many application scenarios for BroadcastReceiver
  • Today, I will explain everything about BroadcastReceiver in detail

definition

That is, broadcast is a global listener, which belongs to one of the four components of Android

Android   Broadcast is divided into two roles: Broadcast sender and broadcast receiver

Application scenario

  • Communication between different Android components (including: within application / between different applications)
  • Multithreaded communication
  • Communication with Android system under specific circumstances

  For example, today's examples: time change monitoring, startup completion monitoring, and network change monitoring

Knowledge point 1: static registration broadcast

         The dynamically registered BroadcastReceiver can freely control registration and logoff, and has great advantages in flexibility. However, it has a disadvantage that the broadcast can only be received after the program is started, because the registration logic is written in the onCreate() method. So is there any way to make the program receive broadcasts without starting it? This requires the use of static registration.

         Because a large number of malicious applications use this mechanism to listen to the system broadcast when the program is not started, so that any application can be frequently awakened from the background, which seriously affects the power and performance of the user's mobile phone, almost every version of the Android system is reducing the function of statically registering BroadcastReceiver. After Android 8.0 system, all implicit broadcasts are not allowed to be received by static registration.

         Static registration enables startup. You can create internal classes through shortcuts provided by Android Studio. Right click com.example.broadcastest package → New → Other → Broadcast Receiver, and a window as shown in the figure will pop up.

  Then modify the code in BootCompleteReceiver as follows:

class MyReceiver : BroadcastReceiver() {

    @SuppressLint("UnsafeProtectedBroadcastReceiver")
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "Boot broadcast", Toast.LENGTH_SHORT).show()
        //Open current program after receiving broadcast
        context.startActivity(context.packageManager.getLaunchIntentForPackage(context.packageName))
    }
}

  The code is very simple. We open the current application in onReceive;

         In addition, the static BroadcastReceiver must be registered in the AndroidManifest.xml file before it can be used. However, since we created the BroadcastReceiver using the shortcut of Android Studio, the registration step has been completed automatically. Open the AndroidManifest.xml file and have a look. The code is as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.kotlin_demo">

    <!-- Receive start complete -->
    <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.Kotlindemo">
        ....
        <!-- Static registration, start broadcasting -->
        <receiver
            android:name=".Chapter5.MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
    </application>
</manifest>

The MyReceiver automatically created cannot receive the startup broadcast, because we still need to modify the AndroidManifest.xml file. As shown above, the operation effect is as follows:

Knowledge point 2: dynamic registration broadcast

         Dynamic broadcasting is best registered in onResume() and onPause() of Activity. For dynamic broadcasting, if there is registration, there must be logout, otherwise it will lead to memory leakage.

         In the code, you call the registerReceiver () method. The specific code is as follows:

    override fun onResume() {
        super.onResume()
        // Monitoring time changes, usually jumping once a minute
        time = TimeReceiver()
        val timeFilter = IntentFilter()
        timeFilter.addAction(Intent.ACTION_TIME_TICK)
        registerReceiver(time, timeFilter)
        // Monitor network changes
        netWork = NetworkReceiver()
        val networkFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        registerReceiver(netWork, networkFilter)
    }

    override fun onPause() {
        super.onPause()
        //The page closes and the broadcast stops
        unregisterReceiver(time)
        unregisterReceiver(netWork)
    }

    inner class TimeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            Toast.makeText(context, "Time is monitored", Toast.LENGTH_SHORT).show()
            Log.i("TAG", "TimeReceiver: Time is monitored")
        }
    }

    inner class NetworkReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            Log.i("TAG", "NetworkReceiver: The network is monitored")

            val manager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val networkInfo = manager.activeNetworkInfo

            // Judge network conditions
            if (networkInfo != null && networkInfo.isAvailable) {
                // What to do when the network is available
                Toast.makeText(context, "Network available", Toast.LENGTH_SHORT).show()
            } else {
                // What to do when the network is not available
                Toast.makeText(context, "network connect fail", Toast.LENGTH_SHORT).show()
            }
        }
    }

The effects are as follows:

Knowledge point 3: Customize global broadcasting

        Realize the function of global notification message broadcasting and click confirm to exit the program.

         A dialog box needs to pop up in the BroadcastReceiver to block the normal operation of users, but if a statically registered BroadcastReceiver is created, there is no way to pop up UI controls such as dialog box in onReceive() method, and it is obviously impossible for us to register a dynamic BroadcastReceiver in each Activity. So what should we do? In fact, the answer is obvious. You only need to dynamically register a BroadcastReceiver in BaseActivity, because all activities inherit from BaseActivity.

First create an ActivityCollector class to manage all activities. The code is as follows:

object ActivityCollector {

    private val activityList = ArrayList<Activity>()

    //Add activity to collection
    fun addActivity(activity: Activity) {
        activityList.add(activity)
    }

    //Remove from collection
    fun removeActivity(activity: Activity){
        activityList.remove(activity)
    }

    //Close all activities
    fun finishAll(){
        for (activity in activityList){
            if (!activity.isFinishing){
                activity.finish()
            }
        }
        activityList.clear()
    }
}

Then create the BaseActivity class as the parent class of all activities. The code is as follows:

open class BaseActivity : AppCompatActivity() {

    private lateinit var text: TextReceiver

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.i("BaseActivity", javaClass.simpleName)
        //Add activity
        ActivityCollector.addActivity(this)
    }

    override fun onResume() {
        super.onResume()
        text = TextReceiver()
        val textFilter = IntentFilter("com.example.kotlin_demo.TextReceiver")
        registerReceiver(text, textFilter)
    }

    override fun onPause() {
        super.onPause()
        unregisterReceiver(text)
    }

    inner class TextReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            AlertDialog.Builder(context)
                .setTitle("Message reminder")
                .setMessage("This is a global broadcast. Click me to exit the application")
                .setPositiveButton("determine") { _, _ ->
                    ActivityCollector.finishAll()
                }
                .setNeutralButton("cancel", null)
                .create()
                .show()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        //Destroy activity
        ActivityCollector.removeActivity(this)
    }
}

A button is then used to trigger a custom broadcast. As follows:

    //3. In BaseActivity, customize a global broadcast and push a message
    val butSend :AppCompatButton = findViewById(R.id.but_send)
    butSend.setOnClickListener {
        //Any place can initiate broadcasting actively
        val intent = Intent("com.example.kotlin_demo.TextReceiver")
        sendBroadcast(intent)
    }

The operation effect is as follows:

         Because we always need to ensure that only activities at the top of the stack can receive this forced offline broadcast, and activities not at the top of the stack should not and do not need to receive this broadcast, this problem can be well solved by writing in onResume() and onPause() methods. When an Activity loses the top of the stack, it will automatically cancel the registration of BroadcastReceiver.

Posted by Drabin on Mon, 08 Nov 2021 00:54:23 -0800