提示:此文章仅作为本人记录日常学习使用,若有存在错误或者不严谨得地方,欢迎各位在评论中指出。
文章目录
一、广播实战:实现强制下线功能
1.1 实现全局管理Actiity功能
我们新创建一个BoradcastBestPractice项目,并创建一个ActivityController的单例类用来管理全局的Activity。通过ActivityController我们不论在任何界面,只需要调用finishAllActivities()方法就可以关闭所有界面。
/**
* 用于管理全局Activity的单例类
*/
object ActivityController {
//用于管理所有Activity的集合
private val activities = ArrayList<Activity>()
//往集合中添加Activity
fun addActivity(targetActivity: Activity) {
activities.add(targetActivity)
}
//从集合中移除Activity
fun removeActivity(targetActivity: Activity) {
activities.remove(targetActivity)
}
//关闭集合中的所有的Activity
fun finishAllActivities() {
for (activity in activities) {
//判断Activity是否正在销毁中
if (!activity.isFinishing) {
activity.finish()
}
}
//销毁所有Activity后清空集合
activities.clear()
}
}
然后我们创建一个BaseActivity作为所有Activity的父类,这样便于我们对全局Activity进行管理。我们在BaseActivity的onCreate()方法和onDestroy()方法中实现了Activity集合元素的添加和移除。当某个Activity继承自BaseActivity后,在该Activity的生命周期中会自动调用父类BaseActivity的onCreate()和onDestroy()方法,进而实现Activity集合的添加和移除:
/**
* 全局Activity的父类
*/
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
//将当前Activity添加到全局Activity列表中
ActivityController.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
//从全局Activity列表中移除当前Activity
ActivityController.removeActivity(this)
}
}
1.2 登录界面布局
接下来我们创建一个LoginActivity.kt作为登录界面的Activity,并让AS为我们自动创建与之匹配的布局。首先我们修改登录界面的布局:
<?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">
<!--账号输入区域-->
<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="账号:"
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>
<!--密码输入区域-->
<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="密码:"
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>
<!--登陆按钮-->
<Button
android:id="@+id/myLoginButton"
android:layout_width="200dp"
android:layout_height="60dp"
android:layout_gravity="center_horizontal"
android:text="登陆"
android:textSize="18sp" />
</LinearLayout>
1.3 登录界面Activity
然后我们让LoginActivity继承自BaseActivity,并实现非常简单的登陆逻辑。当我们输入给定的账号和密码后,点击登陆就会跳转到主界面MainActivity,否则就提示用户重新输入。:
class LoginActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
//登陆按钮点击事件
myLoginButton.setOnClickListener {
val account = myAccountEdit.text.toString()
val password = myPasswordEdit.text.toString()
if (account == "admin" && password == "123456") {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
} else {
Toast.makeText(this, "账号或密码错误,清重新输入!", Toast.LENGTH_SHORT).show()
myPasswordEdit.setText("")
}
}
}
}
1.4 主界面布局
接下来我们为主界面activity_main.xml添加一个按钮用来发送强制下线的广播:
<?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="发送强制下线广播" />
</LinearLayout>
1.5 主界面发送广播功能
接下来我们在MainActivity.kt中实现按钮的点击逻辑。当用户点击按钮后会发送一条FORCE_OFFLINE的标准广播,用来通知应用程序强制用户下线。
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//按钮点击逻辑
forceOfflineButton.setOnClickListener {
//创建意图
val intent = Intent("com.example.boradcastbestpractice.FORCE_OFFLINE")
//发送标准广播
sendBroadcast(intent)
}
}
}
1.6 实现强制下线功能
现在我们还需要创建一个BroadcastReceiver来接收这条强制用户下线的广播,那么我门应该在哪里创建呢?我们希望在BroadcastReceiver中弹出一个AlertDialog来阻止用户的操作。但是如果通过静态注册的方法来创建的话是没有办法在onReceive()方法中弹出一个AlertDialog这样的UI控件。因为onReceive()方法是在后台线程中运行的,而AndroidUI控件操作是需要在应用的主线程中执行的,所以我们只能通过显式的方式来注册BroadcastReceiver。
为了解决这个问题,我们可以在BaseActivity中动态注册一个BroadcastReceiver,因为所有的Activity都是继承自BaseActivity的。
修改BaseActivity的代码:
/**
* 全局Activity的父类
*/
open class BaseActivity : AppCompatActivity() {
//强制下线广播接收器
lateinit var myForceOffLineReceiver: ForceOffLineReceiver
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
//将当前Activity添加到全局Activity表中的
ActivityController.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
//从全局Activity表中移除当前Activity
ActivityController.removeActivity(this)
}
//内部类 强制下线广播接收器
inner class ForceOffLineReceiver : BroadcastReceiver() {
//广播接收逻辑
override fun onReceive(context: Context, intent: Intent) {
//创建一个对话框
AlertDialog.Builder(context)?.apply {
setTitle("警告")
setMessage("你已被强制下限,请重新登录.")
//不可取消
setCancelable(false)
//确认按钮点击逻辑
setPositiveButton("好的") { _, _ ->
//销毁Activity列表中所有的Activity
ActivityController.finishAllActivities()
//跳转到登录界面
val intent = Intent(context, LoginActivity::class.java)
context.startActivity(intent)
}
create()
show()
}
}
}
override fun onResume() {
super.onResume()
//创建广播过滤器
val myIntentFilter = IntentFilter()
//为过滤器添加action
myIntentFilter.addAction("com.example.boradcastbestpractice.FORCE_OFFLINE")
//初始化广播接收器
myForceOffLineReceiver = ForceOffLineReceiver()
//隐式注册广播接收器
registerReceiver(myForceOffLineReceiver, myIntentFilter)
//在Android 13或者更高版本中你需要额外添加一个参数来指明是否允许该广播接收器接收来自其他应用的广播 如果不添加该参数则会闪退
//registerReceiver(mForceOffLineReceiver, myIntentFilter, RECEIVER_EXPORTED)
}
override fun onPause() {
super.onPause()
//注销广播接收器
unregisterReceiver(myForceOffLineReceiver)
}
}
我们在BaseActivity中通过内部类的方式声明了一个强制下线的广播接收器ForceOffLineReceiver,让广播接收器收到广播后就会弹出一个对话框进行提示,当用户点击确认按钮后就会销毁所有的Activity并跳转到重新登陆界面。
我们把注册和注销ForceOffLineReceiver的逻辑放到了onResume()和onPause()方法中,这是因为我们需要确保只有处于栈顶的Activity才能够接收到这条强制下线的广播,非栈顶的Activity是不应该也没有必要接收到这条广播的。
- onResume( ):Activity 准备好和用户进行交互 时调用,此时Activity可见(位于栈顶)并且可交互。
- onPause( ):另一个Activity(未占满屏幕或者全透明)跑到前台时,原来Activity的onPause( )方法会被调用,此时原来的Activity不可见。
这样我们强制下线的逻辑就已经编写完毕了,最后还需要在AndroidManifest.xml将程序主界面设置为LoginActivity而不是MainActivity(我们肯定希望用户登陆后才能进入程序主界面):
<?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>
设置登录界面为应用程序主界面
<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 运行效果
到这里我们输入设置的账号和密码就会从LoginActivity登录界面跳转到应用主界面MainActivity了。当我们点击主界面的按钮发送强制下线广播后,会弹出一个无法取消的对话框,当我们点击确认按钮后会销毁所有Activity并跳转到登录界面。