在一些应用软件上我们经常会遇到诸如“您的XX账号在别处登录,请您重新登陆!”的问题,那么这是如何实现的呢?下面正是一个利用广播机制来实现强制下线的例子。

一、任务

1、首先创建一个ActivityCollector类用来管理活动,在这个类中添加相应的方法
2、然后创建一个BaseActivity类作为作为父类,在子类中覆盖该类的方法
3、设计一个登陆界面,首先创建登录界面的布局文件login.xml
4、创建LoginActivity类实现登陆界面
5、创建登陆后的主界面,同样也是创建布局文件main.xml
6、创建MainActivity类实现主界面
7、创建一个广播接收器类ForceOfflineReceiver,用于接收强制下线的广播
8、在注册文件中注册

二、原理图

java如何强制结束递归 java如何做到强制下线_android

三、主要实现

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

java如何强制结束递归 java如何做到强制下线_xml_02


使用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

java如何强制结束递归 java如何做到强制下线_java如何强制结束递归_03


在这里主界面只是一个简单的文本显示,当然还可以添加更多的功能。所以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()方法创建一个窗口实例,并设置其类型

java如何强制结束递归 java如何做到强制下线_ide_04


在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、首先是登陆界面

java如何强制结束递归 java如何做到强制下线_android_05

2、输入用户名和密码,如果密码输入123,会出现下面显示

java如何强制结束递归 java如何做到强制下线_xml_06

3、如果输入正确,则会成功登陆

java如何强制结束递归 java如何做到强制下线_xml_07

4、进入主界面点击按钮,相当于其他用户登录此账号,以致被迫下线

java如何强制结束递归 java如何做到强制下线_java如何强制结束递归_08

5、点击ok,重新回到登录界面