16Application Development – Practical Broadcasting: Implementing Forced Offline Function

Tip: This article is only used to record my daily study. If there are any errors or inaccuracies, please correct me.

Article directory

  • 1. Practical broadcasting: realizing forced offline function
    • 1.1 Implement global management Activity function
    • 1.2 Login interface layout
    • 1.3 Login interface Activity
    • 1.4 Main interface layout
    • 1.5 The main interface sends broadcast function
    • 1.6 Implement forced offline function
    • 1.7 Operation effect

1. Practical broadcasting: implementing forced offline function

1.1 Implement global management Activity function

We create a new BoradcastBestPractice project and create a singleton class of ActivityController to manage the global Activity. Through ActivityController, no matter what interface we are on, we only need to call the finishAllActivities() method to close all interfaces.

/**
 * Singleton class used to manage global Activity
 */
object ActivityController {<!-- -->

    //A collection used to manage all activities
    private val activities = ArrayList<Activity>()

    //Add Activity to the collection
    fun addActivity(targetActivity: Activity) {<!-- -->
        activities.add(targetActivity)
    }

    //Remove Activity from the collection
    fun removeActivity(targetActivity: Activity) {<!-- -->
        activities.remove(targetActivity)
    }

    //Close all activities in the collection
    fun finishAllActivities() {<!-- -->
        for (activity in activities) {<!-- -->
            //Determine whether the Activity is being destroyed
            if (!activity.isFinishing) {<!-- -->
                activity.finish()
            }
        }
        //Clear the collection after destroying all activities
        activities.clear()
    }
}

Then we create a BaseActivity as the parent class of all activities, which makes it easier for us to manage global activities. We implemented the addition and removal of Activity collection elements in the onCreate() method and onDestroy() method of BaseActivity. When an Activity inherits from BaseActivity, the onCreate() and onDestroy() methods of the parent class BaseActivity will be automatically called during the life cycle of the Activity, thereby adding and removing the Activity collection:

/**
 * The parent class of global Activity
 */
open class BaseActivity : AppCompatActivity() {<!-- -->

    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {<!-- -->
        super.onCreate(savedInstanceState, persistentState)
        //Add the current Activity to the global Activity list
        ActivityController.addActivity(this)
    }

    override fun onDestroy() {<!-- -->
        super.onDestroy()
        //Remove the current Activity from the global Activity list
        ActivityController.removeActivity(this)
    }
}

1.2 Login interface layout

Next, we create a LoginActivity.kt as the Activity of the login interface, and let AS automatically create a matching layout for us. First we modify the layout of the login interface:

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

    <!--Account input area-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp"
            android:text="Account:"
            android:textSize="18sp" />

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

    <!--Password input area-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:orientation="horizontal">

        <TextView
            android:layout_width="90dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginLeft="10dp"
            android:text="Password:"
            android:textSize="18sp" />

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

    <!--Login button-->
    <Button
        android:id="@ + id/myLoginButton"
        android:layout_width="200dp"
        android:layout_height="60dp"
        android:layout_gravity="center_horizontal"
        android:text="Login"
        android:textSize="18sp" />

</LinearLayout>

1.3 Login interface Activity

Then we let LoginActivity inherit from BaseActivity and implement very simple login logic. When we enter the given account number and password, clicking login will jump to the main interface MainActivity, otherwise the user will be prompted to re-enter. :

class LoginActivity : BaseActivity() {<!-- -->

    override fun onCreate(savedInstanceState: Bundle?) {<!-- -->
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)
        
        //Login button click event
        myLoginButton.setOnClickListener {<!-- -->
            val account = myAccountEdit.text.toString()
            val password = myPasswordEdit.text.toString()
            if (account == "admin" & amp; & amp; password == "123456") {<!-- -->
                val intent = Intent(this, MainActivity::class.java)
                startActivity(intent)
                finish()
            } else {<!-- -->
                Toast.makeText(this, "Wrong account or password, please clear it and re-enter it!", Toast.LENGTH_SHORT).show()
                myPasswordEdit.setText("")
            }
        }
    }
}

1.4 Main interface layout

Next, we add a button to the main interface activity_main.xml to send a forced offline broadcast:

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

    <Button
        android:id="@ + id/forceOfflineButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="Send forced offline broadcast" />

</LinearLayout>

1.5 Main interface broadcast function

Next, we implement the button click logic in MainActivity.kt. When the user clicks the button, a standard broadcast of FORCE_OFFLINE will be sent to notify the application to force the user to log off.

class MainActivity : BaseActivity() {<!-- -->

