Android学习总结(八)———— 广播的最佳实践(实现强制下线功能)

一.基本概念

  强制下线功能功能应该算是比较常见的了,很多应用程序都具备这个功能,比如你的QQ号或者微信号在别处登录了,就会将你强制挤下线。只需要在界面上弹出一个对话框,让用户无法进行任何其他的操作,必须要点击对话框中的确定按钮,然后回到登录界面即可。可是这样就存在着一个问题,因为我们被通知需要强制下线时可能正处于任何一个界面,难道需要在每一个界面都编写一个弹出对话框的逻辑吗?如果真的是这样想那么思维就偏远了,我们完全可以借助所学的广播知识来实现这一个功能。

二.代码之旅

  强制下线功能需要先关闭所有的活动,然后回到登录界面,代码如下所示:

package com.nyl.nativebroadcast;

import android.app.Activity;

import java.util.ArrayList;
import java.util.List;


public class ActivityCollector {

    public static List<Activity> activities = new ArrayList<>();

    public static void addActivity(Activity activity){
        activities.add(activity);
    }

    public static void removeActivity(Activity activity){
        activities.remove(activity);
    }
    public static void finishAll(){
        for (Activity activity : activities){
            if (!activity.isFinishing()){
                activity.finish();
            }
        }
    }
}

  创建一个BaseActivity类作为所有活动的父类,代码如下所示:

package com.nyl.nativebroadcast;

import android.app.Activity;
import android.os.Bundle;


public class BaseActivity extends Activity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

  创建一个登录界面的布局,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_login"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
   android:stretchColumns="1">

    <TableRow>

        <TextView
            android:layout_height="wrap_content"
            android:text="账号:"/>

        <EditText
            android:id="@+id/etAccount"
            android:layout_height="wrap_content"
            android:hint="请输入账号"/>
    </TableRow>

    <TableRow>

        <TextView
            android:layout_height="wrap_content"
            android:text="密码:"/>

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

    <TableRow>
        <Button
            android:id="@+id/btnLogin"
            android:layout_height="wrap_content"
            android:layout_span="2"
            android:text="登录"/>
    </TableRow>

</TableLayout>

  新建一个LoginActivity继承BaseActivity,代码如下所示:

package com.nyl.nativebroadcast;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class LoginActivity extends BaseActivity implements View.OnClickListener {

    private EditText etAccount;
    private EditText etPassword;
    private Button btnLogin;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        initView(); //初始化布局
    }

    private void initView() {
        etAccount = (EditText) findViewById(R.id.etAccount);
        etPassword = (EditText) findViewById(R.id.etPassword);
        btnLogin = (Button) findViewById(R.id.btnLogin);

        btnLogin.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        //获取用户输入的账号,密码的数据
        String account = etAccount.getText().toString();
        String password = etPassword.getText().toString();
        //如果账号是123且密码是123,就登录成功
        if (account.equals("123") && password.equals("123")){
            Intent intent = new Intent(LoginActivity.this,MainActivity.class);
            startActivity(intent);
            finish();
        }else {
            Toast.makeText(LoginActivity.this,"账号或密码是无效的",Toast.LENGTH_SHORT).show();
        }
    }
}

  同样在MainActivity继承BaseActivity,一个非常简单的MainActivity,不过多解释,代码如下所示:

package com.nyl.nativebroadcast;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends BaseActivity implements View.OnClickListener {

    private Button btnOffline;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnOffline = (Button) findViewById(R.id.btnOffline);

        btnOffline.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
        Intent intent = new Intent("com.nyl.nativebroadcast.ForceOfflineReceiver");
        sendBroadcast(intent);
    }
}

  创建一个广播接收器,代码如下所示:

package com.nyl.nativebroadcast;

import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.view.WindowManager;


public class ForceOfflineReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(final Context context, Intent intent) {
        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
        dialogBuilder.setTitle("警告");
        dialogBuilder.setMessage("你被迫离线,请重新登录!");
        dialogBuilder.setCancelable(false);
        dialogBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                /**
                 * 调用ActivityCollector的finishAll()方法来销毁掉所有的活动,
                 * 并重新启动LoginActivity这个活动
                 */
                ActivityCollector.finishAll();//销毁所以活动
                Intent intent = new Intent(context,LoginActivity.class);
                /**
                 * 在广播接收器里启动活动,因此一定要给Intent加入FLAG_ACTIVITY_NEW_TASK这个标准
                 */
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
            }
        });
        AlertDialog alertDialog = dialogBuilder.create();
        //需要设置AlertDialog的类型,保证在广播接收器中可以正常弹出
        alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        alertDialog.show();
    }
}

  最后不要忘记在AndroidManifest.xml文件进行配置,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.nyl.nativebroadcast">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        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>
        
        <receiver android:name=".ForceOfflineReceiver">
            <intent-filter>
                <action android:name="com.nyl.nativebroadcast.ForceOfflineReceiver"/>
            </intent-filter>
        </receiver>
    </application>

</manifest>

  好了,现在尝试运行一下程序吧,首先进入到登录界面,并可以在这里输入账号和密码,如下图:

  

  如果输入的账号是:123,密码是123,点击登录按钮就会进入到程序的主界面,如下图:

  

  这时点击一下发送强制用户下线的广播按钮,就会发出一条强制下线的广播,ForceOfflineReceiver里收到这条广播后会弹出一个对话框提示用户已被强制下线,如下图所示:

  

  这时用户将无法再对界面的任何元素进行操作,只能点击OK按钮,然后回重新回到登录界面。这样,强制下线功能就已经完整的实现了。

posted @ 2017-03-08 13:13  ButterflyGirl  阅读(1293)  评论(0编辑  收藏  举报