在一些应用软件上我们经常会遇到诸如“您的XX账号在别处登录,请您重新登陆!”的问题,那么这是如何实现的呢?下面正是一个利用广播机制来实现强制下线的例子。
一、任务
1、首先创建一个ActivityCollector类用来管理活动,在这个类中添加相应的方法
2、然后创建一个BaseActivity类作为作为父类,在子类中覆盖该类的方法
3、设计一个登陆界面,首先创建登录界面的布局文件login.xml
4、创建LoginActivity类实现登陆界面
5、创建登陆后的主界面,同样也是创建布局文件main.xml
6、创建MainActivity类实现主界面
7、创建一个广播接收器类ForceOfflineReceiver,用于接收强制下线的广播
8、在注册文件中注册
二、原理图
三、主要实现
1、 创建ActivityCollector
在ActivityCollector活动管理类中创建一个List对象用来存储活动,前两个方法是添加和删除活动,第三个删除所有活动,其中在方法中的for(Activity activity:activities)指在集合中循环遍历,从右边取出一个活动赋予左边,然后判断是否已销毁。
package com.qzxx;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
public class ActivityCollector {
public static List<Activity> activities=new ArrayList<Activity>();//创建一个集合用来存储所有的Activity
//添加Activity
public static void addActivity(Activity activity){
activities.add(activity);
}
//删除一个Activity
public static void removeActivity(Activity activity){
activities.remove(activity);
}
//关闭所有的Activity
public static void finishAll(){
for(Activity activity:activities) //在列表中依次循环遍历
if(!activity.isFinishing()){
activity.finish();
}
}
}
2、创建BaseActivity
此类作为所有活动的父类,重写了Activity中的onCreate()方法和onDestroy()方法,而ActivityCollector.addActivity(this);指将当前活动添加到集合中,同理调用removeActivity()指移除当前活动。
package com.qzxx;
import android.app.Activity;
import android.os.Bundle;
public class BaseActivity extends Activity{
//创建一个类就添加到ActivityCollector中
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
ActivityCollector.addActivity(this);
}
//摧毁一个类就从ActivityCollestor移除
@Override
protected void onDestroy(){
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
3、 创建布局文件login.xml
使用TableLayout来布局,TableLayout是由多个TableRow组成,在此布局中包含三个TableRow(蓝色),第一TableRow包括一个TextView(红色)和一个EditText(绿色),用来输入用户名,第二个为密码输入,第三个为登陆按钮(黄色)。
其中属性android:stretchColumns=”1”指第二列被拉伸;android:layout_span=”2”指该单元格占两列
<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="@string/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="@string/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="@string/Login"/>
</TableRow>
</TableLayout>
4、创建LoginActivity
让LoginActivity继承BaseActivity,在此类中主要实现登录界面,获取各UI后,在button的监听器中获取编辑框内容,然后指定用户名以及密码,在点击按钮后在if语句中通过调用equals()方法来判断输入内容与给定内容是否一致,若相同创建Intent实例,然后跳转至MainActivity主界面中,如果输入错误利用Toast显示一段信息。
package com.qzxx;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class LoginActivity extends BaseActivity{
private EditText account;
private EditText password;
private Button login;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO 自动生成的方法存根
super.onCreate(savedInstanceState);
setContentView(R.layout.login);
//获取控件
account=(EditText)findViewById(R.id.account);
password=(EditText)findViewById(R.id.password);
login=(Button)findViewById(R.id.login);
login.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO 自动生成的方法存根
//获取编辑框内容转化为字符串
String accountStr=account.getText().toString();
String passwordStr=password.getText().toString();
//判断输入账户和密码是否为774982269和123456,若输入正确则跳入下一个Activity,否则显示重新输入
if(accountStr.equals("774982269")&&passwordStr.equals("123456")){
Intent intent = new Intent(LoginActivity.this,MainActivity.class);
startActivity(intent);
finish();
}
else{
Toast.makeText(LoginActivity.this,R.string.tip,Toast.LENGTH_LONG).show();
password.setText(null);
}
}
});
}
}
5、创建布局文件main.xml
在这里主界面只是一个简单的文本显示,当然还可以添加更多的功能。所以main.xml比较简单,使用RelativeLayout进行布局,绿色为文本,黄色为按钮。
在相对布局中组件不能自定义位置,只能通过父容器或其他组件进行设置,其中
android: android:layout_alignParentBottom=”true”指该组件与父容器下边沿对齐;
android:layout_centerHoriznotallow=”true”指该组件水平居中
当然还有android:layout_above 指该组件位于给定组件的上方;android:layout_toLeftOf 指该组件的左边缘与给定组件的右边缘对齐。其XML属性还有很多这里只介绍这么几个。
<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:background="@drawable/ab"
>
<Button
android:id="@+id/Force"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_margin="100sp"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="强制下线"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="您已成功进入主界面"
android:textSize="25dp"
android:layout_margin="30sp"/>
</RelativeLayout>
6、 创建MainActivity
在MainActivity中主要通过点击按钮,在监听器中创建Intent对象发送一个值”com.example.broadcasttext.FORCE_OFFLINE”,在广播接收器中添加与之相同的action,从而得到强制下线的效果。
package com.qzxx;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends BaseActivity {
private Button Force;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Force=(Button)findViewById(R.id.Force);
Force.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// TODO 自动生成的方法存根
Intent intent =new Intent("com.example.broadcasttext.FORCE_OFFLINE");
sendBroadcast(intent);
}
});
}
}
7、创建ForceOfflineReceiver
这一步正是整个过程最关键的一步,首先创建广播接收器ForceOfflineReceiver类继承BroadcastReceiver,然后重写onReceive()方法,在方法中创建一个AlertDialog.Bulider对象,然后设置其标题、提示信息并设为不可取消,接着设置按钮,在监听事件中调用ActivityCollector.finishAll()方法指在点击按钮后销毁所有活动,同时通过intent使该Activity跳转到LoginActivity,并设置一个重新进入到LoginActivity的标志intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),(本人开始不太理解,最后上网百度后才明白。因为存在一个存放Activity的集合task(与栈相似),而跳转操作一般是在Activity中实现的,使要启动的Activity直接进入task,如果一个非Activity要启动时,就必须创建一个新的task来存储Activity),然后创建一个AlertDialog对象,调用create()方法创建一个窗口实例,并设置其类型
在API文档中查看可知该窗口优先级很高,在弹出时始终处于应用的最上方。
package com.qzxx;
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) {
// TODO 自动生成的方法存根
//创建一个对话框提示用户被强制下线
AlertDialog.Builder alertDialog = new AlertDialog.Builder(context);
alertDialog.setTitle("Warning");
alertDialog.setMessage("您的账号在别处登录,您已被迫下线,请重新登录!");
alertDialog.setCancelable(false);
//设置按钮确定事件,按确定后销毁所有活动
alertDialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO 自动生成的方法存根
ActivityCollector.finishAll();
//重新启动登陆界面
Intent intent = new Intent(context,LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//设置一个重新进入LoginActiviy中的标志
context.startActivity(intent);
}
});
AlertDialog alertDialog1=alertDialog.create();
//设置alertDialog类型
alertDialog1.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog1.show();
}
}
8、在注册文件中注册
首先声明系统权限,不然会出现崩溃,然后对LoginActivity和MainActivity进行注册,同样还要注册广播接收器ForceOfflineReceiver,添加action接收发出的值。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.qzxx"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="20"
android:targetSdkVersion="21" />
<!-- 声明系统权限 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<!-- 注册LoginActivity -->
<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>
<!-- 注册MainActivity -->
<activity android:name=".MainActivity" />
<!-- 注册广播接收器 -->
<receiver android:name=".ForceOfflineReceiver">
<intent-filter>
<action android:name="com.example.broadcasttext.FORCE_OFFLINE"/>
</intent-filter>
</receiver>
</application>
</manifest>
四、结果
在AVD上运行结果
1、首先是登陆界面
2、输入用户名和密码,如果密码输入123,会出现下面显示
3、如果输入正确,则会成功登陆
4、进入主界面点击按钮,相当于其他用户登录此账号,以致被迫下线
5、点击ok,重新回到登录界面