说说在 Android 中如何实现强制下线功能
在应用程序中的一个常见功能是 “强制下线”。比如 QQ 号在别处登录后,就会把当前的 QQ 号挤下线。实现思路是:在界面上弹出一个对话框,让用户无法进行任何其他操作,只能点击对话框中的确定按钮, 然后回到登录界面。
要实现强制下线之前,必须先关闭所有的活动。首先,创建一个 Activities 类用于管理所有的活动:
public class Activities {
public static List<Activity> activities = new ArrayList<>();
/**
* 新增活动
*
* @param activity
*/
public static void add(Activity activity) {
activities.add(activity);
}
/**
* 移除活动
*
* @param activity
*/
public static void remove(Activity activity) {
activities.remove(activity);
}
/**
* 结束所有活动
*/
public static void finishAll() {
for (Activity activity : activities) {
if (activity.isFinishing()) {
activity.finish();
}
}
}
}
然后创建 BaseActivity 类作为所有活动的父类:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Activities.add(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
Activities.remove(this);
}
}
接下来要创建一个登录界面,新建 LoginActivity(在 Android Studio 中,建议创建空的活动),编辑 activity_login.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
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:text="账号:"
android:textSize="18sp" />
<EditText
android:id="@+id/account"
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:text="密码:"
android:textSize="18sp" />
<EditText
android:id="@+id/password"
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/login"
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="登录" />
</LinearLayout>
我们使用 LinearLayout 编写了一个登陆布局,使用纵向排列,从上到下分别是账号、密码和登陆按钮。
接着修改 LoginActivity 中的代码:
public class LoginActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
final EditText accountEditText=(EditText)findViewById(R.id.account);
final EditText passwordEditText=(EditText)findViewById(R.id.password);
Button loginBtn=(Button)findViewById(R.id.login);
loginBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String account=accountEditText.getText().toString();
String password=passwordEditText.getText().toString();
if(account.equals("admin")&&password.equals("1")){//登录成功
startActivity(new Intent(LoginActivity.this,MainActivity.class));
finish();
}else{//登录失败,弹出提示
Toast.makeText(LoginActivity.this, "账号或密码不正确", Toast.LENGTH_SHORT).show();
}
}
});
}
}
LoginActivity 继承自 BaseActivity。然后获取账号与密码的输入值,进行判断。如果登录成功,就启动 MainActivity 活动;否则登录失败,弹出提示。
这里假设把 MainActivity作为登陆成功后进入的主界面,修改对应的布局文件:
<?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:gravity="center_horizontal"
android:orientation="vertical">
<Button
android:id="@+id/force_offline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="发送强制下线广播"
/>
</LinearLayout>
这里,我们只定义了一个按钮,用于触发强制下线功能。然后修改 MainActivity 中的代码:
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.force_offline).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendBroadcast(new Intent("net.deniro.android.FORCE_OFFLINE"));
}
});
}
}
在按钮的点击事件中发送了一条广播,用于通知程序让强制用户下线。
强制用户下线的逻辑是写在接收这条广播的广播接收器里面,这样强制下线的功能就不会依附于任何的界面,不管是在程序的任何地方,只需要发出这样一条广播,就可以完成强制下线的操作啦O(∩_∩)O~
我们在 BaseActivity 中动态注册了一个广播接收器,因为所有的活动都是继承自 BaseActivity 的,这样这些活动就都可以支持这个广播接收器咯。
修改 BaseActivity 中的代码:
public class BaseActivity extends AppCompatActivity {
private ForceOffLineReceiver receiver;
...
private class ForceOffLineReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("警告");
builder.setMessage("您已被迫下线,请重新登录!");
builder.setCancelable(false);
builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Activities.finishAll();//销毁所有活动
context.startActivity(new Intent(context, LoginActivity.class));//启动【登录】活动
}
});
builder.show();
}
}
@Override
protected void onResume() {
super.onResume();
//注册广播接收器
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("net.deniro.android.FORCE_OFFLINE");
receiver = new ForceOffLineReceiver();
registerReceiver(receiver, intentFilter);
}
@Override
protected void onPause() {
super.onPause();
if(receiver!=null){//注销
unregisterReceiver(receiver);
}
receiver=null;
}
}
这里使用了 AlertDialog,并 setCancelable() 让对话框不可取消(如果可取消,用户点击一下 back 键,对话框就没啦),然后使用 setPositiveButton() 方法定义【确定】按钮,当用户点击按钮时,销毁所有活动并/启动【登录】活动。
我们重写了 onResume() 和 onPause() 这两个生命周期的函数,然后分别在这两个方法里注册和注销了自定义的广播接收器,之所以这样写是因为我们要始终需要保证只有处于栈顶的活动才能接收到下线广播,非栈顶活动不需要也没有必要接收这条广播。
最后修改 AndroidManifest.xml:
<activity
android:name=".LoginActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
将 LoginActivity 设置为主活动。
运行程序:

输入正确的账号和密码后,点击登录进入主界面:

点击【发送强制广播】按钮:
