最近看到一道题目,如何安全退出已调用多个Activity的Application? 想了想难道不是用killProcess()和System.exit()的方法就能退出了吗?

测试了一下,如果你的程序打开多个Activity后,想通过以上两种方法退出应用都不行,它只能结束当前的Activity返回前一个Activity,除非没有前一个Activity,则能退出。

退出调用多个Activity的Application的应用场景:

1.如qq如果在另一端登录了,当前端就需要提示用户异地登陆,并且销毁之前用户打开的相关activity,最后会跳转回登录界面[这里的跳转代替了退出]。

2.通过点击两次返回键退出应用[不过现在好像很少这种操作了?]

以下代码针对这两个场景展开,在网上百度到的退出方法有以下几种:


1、抛异常强制退出:

该方法通过抛异常,使程序ForceClose。验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。

2、记录打开的Activity:

每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。

3、发送特定广播:

在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。

4、递归退出:

在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。

这里只介绍第二种实现方式。需求:

1.有四个ActivityA,B,C,D,A打开B,B打开C,C打开D,在D中点击返回键两次退出程序,清空所有Activity。

2.B,C,点击返回键,能够退回上一级,A返回不退出。

步骤:

1.新建一个Application,新建一个list来管理activity的销毁,定义增加指定activity,销毁指定activity。last是在销毁list时纪录list中的最后一个activity的地址。

TestApplication.java

package tplusr.exittext;

import android.app.Application;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by tangjiarao on 16/9/6.
 */
public class TestApplication extends Application {
    
    //管理所有activity销毁
    List<BaseActivity> mActivityList = new ArrayList<>();

    //记录最后销毁的activity
    BaseActivity last;
    
    /**
     * 提供一个添加activity的方法
     * 每新建一个activity就增加到集合里面
     * @param activity
     */
    public void addActivity(BaseActivity activity) {
        if (!mActivityList.contains(activity)) {
            mActivityList.add(activity);
            checkActivityNum();
        }

    }

    //提供一个移除activity的方法
    public void removeActivity(BaseActivity activity) {
        if (mActivityList.contains(activity)) {
            mActivityList.remove(activity);
        }
    }


    /**
     * 提供一个清空集合的方法
     * 就是使用循环对集合里的activity逐个销魂
     */
    public void clearAllActivity() {
        last =mActivityList.get(mActivityList.size()-1);
        for (int i = 0; i< mActivityList.size(); i++) {
            BaseActivity activity = mActivityList.get(i);
            activity.finish();
            Log.d("BASE", "销毁了:" + activity);
        }
        mActivityList.clear();
    }

    //查看Activity数量及名字
    public void checkActivityNum(){

        Log.d("BASE", "------------List储存的actvity------------");
        for (BaseActivity b: mActivityList){
            Log.d("BASE", b.toString());
        }
        Log.d("BASE", "页面数:"+mActivityList.size());
        Log.d("BASE", "------------List储存的actvity------------");
    }
}

2.新建一个baseActivity作为基类,让子类去继承。这里设置一个回调是因为,当清空Activity时,没来得及onDestory所有的activity就调用了System.exit(0)方法退出程序,所以回调的作业是判断当前即将销毁的activity是不是list里面的最后一个,如果是的话才通知页面4退出程序。

BaseActivity.Java

package tplusr.exittext;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;

import java.util.List;

public abstract class BaseActivity extends AppCompatActivity {

    //当执行完最后销毁的一个activity回调
    public interface Listener{
        public void end();
    }
    private Listener listener;
    public TestApplication testApplication;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initContentView(savedInstanceState);
        testApplication =(TestApplication)this.getApplication();
        //每新建一个activity旧增加一个activity
        testApplication.addActivity(this);
        //输出栈里面存有的activity数量和顶层activity
        Log.d("BASE", getTopActivity(this));

    }
    /**
     * 初始化UI,setContentView等
     * 主要是该Activity不加载xml,让子类加载
     * @param savedInstanceState
     */
    protected abstract void initContentView(Bundle savedInstanceState);

    /**
     * 获取栈顶activity
     * @param context
     * @return
     */
    protected String getTopActivity(Activity context)
    {
        ActivityManager manager = (ActivityManager)context.getSystemService(ACTIVITY_SERVICE) ;
        List<ActivityManager.RunningTaskInfo> runningTaskInfos = manager.getRunningTasks(1) ;

        if(runningTaskInfos != null){
            return "栈数量:"+runningTaskInfos.size()+
                    "  栈顶Activity:"+runningTaskInfos.get(0).topActivity+
                    "  总Activity数:"+runningTaskInfos.get(0).numActivities;
        }
        else{
            return null ;
        }

    }

    protected void onDestroy(){

        testApplication.removeActivity(this);
        Log.d("BASE", "onDestroy" + getTopActivity(this));
        super.onDestroy();
        //如果当前的activity等于最后销毁的activity,就回调
        if (testApplication.last==this){
            listener.end();
        }
    }

    public void setListener(Listener listener){
        this.listener=listener;
    }

}


3.程序主页面,重写了返回键,点击返回键不会退出程序,点击按钮跳到页面2


MainActivity.Java

package tplusr.exittext;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import org.w3c.dom.Text;

