• Android开发基础——广播实践


    在一些社交帐号中,强制下线是一个比较常见的功能,比如异地登陆。而实现强制下线功能的思路其实只是在界面上弹出一个对话框,让用户无法进行任何操作,比如点击对话框回到登录界面。这种功能就可以借助广播功能来实现。

    强制下线功能需要先关闭所有的Activity,然后回到登录界面。在此之前,先创建一个ActivityCollector类用于管理所有的Activity:

    1. package com.example.broadcastbestpractice
    2. import android.app.Activity
    3. object ActivityCollector {
    4. private val activities = ArrayList()
    5. fun addActivity(activity: Activity) {
    6. activities.add(activity)
    7. }
    8. fun removeActivity(activity: Activity) {
    9. activities.remove(activity)
    10. }
    11. fun finishAll() {
    12. for (activity in activities) {
    13. if (!activity.isFinishing) {
    14. activity.finish()
    15. }
    16. }
    17. activities.clear()
    18. }
    19. }

    然后创建BaseActivity类作为所有Activity的父类:

    1. open class BaseActivity :AppCompatActivity() {
    2. override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
    3. super.onCreate(savedInstanceState, persistentState)
    4. ActivityCollector.addActivity(this)
    5. }
    6. override fun onDestroy() {
    7. super.onDestroy()
    8. ActivityCollector.removeActivity(this)
    9. }
    10. }

    再创建一个LoginActivity作为登录界面,并编写对应的布局activity_login.xml:

    1. "1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:orientation="vertical"
    4. android:layout_width="match_parent"
    5. android:layout_height="match_parent">
    6. <LinearLayout
    7. android:orientation="horizontal"
    8. android:layout_width="match_parent"
    9. android:layout_height="60dp">
    10. <TextView
    11. android:layout_width="90dp"
    12. android:layout_height="wrap_content"
    13. android:layout_gravity="center_vertical"
    14. android:textSize="18sp"
    15. android:text="Account:"/>
    16. <EditText
    17. android:id="@+id/accountEdit"
    18. android:layout_width="0dp"
    19. android:layout_height="wrap_content"
    20. android:layout_weight="1"
    21. android:layout_gravity="center_vertical"/>
    22. LinearLayout>
    23. <LinearLayout
    24. android:orientation="horizontal"
    25. android:layout_width="match_parent"
    26. android:layout_height="60dp">
    27. <TextView
    28. android:layout_width="90dp"
    29. android:layout_height="wrap_content"
    30. android:layout_gravity="center_vertical"
    31. android:textSize="18sp"
    32. android:text="Password:"/>
    33. <EditText
    34. android:id="@+id/passwordEdit"
    35. android:layout_width="0dp"
    36. android:layout_height="wrap_content"
    37. android:layout_weight="1"
    38. android:layout_gravity="center_vertical"
    39. android:inputType="textPassword"/>
    40. LinearLayout>
    41. <Button
    42. android:id="@+id/login"
    43. android:layout_width="200dp"
    44. android:layout_height="60dp"
    45. android:layout_gravity="center_vertical"
    46. android:text="Login"/>
    47. LinearLayout>

    这里编写了一个登录界面,也很好理解。然后修改LoginActivity中的代码:

    1. class LoginActivity : BaseActivity() {
    2. override fun onCreate(savedInstanceState: Bundle?) {
    3. super.onCreate(savedInstanceState)
    4. setContentView(R.layout.activity_login)
    5. login.setOnClickListener {
    6. val account = accountEdit.text.toString()
    7. val password = passwordEdit.text.toString()
    8. if (account == "admin" && password == "123456") {
    9. val intent = Intent(this, MainActivity::class.java)
    10. startActivity(intent)
    11. finish()
    12. } else {
    13. Toast.makeText(this, "account and password is invalid", Toast.LENGTH_SHORT).show()
    14. }
    15. }
    16. }
    17. }

    这里的代码也很好理解,就是获取布局中的账号和密码,然后匹配一致就启动MainActivity,否则就提示错误。

    这里在MainActivity中加入强制下线功能,修改activity_main.xml:

    1. "1.0" encoding="utf-8"?>
    2. "http://schemas.android.com/apk/res/android"
    3. android:orientation="vertical"
    4. android:layout_width="match_parent"
    5. android:layout_height="match_parent">
    6. android:id="@+id/forceOffline"
    7. android:layout_width="match_parent"
    8. android:layout_height="wrap_content"
    9. android:text="Send force offline broadcast" />

    上面代码也很好理解,只是加入了一个按钮。

    修改MainActivity,添加对应的点击事件:

    1. class MainActivity : BaseActivity() {
    2. override fun onCreate(savedInstanceState: Bundle?) {
    3. super.onCreate(savedInstanceState)
    4. setContentView(R.layout.activity_main)
    5. forceOffline.setOnClickListener {
    6. val intent = Intent("com.example.broadcastbestpractice.FORCE_OFFLINE")
    7. sendBroadcast(intent)
    8. }
    9. }
    10. }

    同样很好理解,在点击事件触发后发了一条广播。而对应接收广播的逻辑并不需要添加到MainActivity中,因为不管什么时候接收了该广播,都要做强制下线处理。

    这里创建BroadcastReceiver来接收该广播,而应该在哪里创建呢?首先BroadcastReceiver接收到广播后是需要弹出对话框来阻塞用户的操作的,但如果是静态注册,是没有办法在onReceiver方法中弹出对话框这种UI控件的。

    因此这里的BroadcastReceiver需要放在BaseActivity中进行动态注册,因为所有的Activity都继承自BaseActivity。修改BaseActivity:

    1. open class BaseActivity :AppCompatActivity() {
    2. lateinit var receiver: ForceOfflineReceiver
    3. override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
    4. super.onCreate(savedInstanceState, persistentState)
    5. ActivityCollector.addActivity(this)
    6. }
    7. override fun onResume() {
    8. super.onResume()
    9. val intentFilter = IntentFilter()
    10. intentFilter.addAction("com.example.broadcastbestpractice.FORCE_OFFLINE")
    11. receiver = ForceOfflineReceiver()
    12. registerReceiver(receiver, intentFilter)
    13. }
    14. override fun onPause() {
    15. super.onPause()
    16. unregisterReceiver(receiver)
    17. }
    18. override fun onDestroy() {
    19. super.onDestroy()
    20. ActivityCollector.removeActivity(this)
    21. }
    22. inner class ForceOfflineReceiver:BroadcastReceiver() {
    23. override fun onReceive(context: Context, intent: Intent?) {
    24. AlertDialog.Builder(context).apply {
    25. setTitle("Warning")
    26. setMessage("You are forced to be offline, please try to login again.")
    27. setCancelable(false)
    28. setPositiveButton("OK") { _, _ ->
    29. ActivityCollector.finishAll()
    30. val intent = Intent(context, LoginActivity::class.java)
    31. context.startActivity(intent)
    32. }
    33. show()
    34. }
    35. }
    36. }
    37. }

    首先是ForceOfflineReceiver中的onReceiver方法中使用AlertDialog.Builder构建了一个对话框,然后使用setCancelable使之不可取消。然后使用setPositiveButton方法注册了确定按钮,当用户点击OK按钮时,就调用ActivityCollector的finishAll方法销毁所有Activity,并重新启动LoginActivity。

    同时在onResume和onPause方法中分别注册和去注册了ForceOfflineReceiver,这是因为需要始终保证只有处于栈顶的Activity才能接收强制下线广播,非栈顶的Activity没有必要接收该广播。

    最后修改AndroidManifest.xml:

    1. "1.0" encoding="utf-8"?>
    2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    3. package="com.example.broadcastbestpractice">
    4. <application
    5. android:allowBackup="true"
    6. android:icon="@mipmap/ic_launcher"
    7. android:label="@string/app_name"
    8. android:roundIcon="@mipmap/ic_launcher_round"
    9. android:supportsRtl="true"
    10. android:theme="@style/Theme.BroadcastBestPractice">
    11. <activity
    12. android:name=".LoginActivity"
    13. android:exported="true">
    14. <intent-filter>
    15. <action android:name="android.intent.action.MAIN" />
    16. <category android:name="android.intent.category.LAUNCHER" />
    17. intent-filter>
    18. activity>
    19. <activity
    20. android:name=".MainActivity"
    21. android:exported="true">
    22. activity>
    23. application>
    24. manifest>

    这里只是将主Activity设置为了LoginActivity,而不是MainActivity。程序运行后的结果为:

     发送广播后被接收后,就会回到登录界面,也就实现了强制下线。

  • 相关阅读:
    100 行代码实现用户登录注册与 RESTful 接口 - 手把手教程附 Python 源码
    算法 - 路径总和
    蓝桥杯KMP总结
    Scala---数据基础
    毫米波雷达模块技术革新:在自动驾驶汽车中的前沿应用
    ffmpeg编译so
    【初识C语言】/*有关C语言函数部分细节展示*/
    HTML入门
    AcWing 4. 多重背包问题 I 学习笔记
    从工厂打螺丝到月薪9.5k测试工程师,我该满足吗?
  • 原文地址:https://blog.csdn.net/SAKURASANN/article/details/127040820