Full code Gitee address: Kotlin Demo: 15 day kotlin learning plan
Day 5 learning content code: Chapter5
catalogue
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.