public class MainActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        TextView t =(TextView)findViewById(R.id.text);
        t.setText("这是页面1");
        Button b =(Button)findViewById(R.id.button);
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setClass(MainActivity.this, test2Activity.class);
                startActivity(intent);
            }
        });
    }

    /**
     * 初始化布局
     * @param savedInstanceState
     */
    protected void initContentView(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);
    }

    /**
     * 重写toString方法
     * @return
     */
    public String toString(){
        return "[页面1:test1Activity Hashcode:"+this.hashCode()+"]";
    }
    /**
     * 重写onKeyDown方法,按返回键时不会销毁Activity
     * @param keyCode
     * @param event
     * @return
     */
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {

            //跳到手机主页面,activity会onStop
            Intent intent = new Intent(Intent.ACTION_MAIN);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addCategory(Intent.CATEGORY_HOME);

            startActivity(intent);
            return true;
        } else
            return super.onKeyDown(keyCode, event);
    }
}


4.页面2和页面3是一样的,不一样的是text内容和跳转的目标activity


test3Activity.Java && test2Activity.Java

package tplusr.exittext;

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

public class test2Activity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView t =(TextView)findViewById(R.id.text);
        t.setText("这是页面2");
        Button b =(Button)findViewById(R.id.button);
        
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setClass(test2Activity.this, test3Activity.class);
                startActivity(intent);
            }
        });
    }

    @Override
    protected void initContentView(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);
    }

    public String toString(){
        return "[页面2:test2Activity Hashcode:"+this.hashCode()+"]";
    }

}

5.页面4实现三个功能:(1)点击按钮退出程序 (2)点击按钮回到主页面 (3)点击两下返回键退出程序

package tplusr.exittext;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class test4Activity extends BaseActivity {

    public long mStartTime=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView t =(TextView)findViewById(R.id.text);
        t.setText("这是页面4");
        Button b1 =(Button)findViewById(R.id.button1);
        Button b2 =(Button)findViewById(R.id.button2);
        b1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //清空页面
                testApplication.clearAllActivity();

                //当最后一个activity销毁完毕后才退出程序
                setListener(new Listener() {
                    @Override
                    public void end() {
                        //输出当前页面栈内容
                        Log.d("BASE", getTopActivity(test4Activity.this));
                        System.exit(0);
                    }
                });

            }
        });

        b2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //清空页面
                testApplication.clearAllActivity();
                //输出当前页面栈内容
                Log.d("BASE", getTopActivity(test4Activity.this));
                //跳回程序主页面
                Intent intent = new Intent();
                intent.setClass(test4Activity.this,MainActivity.class);
                startActivity(intent);
            }
        });
    }

    @Override
    protected void initContentView(Bundle savedInstanceState) {
        setContentView(R.layout.activity_test4);
    }

    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {

            //当点击的时候计算2次点击的时间差,如果在2秒之内点击2次,就退出
            long currentTimeMillis = System.currentTimeMillis();
            if ((currentTimeMillis - mStartTime) < 2000) {
                //清空页面
                testApplication.clearAllActivity();
                //输出当前页面栈内容
                Log.d("BASE", getTopActivity(test4Activity.this));
                //退出进程方法2
                android.os.Process.killProcess(android.os.Process.myPid());
                Intent intent = new Intent(getApplicationContext(),MainActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//在非activity中,要唤起一个activity,就要重现创建一个任务栈
                startActivity(intent);
            } else {
                Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
                mStartTime = System.currentTimeMillis();    //记录点击后的时间
            }
            return true;
        } else
            return super.onKeyDown(keyCode, event);
    }

    public String toString(){
        return "[页面4:test4Activity Hashcode:"+this.hashCode()+"]";
    }
}


AndroidManifest.Xml


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

    <application
        android:name=".TestApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".test2Activity" />
        <activity android:name=".test3Activity" />
        <activity android:name=".test4Activity" />
        <activity android:name=".BaseActivity"></activity>
    </application>

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


功能(1)点击按钮退出程序结果:

Android 多个model打包成一个aar_ide

在Android Device Monitor上,进程结束:

运行时:                                                                    结束进程后:

Android 多个model打包成一个aar_多个activity_02

       

Android 多个model打包成一个aar_多个activity_03

功能(2)点击按钮回到主页面:

Android 多个model打包成一个aar_ide_04

Android 多个model打包成一个aar_多个页面_05


Activity销毁完后,重新new了mainActivity,程序不会退出。

功能(3)点击两下返回键退出程序:结果是跟功能(1)一样的。


现在有两个问题:

问题(1)当在B页面和C页面中点击返回按钮的时候,List中的Activity数量会不断增加。

以下结果是不断跳转到第三个页面,又返回第二个页面的结果:


原因:点击返回按钮的时候会调用destroy方法并且销毁Activity,但是你没有在销毁当前Activity的时候从list里面移除这个Activity。

解决办法:

(1)在destroy方法里面调用Application的removeActivity方法。

(2)不用管他,因为调用clearAllActivity方法时会清空所有实例,尽管实例不存在。


注意:如果你的需求是退出程序,则clearAllActivity方法应该跟退出程序代码一同使用

(1)只有System.exit(0);方法和android.os.Process.killProcess(android.os.Process.myPid());方法,没有clearAllActivity方法,就只能finish掉当前页面,无法退出程序。

(2)只有clearAllActivity方法,没有退出程序代码,程序会依然存在。


最后一个问题!!现在的手机都有任务管理器,即使程序退出了在则手机的任务管理器里面依然能看到该程序,但是程序已经退出了,怎么解决!!!