    override fun onCreate(savedInstanceState: Bundle?) {<!-- -->
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //Button click logic
        forceOfflineButton.setOnClickListener {<!-- -->
            //Create intent
            val intent = Intent("com.example.boradcastbestpractice.FORCE_OFFLINE")
            //Send standard broadcast
            sendBroadcast(intent)
        }
    }
    
}

1.6 Implement forced offline function

Now we still need to create a BroadcastReceiver to receive this broadcast that forces the user to log off, so where should we create it? We hope to pop up an AlertDialog in the BroadcastReceiver to prevent the user’s operation. However, if it is created through static registration, there is no way to pop up a UI control such as AlertDialog in the onReceive() method. Because the onReceive() method runs in the background thread, and AndroidUI control operations need to be performed in the main thread of the application, so we can only explicitly way to register BroadcastReceiver.
In order to solve this problem, we can dynamically register a BroadcastReceiver in BaseActivity, because all Activities inherit from BaseActivity.
Modify the code of BaseActivity:

/**
 * The parent class of global Activity
 */
open class BaseActivity : AppCompatActivity() {<!-- -->

//Force offline broadcast receiver
    lateinit var myForceOffLineReceiver: ForceOffLineReceiver

    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {<!-- -->
        super.onCreate(savedInstanceState, persistentState)
        //Add the current Activity to the global Activity table
        ActivityController.addActivity(this)
    }

    override fun onDestroy() {<!-- -->
        super.onDestroy()
        //Remove the current Activity from the global Activity table
        ActivityController.removeActivity(this)
    }

//Inner class forced offline broadcast receiver
    inner class ForceOffLineReceiver : BroadcastReceiver() {<!-- -->
    //broadcast receiving logic
        override fun onReceive(context: Context, intent: Intent) {<!-- -->
        //Create a dialog box
            AlertDialog.Builder(context)?.apply {<!-- -->
                setTitle("warning")
                setMessage("You have been forced to the lower limit, please log in again.")
                //Cancel
                setCancelable(false)
                //Confirm button click logic
                setPositiveButton("Okay") {<!-- --> _, _ ->
                //Destroy all activities in the activity list
                    ActivityController.finishAllActivities()
                  //Jump to login interface
                    val intent = Intent(context, LoginActivity::class.java)
                    context.startActivity(intent)
                }
                create()
                show()
            }
        }
    }

    override fun onResume() {<!-- -->
        super.onResume()
        //Create broadcast filter
        val myIntentFilter = IntentFilter()
        //Add action to filter
        myIntentFilter.addAction("com.example.boradcastbestpractice.FORCE_OFFLINE")
        //Initialize broadcast receiver
        myForceOffLineReceiver = ForceOffLineReceiver()
        //Implicitly register broadcast receiver
        registerReceiver(myForceOffLineReceiver, myIntentFilter)
    }

    override fun onPause() {<!-- -->
        super.onPause()
        //Log out the broadcast receiver
        unregisterReceiver(myForceOffLineReceiver)
    }

}

We declare a forced offline broadcast receiver ForceOffLineReceiver through an internal class in BaseActivity. After the broadcast receiver receives the broadcast, a dialog box will pop up to prompt. When the user clicks the confirmation button, all activities will be destroyed. and jump to the re-login interface.
We put the logic of registering and unregistering ForceOffLineReceiver in the onResume() and onPause() methods. This is because we need to ensure that only the Activity at the top of the stack can receive this force. Line broadcast, Activities that are not at the top of the stack should not and do not need to receive this broadcast.

  • onResume( ): Called when the Activity is ready to interact with the user. At this time, the Activity is visible (on top of the stack) and interactive.
  • onPause( ): When another Activity (not occupying the screen or fully transparent) comes to the foreground, the onPause( ) method of the original Activity will be called. At this time, the original Activity is invisible< /strong>.

In this way, our forced offline logic has been written. Finally, we need to set the main interface of the program to LoginActivity instead of MainActivity in AndroidManifest.xml (we definitely hope that the user can enter the main interface of the program after logging in):

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

    <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/AppTheme">
        <activity
            android:name=".LoginActivity"
            android:exported="true">
            <intent-filter>
                               Set the login interface as the main interface of the application
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity">
        </activity>
    </application>

</manifest>

1.7 Operation Effect

At this point, when we enter the set account and password, we will jump from the LoginActivity login interface to the main application interface MainActivity. When we click the button on the main interface to send a forced offline broadcast, a dialog box will pop up that cannot be canceled. When we click the confirm button, all activities will be destroyed and jump to the login interface.



syntaxbug.com © 2021 All Rights Reserved.