很多网页或者APP都是通过SessionID(会话ID)或者Token(令牌)和服务器进行交互,以保证交互过程,客户端的唯一性。那么如果账号出现了二次登陆,应该如何处理呢?本文模拟服务器消息,然后简单实现了强制当前账号下线的功能。
一、大概说一下思路,客户端和服务器保持着通讯连接,当服务器检测到同一账号在另一个地方登陆的时候,会给当前账号发送消息,当APP监听到账号二次登陆的消息之后(本地要先实现对用户登陆状态的监听功能),会发送一个本地广播,然后让用户选择关闭或者重新登陆。
效果图:
二、具体实现代码:
- MyBaseActivity.java
*
* @author SHI
* 所有Activity的父类
* 2016-2-1 11:41:42
*
*/
public abstract class MyBaseActivity extends AppCompatActivity {
/**当前Activity对象**/
public FragmentActivity mContext;
/**当前设备宽度**/
public int displayDeviceWidth;
/**当前设备高度**/
public int displayDeviceHeight;
/**初始化布局文件**/
public abstract void initView();
/**初始化页面数据**/
public abstract void initData();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
ActivityCollector.addActivity(this);
mContext = this;
displayDeviceWidth = getResources().getDisplayMetrics().widthPixels;
displayDeviceHeight = getResources().getDisplayMetrics().heightPixels;
initView();
initData();
}
@Override
protected void onDestroy() {
ActivityCollector.removeActivity(this);
super.onDestroy();
}
}
- ActivityCollector.java
/***
* activity管理工具类,方便同时操作所有Activity
* @author SHI
* 2016-6-29 17:04:01
*/
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<Activity>();
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();
}
}
}
}
- LoginActivity.java代码
/**
* 2016年6月29日 16:45:25
* SHI
* 登陆界面
*/
public class LoginActivity extends MyBaseActivity{
private EditText mEmailView;
private EditText mPasswordView;
public final static String regExpEmailNumber = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
@Override
public void initView() {
setContentView(R.layout.activity_login);
mEmailView = (EditText) findViewById(R.id.email);
mPasswordView = (EditText) findViewById(R.id.password);
Button login = (Button) findViewById(R.id.login);
Button press_logout = (Button) findViewById(R.id.press_logout);
login.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
login();
}
});
}
@Override
public void initData() {
}
private void login() {
mEmailView.setError(null);
mPasswordView.setError(null);
String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();
View focusView = null;
if (TextUtils.isEmpty(password) || !isPasswordValid(password))
{
mPasswordView.setError("输入的密码太短了");
focusView = mPasswordView;
}
if (TextUtils.isEmpty(email)) {
mEmailView.setError(getString(R.string.error_field_required));
focusView = mEmailView;
} else if (!isEmailValid(email)) {
mEmailView.setError(getString(R.string.error_invalid_email));
focusView = mEmailView;
}
if (focusView !=null) {
focusView.requestFocus();
} else {
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
finish();
}
}
//是否是合法邮箱
private boolean isEmailValid(String email) {
if(TextUtils.isEmpty(email)){
return false;
}
return email.matches(regExpEmailNumber);
}
//密码是否合法
private boolean isPasswordValid(String password) {
return password.length() > 4;
}
}
- activity_login.xml代码,登陆界面对应的布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.shi.androidstudy.presslogout.LoginActivity">
<ScrollView
android:id="@+id/login_form"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/email_login_form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="邮箱"
android:inputType="textEmailAddress"
android:maxLines="1"
android:singleLine="true" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="密码"
android:inputType="textPassword"
android:maxLines="1"
android:singleLine="true" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/login"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="登陆"
android:textStyle="normal" />
</LinearLayout>
</ScrollView>
</LinearLayout>
- MainActivity.java代码
/**
* 2016年6月29日 16:45:38
* SHI
* 登陆成功进入的主界面,在这个界面模拟9秒之后收到服务器返回的当前用户二次登
* 陆消息,然后发送强制用户下线广播
*/
public class MainActivity extends MyBaseActivity {
private int numDelay = 9;
private Button press_logout;
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if(numDelay > 1){
numDelay--;
press_logout.setText(numDelay+"秒后强制下线");
handler.sendEmptyMessageDelayed(0,1000);
return false;
}
press_logout.setText("强制下线");
Intent intent = new Intent("com.shi.androidstudy.PRESS_LOGOUT");
sendBroadcast(intent);
return true;
}
});
@Override
public void initView() {
setContentView(R.layout.activity_main);
press_logout = (Button) findViewById(R.id.press_logout);
handler.sendEmptyMessageDelayed(0,1000);
}
@Override
public void initData() {
}
@Override
protected void onDestroy() {
handler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
- activity_main.xml 登陆成功主界面对应的布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.shi.androidstudy.presslogout.MainActivity">
<Button
android:id="@+id/press_logout"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="9秒后强制下线"
android:textStyle="normal" />
</RelativeLayout>
- 强制用户下线广播接受者
接收到强制用户下线广播之后,弹出AlertDialog,让用户选择
重新登陆(关闭当前所有Activity重新打开登陆界面) 还是 确定(关闭应用程序),不过谷歌官方不推荐使用AlertDialog,所以,所以也可以打开一个新的Activity去展示提示信息,强制用户下线。
/**
* 广播接受者
* 2016年6月29日 16:46:12
* SHI
*/
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("下线通知");
String line = System.getProperties().getProperty("line.separator");
builder.setMessage("账号在别处登陆,您被迫下线。"+line+"若非本人操作,请尽快修改密码。");
builder.setCancelable(false);
builder.setPositiveButton("重新登陆", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCollector.finishAll();
Intent intent = new Intent(context,LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
});
builder.setNegativeButton("确定",new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCollector.finishAll();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);//点击
alertDialog.show();
}
}
好了,到这里基本就实现了强制当前账号下线的功能了,代码很简单。
最后附上demo下载地址:点击打开