Android中的活动使可以层叠的。我们每启动一个新的活动,就会覆盖在原活动之上,然后点击back键会销毁最上面的活动,下面的一个活动就会重新显示出来。

其实Android是使用任务(Task)来管理活动的,一个任务就是一组存放在栈里的活动的集合,这个栈也被称作返回栈(Back Stack)。栈是一种后进先出的数据结构,在默认情况下,每当我们启动了一个新的活动,它会在返回栈中入栈,并处于栈顶的位置。而每当我们按下Back键或调用finish()方法去销毁一个活动时,处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。系统总是会显示处于栈顶的活动给用户。

android 移除栈 android activity栈_android

活动状态

每个活动在其生命周期中最多可能会有4种状态。
1,运行状态
  当一个活动位于返回栈的栈顶时,这时活动就处于运行状态,系统最不愿意回收的就是处于运行状态的活动,因为这会带来非常差的用户体验。
2,暂停状态
  当一个活动不再处于栈顶位置,但仍然可见,这是活动就进入了暂停状态,处于暂停状态的活动仍然是完全存活着的,系统也不愿意去回收这种活动,只有内存在极低的情况下,系统才会去考虑回收这种活动
3,停止状态
  当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入了停止状态。系统仍然会为这种活动保存相应的状态和成员变量,但是这种并不是完全可靠的,当其他地方需要内存时,处于停止状态的活动有可能会被系统回收。
4,销毁状态
  当一个活动从返回栈中移除后就变成销毁状态,系统会倾向于回收处于这种状态的活动,从而保证手机内存的充足

活动的生命周期

Activity类中定义了7个回调方法,覆盖了活动生命周期的每一个环节。
  onCreate()。这是Activity的创建阶段。这个阶段的任务就是创建界面视图。这个方法有一个Bundle参数,这个参数是用来传送上一次程序运行时保存的数据状态。比如说你看书的时候看到第几页了。
  onStart()。这个方法在活动由不可见变为可见的时候调用。
  onResume()。这个方法在系统准备好和用户进行交互的时候调用。此时的活动一定位于返回栈的栈顶,并且处于运行状态。
  onPause()。这个方法在系统准备去启动或者恢复另一个活动的时候调用。我们通常会在这个方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据,但这个方法的执行速度一定要快,不然会影响到新的栈顶活动的调用。
  任何中断恢复阶段的操作都会导致暂停状态函数的调用。比如按了主键,或者有电话进来了。在这个阶段里可以暂停一些正在进行的操作,比如说你正在播放电影,可以暂停一下。在这个阶段的时候用户的界面还是可见的,只是在后端,就像蒙上了一层阴影。从这个阶段一个有两个去向,一个是终止,一个是恢复。恢复就是再把用户界面推到前端。下来是终止状态。
  onStop()。这个方法在活动完全不可见的时候调用。它和onPause()方法的主要区别在于,如果启动一个新的活动是一个对话框式的活动,那么onPause()方法会得到执行,而onStop()方法并不会执行。
  如果用户的见面转为不可见。这个停止函数就会被调用。从这个阶段有两个去向。一个是开始,一个是消除。开始阶段的调用是由用户再启动这个程序而触发的。消除阶段的调用是系统关掉这个程序而触发的。
  onDestroy()。这个方法在活动被销毁之前调用,之后活动的状态将变为销毁状态。
  onRestart()。这个方法在活动由停止状态变为运行状态之前调用,也就是活动被重新启动了。(再启动有个专门的状态。是从停止状态进入开始阶段的过渡状态。)

以上7个方法除了onRestart()方法,其他都是两两相对的,从而又可以将活动分为3种生命周期。

  完整生命周期。活动在onCreate()方法和onDestroy()方法之间所经历的,就是完整生存期。一般情况下,一个活动会在onCreate()方法种完成各种初始化操作,而在onDestroy()方法种完成释放内存的操作

  可见生存期。活动在onStart()方法和onStop()方法之间所经历的,就是可见生存期。在可见生存期内,活动对于用户总是可见的,即便有可能无法和用户进行交互。我们可以通过这两个方法,合理地管理那些对用户可见的资源。比如在onStart()方法种对资源进行加载,而在onStop()方法中对资源进行释放,从而保证处于停止状态的活动不会占用过多内存。

  前台生存期。活动在onResume()方法和onPause()方法之间所经历的就是前台生存期。在前台生存期内,活动总是处于运行状态的,此时的活动是可以和用户进行交互的,我们平时看到和接触最多的也就是这个状态下的活动。

android 移除栈 android activity栈_android studio_02

实例

以横竖屏切换为例子。

在layout目录下创建两个activity_1.xml文件,其中一个为land

android 移除栈 android activity栈_android_03


activity_1.xml代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="横屏:btn1++"
        android:textSize="25dp" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="横屏:btn2 --"
        android:textSize="25dp" />

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="45dp" />
</LinearLayout>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="竖屏:btn1++"
        android:textSize="25dp" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="竖屏:btn2 --"
        android:textSize="25dp" />

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="45dp" />
</LinearLayout>

