Basics of Android Development - Broadcasting Practice

In some social accounts, forced offline is a relatively common function, such as remote login. The idea of ​​​​realizing the forced offline function is actually just to pop up a dialog box on the interface, so that the user cannot perform any operations, such as clicking the dialog box to return to the login interface. This function can be realized by means of the broadcast function.

The forced logout function needs to close all activities first, and then return to the login interface. Before that, create an ActivityCollector class to manage all activities:

package com.example.broadcastbestpractice

import android.app.Activity

object ActivityCollector {
    private val activities = ArrayList<Activity>()
    
    fun addActivity(activity: Activity) {
        activities.add(activity)
    }
    
    fun removeActivity(activity: Activity) {
        activities.remove(activity)
    }
    
    fun finishAll() {
        for (activity in activities) {
            if (!activity.isFinishing) {
                activity.finish()
            }
        }
        activities.clear()
    }
}

Then create the BaseActivity class as the parent class of all Activities:

open class BaseActivity :AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        ActivityCollector.addActivity(this)
    }

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

Create another LoginActivity as the login interface, and write the corresponding layout activity_login.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textSize="18sp"
            android:text="Account:"/>

        <EditText
            android:id="@+id/accountEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical"/>
    </LinearLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:textSize="18sp"
            android:text="Password:"/>

        <EditText
            android:id="@+id/passwordEdit"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_gravity="center_vertical"
            android:inputType="textPassword"/>
    </LinearLayout>

    <Button
        android:id="@+id/login"
        android:layout_width="200dp"
        android:layout_height="60dp"
        android:layout_gravity="center_vertical"
        android:text="Login"/>

</LinearLayout>

A login interface is written here, which is also easy to understand. Then modify the code in LoginActivity:

class LoginActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        
        login.setOnClickListener {
            val account = accountEdit.text.toString()
            val password = passwordEdit.text.toString()
            
            if (account == "admin" && password == "123456") {
                val intent = Intent(this, MainActivity::class.java)
                startActivity(intent)
                finish()
            } else {
                Toast.makeText(this, "account and password is invalid", Toast.LENGTH_SHORT).show()
            }
        }
    }
}

The code here is also easy to understand, that is, to get the account number and password in the layout, and then start the MainActivity if the match is consistent, otherwise it will prompt an error.

Here, add the forced offline function to MainActivity and modify activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/forceOffline"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send force offline broadcast" />

</LinearLayout>

The above code is also easy to understand, just add a button.

Modify MainActivity and add the corresponding click event:

class MainActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        forceOffline.setOnClickListener {
            val intent = Intent("com.example.broadcastbestpractice.FORCE_OFFLINE")
            sendBroadcast(intent)
        }
    }
}

It is also easy to understand that a broadcast is sent after the click event is triggered. The logic corresponding to receiving the broadcast does not need to be added to MainActivity, because no matter when the broadcast is received, it must be forced to go offline.

A BroadcastReceiver is created here to receive the broadcast, but where should it be created? First of all, after BroadcastReceiver receives the broadcast, it needs to pop up a dialog box to block the user's operation, but if it is statically registered, there is no way to pop up a UI control such as a dialog box in the onReceiver method.

Therefore, the BroadcastReceiver here needs to be dynamically registered in BaseActivity, because all Activities inherit from BaseActivity. Modify BaseActivity:

open class BaseActivity :AppCompatActivity() {
    
    lateinit var receiver: ForceOfflineReceiver
        
    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        ActivityCollector.addActivity(this)
    }

    override fun onResume() {
        super.onResume()
        val intentFilter = IntentFilter()
        intentFilter.addAction("com.example.broadcastbestpractice.FORCE_OFFLINE")
        receiver = ForceOfflineReceiver()
        registerReceiver(receiver, intentFilter)
    }

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

    override fun onDestroy() {
        super.onDestroy()
        ActivityCollector.removeActivity(this)
    }
    
    inner class ForceOfflineReceiver:BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent?) {
            AlertDialog.Builder(context).apply { 
                setTitle("Warning")
                setMessage("You are forced to be offline, please try to login again.")
                setCancelable(false)
                setPositiveButton("OK") { _, _ -> 
                    ActivityCollector.finishAll()
                    val intent = Intent(context, LoginActivity::class.java)
                    context.startActivity(intent)
                }
                show()
            }
        }
    }
}

The first is to use AlertDialog.Builder to build a dialog box in the onReceiver method in ForceOfflineReceiver, and then use setCancelable to make it uncancellable. Then use the setPositiveButton method to register the OK button. When the user clicks the OK button, call the finishAll method of ActivityCollector to destroy all activities and restart LoginActivity.

At the same time, the ForceOfflineReceiver is registered and unregistered in the onResume and onPause methods, because it is necessary to always ensure that only the Activity at the top of the stack can receive the forced offline broadcast, and the non-top Activity does not need to receive the broadcast.

Finally modify AndroidManifest.xml:

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

    <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.BroadcastBestPractice">
        <activity
            android:name=".LoginActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <activity
            android:name=".MainActivity"
            android:exported="true">

        </activity>
    </application>

</manifest>

Here just set the main Activity to LoginActivity instead of MainActivity. The result after running the program is:

 After sending the broadcast and being received, it will return to the login interface, which realizes the forced offline.

Guess you like

Origin blog.csdn.net/SAKURASANN/article/details/127040820