Activity的生命周期

从活动一跳转到活动二:

image.png

在跳转中执行完onPause之后马上就执行活动二的onCreate方法,等活动二的onResume执行完后才执行活动一的onStop方法,所以不能再onPause方法里执行耗时操作


image.png

当Activity异常终止的时候onSaveInstanceState和onRestoreInstanceState就会被调用,用来储存和恢复数据,其他情况下不会触发这个过程

  • 在活动异常终止不可见时马上调用储存数据的方法,当活动要运行到前台可见之前调用恢复数据的方法
  • 每个View都有这两个方法,了解他们的具体实现后就知道系统能够自动为每个view恢复哪些数据
  • 注:onSaveInstanceState一定是在onStop方法前调用,但和onPause方法没有既定的时序关系,有可能在这个方法之前调用,也有可能在这个方法之后调用
  • 什么情况为异常终止:
  1. 资源相关的系统配置发生改变导致Activity被杀死并重新创建
    例如从横屏状态突然带竖屏状态
  2. 资源内存不足导致优先级低的Activity被杀死

测试数据储存和恢复
测试代码如下:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //onCreate如果是正常启动参数为null
        if (savedInstanceState!=null){
            String test=savedInstanceState.getString("extra_text");
            Log.d(TAG, "onCreate恢复数据:"+test);
        }
        Button button=findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent(MainActivity.this,Main2Activity.class);
                startActivity(intent);
            }
        });
        Log.d(TAG, "onCreate: ");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart: ");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop: ");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG, "onSaveInstanceState: ");
        outState.putString("extra_text","text");
    }
    //onRestoreInstanceState一旦被调用其参数一定是有值的
    @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String test=savedInstanceState.getString("extra_text");
        Log.d(TAG, "onRestoreInstanceState恢复数据:"+test);
    }
}

结果:

image.png

可以用Bundle传输数据

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //用Bundle传输数据
                Intent intent=new Intent(MainActivity.this,Main2Activity.class);
                Bundle bundle=new Bundle();
                bundle.putString("name","Tom");
                bundle.putInt("age",20);
                intent.putExtras(bundle);
                //用intent传输数据
                intent.putExtra("nickname","lkl");
                startActivity(intent);
            }
        });

public class Main2Activity extends AppCompatActivity {
    private static final String TAG = "Main2Activity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        //获取数据
        Intent intent1=getIntent();
        //接收intent的传输
        String nickName=intent1.getStringExtra("nickname");
        Log.d(TAG, "onCreate别名[intent]):"+nickName);
        //接收Bundle的传输
        Bundle bundle=intent1.getExtras();
        if (bundle!=null){
            String name=bundle.getString("name");
            int age=bundle.getInt("age");
            Log.d(TAG, "onCreate名字[bundle]: "+name);
            Log.d(TAG, "onCreate年龄[bundle]:"+age);
        }
    }
}

image.png

控制屏幕翻转后活动不重新创建
1.在AndroidManifest.xml文件里配置

android:configChanges="orientation|screenSize"

如下:


            
                
                
            
        

2.在活动里从写onConfigurationChanged方法

 @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d(TAG, "onConfigurationChanged: 最新的位置"+newConfig.orientation);
    }

这样就不会再调用onSaveInstanceState和onRestoreInstanceState方法了

image.png

从图中可以看出这个方法被调用了两次!Why?
1.当推出键盘的时候,会触发硬件的改变,使手机竖屏变成了横屏
2.当推进键盘的时候,合上手机的一刻,触发的是同一样的固件,由于,固件是无法辨别那个是推进,和推出,发出的是一样的信号,然后系统就会认为这是一个横屏改变,等到合上手机的时候系统再接受到一个信号,然后切换成竖屏.这样系统,认为横屏切换竖屏改变了两次,这样就导致调用了两次onConfigurationChanged();


再加一个Button控件测试不同的启动模式
在对应的活动中添加:

android:launchMode="standard"

在活动中的代码:

Button button2=findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent2=new Intent(MainActivity.this,MainActivity.class);
                startActivity(intent2);
            }
        });
  • 每点击一次按钮就会重新创建一个相同的活动


    image.png

修改

android:launchMode="singleTop"

点击了两次,back了一次的执行情况

image.png
补充说明任务栈

重要参数:TaskAffinity(任务相关性)

  • 每个 Activity 运行时都有一个其归属的 task栈,我们可以用 activity.getTaskId() 的方法得到当前 activity 的taskId。如果两个 activity 的 taskId 不同,则他们肯定不会属于同一个 task。
  • Android 手机的任务列表就是根据不同 task 弹出的,我们可以根据任务管理器有几个 item 图标,来知道我们开启了几个 task。
  • 指定活动2的栈测试使用方法:

        
  • 如上图所示,taskaffinity 可以单独对一个 activity 使用,代表该 activity 所想归属的 task;
    也能对application 使用,代表该 application 内声明的所有 activity 都归属于这个task。这个属性值必须和包名不同,否则就相当于没有指定
  • 如果 activity 组件没有声明 taskAffinity 的话,该 activity 的 taskAffinity 属性也是有默认值的。如果 application 指定了 taskAffinity 值,默认值就是 application 指定的 taskAffinity 值;如果 application 未指定的话,默认值就是 manifest 中声明的包名(package 对应的字符串)
  • 是不是我指定了一个 Activity 的 taskAffinity 值(跟包名不同),运行该 Activity 时,是否就会新开这个 task栈呢?答案是否定的,一个 Activity 运行时所归属的task,默认是启动它 的那个Activity 所在的 task