MainActivity.java代码

public class MainActivity extends AppCompatActivity {

    int mIndex=0;
    private TextView tv1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_1);
        Log.v("MainActivity", "调用了onCreate()方法");
        mIndex = 0;
        Button btn1 = (Button) findViewById(R.id.btn1);
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mIndex++;
                tv1.setText("" + mIndex);
            }
        });
        Button btn2 = (Button) findViewById(R.id.btn2);
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mIndex--;
                tv1.setText("" + mIndex);
            }
        });
        tv1 = (TextView) findViewById(R.id.tv1);
        tv1.setText("" + mIndex);


    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.v("MainActivity", "调用了onStart()方法");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.v("MainActivity", "调用了onResume()方法");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.v("MainActivity", "调用了onPause()方法");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.v("MainActivity", "调用了onStop()方法");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.v("MainActivity", "调用了onDestroy()方法");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.v("MainActivity", "调用了onRestart()方法");
    }

}

效果

android 移除栈 android activity栈_android studio_04

android 移除栈 android activity栈_xml_05

android 移除栈 android activity栈_android_06

android 移除栈 android activity栈_xml_07

存在问题

横竖屏切换时计数器都是显示为0,如下图

android 移除栈 android activity栈_ide_08

android 移除栈 android activity栈_android 移除栈_09

原因:当运行时配置变更发生时,考虑到可能会 有更合适的资源来匹配新的设备配置,于是,Android就会销毁当前 activity,为新配置寻找更佳的资源,然后在创建的新activity实例中使用 这些更合适的资源。

解决方法

1.只用横屏或者竖屏(不推荐)

在AndroidManidest.xml中加入以下

android:configChanges="orientation|screenSize"
android:screenOrientation="landscape"

注意加入位置

android 移除栈 android activity栈_ide_10


效果:转屏可以看到计数不变,但是无法支持两种屏转换。

android 移除栈 android activity栈_android studio_11

android 移除栈 android activity栈_ide_12

2.覆盖onSaveInstanceState方法(常用方法)

Activity种提供了一个**onSaveInstanceState()**回调方法,这个方法可以保证在活动被回收之前一定会被调用,因此可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。

onSaveInstanceState()方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用putString()方法来保存字符串,使用putInt()方法保存整型数据,以此类推。每个保存方法需要传入两个参数,第一个参数是键,用于后面Bundle中取值,第二个参数是真正要保存的内容。

(1)删除AndroidManifest.xml 中的

android:configChanges=“orientation|screenSize”
 android:screenOrientation=“landscape”

(2)销毁activity对象之前保存状态,除非用户按【后退键】,此时代表用户不需要activity了, 随后自然该activity会从内存中抹掉,自然也就没有必要保存计数器值了。

(3) 新建activity对象后,取出状态;在onCreate方法中实现取数据。

(保存数据)在MainActivity.java中添加onSaveInstanceState()方法

@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("index", mIndex);
    }

(取出数据)onCreate(Bundle savedInstanceState)方法中把mIndex的初始化去掉,在 tv1.setText("" + mIndex); 之前添加

if (null != savedInstanceState) {
            mIndex = savedInstanceState.getInt("index", 0);
            Log.v("MainActivity", "onCreate()中:mIndex=" + mIndex);
        }

android 移除栈 android activity栈_xml_13

android 移除栈 android activity栈_android 移除栈_14

android 移除栈 android activity栈_android studio_15

android 移除栈 android activity栈_android_16

转屏后可以看到mIndex=9不变

效果

android 移除栈 android activity栈_android studio_17

android 移除栈 android activity栈_android_18

3.设置静态成员变量(不推荐)

如图

android 移除栈 android activity栈_android studio_19


也可以实现其效果

android 移除栈 android activity栈_android_20

android 移除栈 android activity栈_ide_21

整体MainActivity.java代码

public class MainActivity extends AppCompatActivity {

    int mIndex=0;   
    private TextView tv1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_1);
        Log.v("MainActivity", "调用了onCreate()方法");
//        mIndex = 0;
        Button btn1 = (Button) findViewById(R.id.btn1);
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mIndex++;
                tv1.setText("" + mIndex);
            }
        });
        Button btn2 = (Button) findViewById(R.id.btn2);
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mIndex--;
                tv1.setText("" + mIndex);
            }
        });
        tv1 = (TextView) findViewById(R.id.tv1);
        if (null != savedInstanceState) {
            mIndex = savedInstanceState.getInt("index", 0);
            Log.v("MainActivity", "onCreate()中:mIndex=" + mIndex);
        }
        tv1.setText("" + mIndex);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.v("MainActivity", "调用了onStart()方法");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.v("MainActivity", "调用了onResume()方法");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.v("MainActivity", "调用了onPause()方法");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.v("MainActivity", "调用了onStop()方法");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.v("MainActivity", "调用了onDestroy()方法");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.v("MainActivity", "调用了onRestart()方法");
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.v("MainActivity", "调用了onConfigurationChanged()方法");
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("index", mIndex);
    }
}