最近看到一道题目,如何安全退出已调用多个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 Device Monitor上,进程结束:
运行时: 结束进程后:
功能(2)点击按钮回到主页面:
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方法,没有退出程序代码,程序会依然存在。
最后一个问题!!现在的手机都有任务管理器,即使程序退出了在则手机的任务管理器里面依然能看到该程序,但是程序已经退出了,怎么解决!!!