在活动1和2分别打印栈的id

 Log.d(TAG, "活动1的栈: "+this.getTaskId());
 Log.d(TAG, "活动2的栈: "+this.getTaskId());

结果:

image.png

结论:

  • taskAffinity 单独使用并不会生效。
    要想其生效,需要配合其他属性使用,或者配合 Intent.FLAG_ACTIVITY_NEW_TASK,或者配合allowTaskReparenting 。使用时用其中的一个就行
    测试:
    在跳转的intent中加入:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

总体主要代码:

Intent intent=new Intent(MainActivity.this,Main2Activity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

结果:


image.png

完整测试及log日志:
我们假定都是 Activity A 跳转到 Activity B 中,A没有指定 taskAffinity 属性,B 的launchMode 为standard。

case1: A、B 属同一App, intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 未指定 taskAffinity 属性
D/MyApplication: onActivityResumed+MainActivity####taskid = 61
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 61

  • 可以验证:一个Activity 归属的task 是由 启动它的 Activity 所决定的。

case2: A、B 属同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 指定 taskAffinity 属性,但与包名相同
D/MyApplication: onActivityResumed+MainActivity####taskid = 62
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 62

  • 可以验证,一个 Activity 的默认 task 值就是 manifest 定义的包名。

case3: A、B 属同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 指定 taskAffinity 属性,但与包名不同
D/MyApplication: onActivityResumed+MainActivity####taskid = 63
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 63

  • 可以验证:不指定 FLAG_ACTIVITY_NEW_TASK的话, 即使 taskAffinity 不同,一个Activity 归属的task 仍然是由 启动它的 Activity 所决定的。

case4: A、B 属同一App,intent 指定 FLAG_ACTIVITY_NEW_TASK,B 未指定 taskAffinity 属性
D/MyApplication: onActivityResumed+MainActivity####taskid = 64
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 64

  • 可以验证:即使 使用了 FLAG_ACTIVITY_NEW_TASK,但由于两者的 taskAffinity 相同,所以仍然不会开启一个新的task。

case5: A、B 属同一App,intent 指定 FLAG_ACTIVITY_NEW_TASK,B 指定 taskAffinity 属性,且和包名不同
D/MyApplication: onActivityResumed+MainActivity####taskid = 65
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 66

  • 可以验证:开启一个新task 的条件是 FLAG_ACTIVITY_NEW_TASK 和 taskAffinity 不同 缺一不可。

case6: A、B 属同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 未指定 taskAffinity 属性,B启动模式为 singletask or singletop
D/MyApplication: onActivityResumed+MainActivity####taskid = 67
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 67

  • 可以得出:未指定 FLAG_ACTIVITY_NEW_TASK 和 新的 taskAffinity 时,这两种启动模式对task 没有影响

case7: A、B 属同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK,B 未指定 taskAffinity 属性,B启动模式为 singleinstance
D/MyApplication: onActivityResumed+MainActivity####taskid = 70
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 71

  • 可以得出:
    singleinstance 启动模式本身就是会开启一个新的task 装载 这个Activity,且task 中只有这一个 Activity。
  • 经判断得知,AMS 是先对 launchMode 做判断 再处理 FLAG_ACTIVITY_NEW_TASK 的,如果是 singleinstance ,则会直接开启一个task。
onNewIntent方法与启动模式

前提:ActivityA已经启动过,处于当前应用的Activity任务栈中;

当ActivityA的LaunchMode为Standard时:

由于每次启动ActivityA都是启动新的实例,和原来启动的没关系,所以不会调用原来ActivityA的onNewIntent方法

当ActivityA的LaunchMode为SingleTop时:

如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()方法 ,生命周期顺序为:

onCreate--->onStart--->onResume---onPause>onNewIntent--->onResume

当ActivityA的LaunchMode为SingleInstance,SingleTask:

如果ActivityA已经在堆栈中,那么此时会调用onNewIntent()方法,生命周期调用顺序为:

onCreate--->onStart--->onResume---按下Home键>onPause--->onstop--->onNewIntent--->onRestart--->onstart--->onResume

测试代码:

   button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent2=new Intent(MainActivity.this,MainActivity.class);
                intent2.putExtra("music","hey kong");
                startActivity(intent2);
            }
        });
  @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        String musicIs=getIntent().getStringExtra("music");
        Log.d(TAG, "onNewIntent:1 "+musicIs);
    }

注意:
有时候,我们在多次启动同一个栈唯一模式下的activity时,在onNewIntent()里面的getIntent()得到的intent感觉都是第一次的那个数据。对,这里就是这个陷阱。因为它就是会返回第一个intent的数据。就是这么坑。
原因就是我们没有在onNewIntent()里面设置setIntent(),将最新的intent设置给这个activity实例。
加了setIntent():


image.png

没加:
修改:

 intent2.putExtra("music","hey kong2");

结果:

image.png

还是上一个intent结果,没有改变
加了setIntent():

image.png

你可能感兴趣的:(Activity的生命周期)