我们来利用动态广播机制写一个应用的强制下线功能,首先我们要清楚业务需求,强制下线应该关闭所有Activity 并且打开登录界面的Activity,那么好,首先我们来编写一个Activity 管理类,也就是第三章最佳实践的知识点。
ActivityCollector 单例类:
object ActivityCollector {
private val activitys = ArrayList<Activity>()
fun addActivity(activity: Activity){
activitys.add(activity)
}
fun removeActivity(activity: Activity){
activitys.remove(activity)
}
fun finishAll(){
for (activity in activitys) {
if (!activity.isFinishing){
activity.finish()
}
}
activitys.clear()
}
}
然后我们创建一个BaseActivity 继承AppCompatActivity ,让所有Activity 都继承BaseActivity
open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityCollector.addActivity(this)
}
override fun onDestroy() {
super.onDestroy()
ActivityCollector.removeActivity(this)
}
}
然后我们来写登录界面,首先创建一个LoginActivity 让AndroidStudio 自动帮我们创建布局:
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoginActivity">
<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: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: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: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_horizontal"
android:text="Login"
/>
</LinearLayout>
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()
// 如果账号是admin 且密码是 123456 就认为登录成功
if (account == "admin" && password == "123456"){
val intent = Intent(this,MainActivity::class.java)
startActivity(intent)
finish()
}else{
Toast.makeText(this,"account or password is invalid",Toast.LENGTH_LONG).show()
}
}
}
}
一个登录界面,拿到账号和密码模拟效验,效验成功跳转到主页面。
我们在主页面加入一个功能,强制下线,也是本次的目的。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/forceOffline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send force offline broadcast"
/>
</LinearLayout>
MainActivity
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
forceOffline.setOnClickListener {
val intent = Intent("com.example.broadcastreceiverlogin.FORCE_OFFLINE")
sendBroadcast(intent)
}
}
}
发送一个广播,接收到这个广播的广播接收者执行强制退出功能。
那么有一个问题,谁来注册这个广播,我们知道MainActivity 只是来通知整个程序强制用户下线的,所以逻辑一定不是写在MainActivity 中,谁会无聊到自己通知自己去上厕所呢???而且还有一个问题,我们需要弹出一个提示框来阻塞用户的正常操作,这里就需要Activity的Context了,我们难道每个Activity 都要动态注册一个 广播吗,显然我们可以利用BaseActivity 来实现:
open class BaseActivity : AppCompatActivity() {
lateinit var receiver: ForceOfflineReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityCollector.addActivity(this)
}
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() // 销毁所有Activity
val i = Intent(context,LoginActivity::class.java)
context.startActivity(i)
}
}.show()
}
}
override fun onResume() {
super.onResume()
val intentFilter = IntentFilter("com.example.broadcastreceiverlogin.FORCE_OFFLINE")
receiver = ForceOfflineReceiver()
registerReceiver(receiver,intentFilter)
}
override fun onPause() {
super.onPause()
unregisterReceiver(receiver)
}
}
首先我们在BaseActivity 创建了一个广播接收者内部类 ForceOfflineReceiver,然后在 BaseActivity 创建了一个ForceOfflineReceiver的全局变量 receiver ,接下来我们在Activity 可见可用的时候注册广播,在Activity 不可见不可用的时候注销广播。广播的内部逻辑就是关闭所有Activity ,并且跳转到登录页面。
功能已经完成,我们将LoginActivity 设为主页面,毕竟每个应用都是从登录开始的。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcastreceiverlogin">
<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">
<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>
代码是没问题的,喜欢动手的可以自己敲一下,亲测完美运行。