广播的最佳实践——实现强制下线功能


/**
* 在界面上弹出一个对话框,让用户无法进行任何其他操作,必须要点击对话框中的确定按钮,然后回到登录界面
* 强制下线功能需要先关闭掉所有的活动,然后回到登录界面
*/


1. 定义一个ActivityCollector.java类,来收集活动,主要是为了关闭所有活动

public class ActivityCollector {    //创建活动收集类,以定义所有Activity关闭的方法
    public static List<Activity> activities = new ArrayList<Activity>();

    public static void addActivity(Activity activity) {    //将Activity添加到集合
        activities.add(activity);
    }

    public static void removeActivity(Activity activity) {    //将Activity从集合移除
        activities.remove(activity);
    }

    public static void finishAll() {    //关闭集合中所有Activity
        for (Activity activity : activities) {
            if (!activity.isFinishing()) {
                activity.finish();
            }
        }
    }
}

2. 创建一个BaseActivity,让所有Activity继承,因为所有Activity都需要该类下的onCreate和onDestroy方法

public class BaseActivity extends AppCompatActivity {    //作为所有类的父类
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {    //在活动创建的时候,就将该活动加入到ActivityCollector中
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy() {  //在活动销毁的时候,将该活动从ActivityCollector中移除,因为该活动已经结束不用再在ActivityCollector结束
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

3. 登录界面布局

<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:stretchColumns="1">

    <TableRow>

        <TextView
            android:layout_height="wrap_content"
            android:text="Account:" />

        <EditText
            android:id="@+id/account"
            android:layout_height="wrap_content"
            android:hint="Input your account" />
    </TableRow>

    <TableRow>

        <TextView
            android:layout_height="wrap_content"
            android:text="Password:" />

        <EditText
            android:id="@+id/password"
            android:layout_height="wrap_content"
            android:inputType="textPassword" />
    </TableRow>

    <TableRow>

        <Button
            android:id="@+id/login"
            android:layout_height="wrap_content"
            android:layout_span="2"
            android:text="Login" />
    </TableRow>
</TableLayout>


4. 登录界面的LoginActivity

public class LoginActivity extends BaseActivity {
    @BindView(R.id.et_account)
    EditText etAccount;
    @BindView(R.id.et_password)
    EditText etPassword;
    @BindView(R.id.btn_login)
    Button btnLogin;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.btn_login)
    public void onClick() {
        String account = etAccount.getText().toString();
        String password = etPassword.getText().toString();
        //账号:admin 密码:123456 登录成功
        if (account.equals("admin") && password.equals("123456")) {
            Intent intent = new Intent(this, MainActivity.class);
            startActivity(intent);
            finish();
        } else {
            Toast.makeText(this, "账号或密码错误", Toast.LENGTH_SHORT).show();
        }
    }
}

5. 登录成功后的界面MainActivity

将MainActivity 理解成是登录成功后进入的程序主界面,只需要加入强制下线功能。

强制用户下线的逻辑并不是写在MainActivity里的,而是应该写在接收这条广播的广播接收器里面,这样强制下线的功能就不会依附于任何的界面,不管是在程序的任何地方,只需要发出这样一条广播,就可以完成强制下线的操作了 

public class MainActivity extends BaseActivity {
    @BindView(R.id.btn_force_offline)
    Button btnForceOffline;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.btn_force_offline)
    public void onClick() {
        //发送了一条广播,广播的值为com.example.p206.FORCE_OFFLINE,这条广播就是用于通知程序强制用户下线的
        Intent intent = new Intent("com.example.p206.FORCE_OFFLINE");
        sendBroadcast(intent);
    }
}

6. 创建广播接收器ForceOfflineReceiver

public class ForceOfflineReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(final Context context, Intent intent) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setTitle("警告")
                .setMessage("您已强制下线,请重新登录")
                .setCancelable(false)   //不可以点击返回键取消对话框
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        ActivityCollector.finishAll();  //销毁所有活动
                        Intent intent1 = new Intent(context, LoginActivity.class);  //重启LoginActivity
                        intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    //因为是在广播接收器里启动活动的,因此一定要给Intent加入FLAG_ACTIVITY_NEW_TASK这个标志
                        context.startActivity(intent1);
                    }
                });
        AlertDialog dialog = builder.create();
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);   //把AlertDialog的类型设为TYPE_SYSTEM_ALERT,不然它将无法在广播接收器里弹出
        dialog.show();
    }
}

7. AndroidManifest.xml

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />    //权限

<activity android:name=".LoginActivity">    //主活动界面LoginActivity
    <intent-filter> 
        <action android:name="android.intent.action.MAIN" /> 
        <category android:name="android.intent.category.LAUNCHER" /> 
    </intent-filter>
</activity>
<activity android:name=".MainActivity" />

<receiver android:name=".ForceOfflineReceiver">    //广播接收器
    <intent-filter> 
        <action android:name="com.example.p206.FORCE_OFFLINE" />
    </intent-filter>
</receiver>