Android开发艺术探索知识回顾——第1章 Activity的生命周期和启动模式:1、生命周期

声明:本教程为读书笔记,不收取任何费用,欢迎转载,尊重作者劳动成果,不得用于商业用途,侵权必究!!!

好记性不如烂笔头。16年那会就阅读过这本书,在印象笔记上面做了一些记录,最近重新过一篇放在博客里面。

本书的作者是任玉刚,他的博客地址:https://blog.csdn.net/singwhatiwanna

目录

第1章 Activity的生命周期和启动模式

1.1 Activity的生命周期全面分析

1.1.1 典型情况下的生命周期分析

在正常情况下,Activity会经历如下生命周期。

生命周期附加说明

这里提出2个问题,不知道大家是否清楚。

第一个问题:从实际使用过程来说

第二个问题:可以从 Android 源码里得到解释。

第二个问题:写个例子验证一下

代码:MainActivity.java

代码:SecondActivity.java

1.1.2 异常情况下的生命周期分析

情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建

源码:TextView# onSavelnstanceState

旋转屏幕异常终止Activity

旋转屏幕异常终止——代码示例

onRestorelnstanceState 和 onCreate 的区别

情况2:资源内存不足导致低优先级的Activity被杀死

当系统配置发生改变后,Activity不重新创建的情况

表1-1 configChanges的项目和含义                                    

指定configChanges属性——代码示例


第1章 Activity的生命周期和启动模式

作为本书的第1章,本章主要介绍Activity相关的一些内容。Activity作为四大组件之首,是使用最为频繁的一种组件,中文直接翻译为“活动",但是笔者认为这种翻译有些生硬,如果翻译成界面就会更好理解。正常情况下,除了 WindowDialog 和 Toast,我们能见到的界面的确只有Activity。Activity是如此重要,以至于本书开篇就不得不讲到它。

当然,由于本书的定位为进阶书,所以不会介绍如何启动Activity这类入门知识,本章的侧重点是Activity在使用过程中的一些不容易搞清楚的概念,主要包括生命周期和启动模式以及IntentFilter的匹配规则分析。其中Activity在异常情况下的生命周期是十分微妙的,至于Activity的启动模式和形形色色的 Flags 更是让初学者摸不到头脑,就连隐式启动 Activity 中也有着复杂的 Intent 匹配过程,不过不用担心,本章接下来将一一解开这些疑难问题的神秘面纱。

 

1.1 Activity的生命周期全面分析

本节将Activity的生命周期分为两部分内容,一部分是典型情况下的生命周期,另一部分是异常情况下的生命周期。

所谓典型情况下的生命周期,是指在有用户参与的情况下,Activity 所经过的生命周期的改变;

而异常情况下的生命周期,是指 Activity 被系统回收 或者 由于当前设备的 Configuration 发生改变从而导致 Activity 被销毁重建,异常情况下的生命周期的关注点和典型情况下略有不同。

 

1.1.1 典型情况下的生命周期分析

在正常情况下,Activity会经历如下生命周期。

  1. onCreate表示 Activity 正在被创建,这是生命周期的第一个方法。在这个方法中, 我们可以做一些初始化工作,比如调用setContentView 去加载界面布局资源、初始化Activity 所需数据等。

  2. onRestart表示 Activity 正在重新启动。一般情况下,当当前 Activity 从不可见重新变为可见状态时,onRestart 就会被调用。这种情形一般是用户行为所导致的,比如用户按 Home 键切换到桌面或者用户打开了一个新的 Activity,这时当前的 Activity 就会暂停,也就是 onPause 和 onStop 被执行了,接着用户又回到了这个 Activity,就会出现这种情况。
  3. onStart表示 Activity 正在被启动,即将开始,这时 Activity 已经可见了,但是还没有出现在前台,还无法和用户交互。这个时候其实可以理解为 Activity 已经显示出来了,但是我们还看不到。
  4. onResume表示 Activity已经可见了,并且出现在前台并开始活动。要注意这个和 onStart 的对比,onStart 和 onResume 都表示Activity已经可见,但是 onStart 的时候 Activity 还在后台,onResume 的时候 Activity 才显示到前台。
  5. onPause表示 Activity 正在停止,正常情况下,紧接着 onStop 就会被调用。在特殊情况下,如果这个时候快速地再回到当前 Activity,那么 onResume 会被调用。笔者的理解是,这种情况属于极端情况,用户操作很难重现这一场景。此时可以做一些存储数据、 停止动画等工作,但是注意不能太耗时,因为这会影响到新 Activity 的显示,onPause 必须先执行完,新 Activity的 onResume 才会执行。
  6. onStop表示 Activity 即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。
  7. onDestroy表示 Activity 即将被销毁,这是 Activity 生命周期中的最后一个回调, 在这里,我们可以做一些回收工作和最终的资源释放。

正常情况下,Activity的常用生命周期就只有上面 个,图1-1更详细地描述了 Activity 各种生命周期的切换过程。

 

生命周期附加说明

针对图1-1,这里再附加一下具体说明,分如下几种情况。

(1)针对一个特定的 Activity,第一次启动,回调如下:onCreate -> onStart -> onResume

(2)当用户打开新的Activity或者切换到桌面的时候,回调如下:onPause -> onStop 。这里有一种特殊情况,如果新Activity釆用了透明主题,那么当前Activity不会回调onStop。

(3)当用户再次回到原 Activity 时,回调如下:onRestart-> onStart -> onResume。

(4)当用户按back键回退时,回调如下:onPause -> onStop -> onDestroy

(5)当Activity被系统回收后再次打开,生命周期方法回调过程和(1)一样,注意只是生命周期方法一样,不代表所有过程都一样,这个问题在下一节会详细说明。

(6)从整个生命周期来说,onCreate 和 onDestroy 是配对的,分别标识着 Activity 的创建和销毁,并且只可能有一次调用。从 Activity 是否可见来说,onStart 和 onStop 是配对的,随着用户的操作或者设备屏幕的点亮和熄灭,这两个方法可能被调用多次;从 Activity 是否在前台来说 onResume 和 onPause 是配对的,随着用户操作或者设备屏幕的点亮和熄灭, 这两个方法可能被调用多次。

 

这里提出2个问题,不知道大家是否清楚。

问题1:onStart 和 onResumeonPause 和 onStop 从描述上来看差不多,对我们来说有什么实质的不同呢?

问题2:假设当前 Activity A,如果这时用户打开一个新 Activity B,那么 的 onResume 和 的 onPause 哪个先执行呢?

 

第一个问题:从实际使用过程来说

先说第一个问题,从实际使用过程来说,onStart 和 onResumeonPause 和 onStop 看起来的确差不多,甚至我们可以只保留其中一对,比如只保留 onStart 和 onStop 既然如此, 那为什么Android系统还要提供看起来重复的接口呢?根据上面的分析,我们知道,这两个配对的回调分别表示不同的意义,onStart 和 onStop 是从 Activity 是否可见这个角度来回调的,而 onResume 和 onPause 是从Activity是否位于前台这个角度来回调的,除了这种区别,在实际使用中没有其他明显区别。

 

第二个问题:可以从 Android 源码里得到解释

关于 Activity 的工作原理在本书后续章节会进行介绍,这里我们先大概了解即可。从 Activity 的启动过程来看,我们来看一下系统源码。Activity 的启动过程的源码相当复杂,涉及 Instrumentation、ActivityThread 和 ActivityManagerService(下面简称AMS)

这里不详细分析这一过程,简单理解,启动 Activity 的请求会由 Instrumentation 来处理,然后它通过 Binder 向 AMS 发请求,AMS内部维护着一个 ActivityStack 并负责栈内的 Activity 的状态同步,AMS 通过 ActivityThread 去同步 Activity 的状态从而完成生命周期方法的调用。在 ActivityStack 中的 resumeTopActivity-InnerLocked 方法中,有这么一段代码:

从上述代码可以看出,在新 Activity 启动之前,栈顶的 Activity 需要先 onPause 后,新 Activity 才能启动。

最终,在 ActivityStackSupervisor 中的 realStartActivityLocked 方法会调用如下代码。

我们知道,这个 app.thread 的类型是 IApplicationThread,而 IApplicationThread 的具体实现是 ActivityThread 中的 ApplicationThread 。所以,这段代码实际上调到了 ActivityThread 的中,即 ApplicationThread scheduleLaunchActivity 方法,而 scheduleLaunchActivity 方法最终会完成新 Activity 的 onCreateonStart、onResume 的调用过程。因此,可以得出结论, 是旧 Activity 先 onPause,然后新 Activity 再启动。

至于 ApplicationThread scheduleLaunchActivity 方法为什么会完成新 Activity onCreate、onStart、onResume 的调用过程,请看下面的代码。scheduleLaunchActivity 最终会调用如下方法,而如下方法的确会完成 onCreate、onStart、onResume 的调用过程。

从上面的分析可以看出,当新启动一个 Activity 的时候,旧 Activity 的 onPause 会先执行,然后才会启动新的 Activity。

 

第二个问题:写个例子验证一下

到底是不是这样呢?我们写个例子验证一下,如下是2个 Activity 的代码,在 MainActivity 中单击按钮可以跳转到 SecondActivity,同时为了分析我们的问题,在生命周期方法中打印出了日志,通过日志我们就能看出它们的调用顺序。

代码:MainActivity.java

package com.yyh.demo1.activity_onPause;

import com.ryg.chapter_1.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;


public class MainActivity extends Activity {

    public static final String TAG = "MainActivity@@@";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_demo1);

        findViewById(R.id.btnTo).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });

    }

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

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

 

代码:SecondActivity.java

package com.yyh.demo1.activity_onPause;

import com.ryg.chapter_1.R;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;


public class SecondActivity extends Activity {

    private static final String TAG = "SecondActivity@@@";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second_demo1);
        Log.i(TAG, "onCreate");
    }

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

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

我们来看一下log,是不是和我们上面分析的一样,如图1-2所示。

通过图1-2可以发现,旧 Activity 的 onPause 先调用,然后新 Activity 才启动,这也证实了我们上面的分析过程。也许有人会问,你只是分析了 Android5.0 的源码,你怎么知道所有版木的源码都是相同逻辑呢?

关于这个问题,我们的确不大可能把所有版本的源码都分析一遍,但是作为Android运行过程的基本机制,随着版本的更新并不会有大的调整, 因为Android系统也需要兼容性,不能说在不同版本上同一个运行机制有着截然不同的表现。关于这一点我们需要把握一个度,就是对于Android运行的基本机制在不同 Android 版本上具有延续性。

从另一个角度来说,Android官方文档对 onPause 的解释有这么一句:不能在 onPause 中做重量级的操作,因为必须 onPause 执行完成以后新 Activity 才能 Resume从这一点也能间接证明我们的结论。通过分析这个问题,我们知道 onPause 和 onStop 都不能执行耗时的操作,尤其是 onPause方法,这也意味着,我们应当尽量在 onStop 中做操作,从而使得新 Activity 尽快显示出来并切换到前台。

 

1.1.2 异常情况下的生命周期分析

上一节我们分析了典型情况下 Activity 的生命周期,本节我们接着分析 Activity 在异常情况下的生命周期。我们知道,Activity 除了受用户操作所导致的正常的生命周期方法调度, 还有一些异常情况,比如当资源相关的系统配置发生改变以及系统内存不足时,Activity就可能被杀死。下面我们具体分析这两种情况。

 

情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建

理解这个问题,我们首先要对系统的资源加载机制有一定了解,这里不详细分析系统的资源加载机制,只是简单说明一下。拿最简单的图片来说,当我们把一张图片放在 drawable 目录后,就可以通过 Resources 去获取这张图片。同时为了兼容不同的设备,我们可能还需要在其他一些目录放置不同的图片,比如drawable-mdpidrawable-hdpidrawable-land等。

这样,当应用程序启动时,系统就会根据当前设备的情况去加载合适的 Resources 资源,比如说横屏手机和竖屏手机会拿到两张不同的图片(设定了 landscape 或者 portrait 状态下的图片)。比如说当前 Activity 处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了改变,在默认情况下,Activity就会被销毁并且重新创建,当然我们也可以阻止系统重新创建我们的Activity。

在默认情况下,如果我们的Activity不做特殊处理,那么当系统配置发生改变后, Activity就会被销毁并重新创建,其生命周期如图 1-3 所示。

当系统配置发生改变后,Activity会被销毁,其 onPauseonStop、onDestroy 均会被调用,同时由于 Activity 是在异常情况下终止的,系统会调用 onSavelnstanceState 来保存当前 Activity 的状态。这个方法的调用时机是在 onStop 之前,它和 onPause 没有既定的时序关系,它既可能在 onPause 之前调用,也可能在 onPause 之后调用。

需要强调的一点是,这个方法只会出现在 Activity 被异常终止的情况下,正常情况下系统不会回调这个方法。Activity 被重新创建后,系统会调用 onRestorelnstanceState,并且把 Activity 销毁时 onSavelnstanceState 方法所保存的 Bundle 对象作为参数同时传递给 onRestorelnstanceState 和 onCreate 方法。

因此,我们可以通过 onRestorelnstanceState 和 onCreate 方法来判断 Activity 是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestorelnstanceState 的调用时机在 onStart 之后。

同时,我们要知道,在 onSavelnstanceState 和 onRestorelnstanceState 方法中,系统自动为我们做了一定的恢复工作。当Activity在异常情况下需要重新创建时,系统会默认为我们保存当前 Activity 的视图结构,并且在 Activity 重启后为我们恢复这些数据,比如文本框中用户输入的数据、ListView滚动的位置等,这些View相关的状态系统都能够默认,为我们恢复。

具体针对某一个特定的 View 系统能为我们恢复哪些数据,我们可以査看 View 的源码。和 Activity 一样,每个 View 都有 onSavelnstanceState onRestorelnstanceState 这两个方法,看一下它们的具体实现,就能知道系统能够自动为每个 View 恢复哪些数据。

关于保存和恢复 View 层次结构,系统的工作流程是这样的:首先 Activity 被意外终止时,Activity 会调用 onSavelnstanceState 去保存数据,然后 Activity 会委托 Window 去保存数据,接着 Window 再委托它上面的顶级容器去保存数据。顶层容器是一个 ViewGroup, 一般来说它很可能是 DecorView最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。

可以发现,这是一种典型的委托思想,上层委托下层、父容器委托子元素去处理一件事情,这种思想在 Android 中有很多应用,比如 View 的绘制过程、事件分发等都是釆用类似的思想。至于数据恢复过程也是类似的,这里就不再重复介绍了。接下来举个例子,拿TextView来说,我们分析一下它到底保存了哪些数据。

 

源码:TextView# onSavelnstanceState

    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();

        // Save state if we are forced to
        boolean save = mFreezesText;
        int start = 0;
        int end = 0;

        if (mText != null) {
            start = getSelectionStart();
            end = getSelectionEnd();
            if (start >= 0 || end >= 0) {
                // Or save state if there is a selection
                save = true;
            }
        }

        if (save) {
            SavedState ss = new SavedState(superState);
            // XXX Should also save the current scroll position!
            ss.selStart = start;
            ss.selEnd = end;

            if (mText instanceof Spanned) {
                Spannable sp = new SpannableStringBuilder(mText);

                if (mEditor != null) {
                    removeMisspelledSpans(sp);
                    sp.removeSpan(mEditor.mSuggestionRangeSpan);
                }

                ss.text = sp;
            } else {
                ss.text = mText.toString();
            }

            if (isFocused() && start >= 0 && end >= 0) {
                ss.frozenWithFocus = true;
            }

            ss.error = getError();

            return ss;
        }

        return superState;
    }

从上述源码可以很容易看出,TextView 保存了自己的文本选中状态和文本内容,并且通过査看其 onRestorelnstanceState 方法的源码,可以发现它的确恢复了这些数据,具体源码就不再贴出了,读者可以去看看源码。

 

旋转屏幕异常终止Activity

下面我们看一个实际的例子,来对比一下 Activity 正常终止和异常终止的不同,同时验证系统的数据恢复能力。为了方便,我们选择旋转屏幕来异常终止Activity,如图1-4所示。

 

通过图1-4可以看出,在我们选择屏幕以后,Activity被销毁后重新创建,我们输入的文本“这是测试文本”被正确地还原,这说明系统的确能够自动地做一些View层次结构方面的数据存储和恢复。

 

旋转屏幕异常终止——代码示例

下面再用一个例子,来验证我们自己做数据存储和恢复的情况,代码如下:

package com.yyh.demo2.onSavelnstanceState;

import com.ryg.chapter_1.R;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;


public class MainActivity extends Activity {

    public static final String TAG = "MainActivity@@@";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_demo2);

        if (savedInstanceState != null) {
            String string = savedInstanceState.getString("extra_test");
            Log.d(TAG, "[onCreate]:" + string);
        }
        
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG, "onSaveInstanceState");
        outState.putString("extra_test", "test");
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String string = savedInstanceState.getString("extra_test");
        Log.d(TAG, "[onRestoreInstanceState]:" + string);
    }
    
    
    @Override
    protected void onStart() {
        Log.d(TAG, "onStart");
        super.onStart();
    }

    @Override
    protected void onResume() {
        Log.d(TAG, "onResume");
        super.onStart();
    }
    
    @Override
    protected void onPause() {
        Log.d(TAG, "onPause");
        super.onPause();
    }
    
    @Override
    protected void onStop() {
        Log.d(TAG, "onStop");
        super.onStop();
    }
    
    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }
    
    @Override
    protected void onRestart() {
    	Log.d(TAG, "onRestart");
    	super.onRestart();
    }
    
}

上面的代码很简单,首先我们在 onSavelnstanceStete 中存储一个字符串,然后当 Activity 被销毁并重新创建后,我们再去获取之前存储的字符串。接收的位置可以选择 onRestorelnstanceState 或者 onCreate。

 

onRestorelnstanceState 和 onCreate 的区别

二者的区别是:onRestorelnstanceState 一旦被调用,其参数 Bundle savedlnstanceState 一定是有值的,我们不用额外地判断是否为空;但是 onCreate 不行,onCreate 如果是正常启动的话,其参数 Bundle savedlnstanceState null,所以必须要额外判断。这两个方法我们选择任意一个都可以进行数据恢复,但是官方文档的建议是釆用 onRestorelnstanceState 去恢复数据。下面我们看一下运行的日志,如图1-5所示。

如图1-5所示,Activity被销毁了以后调用了 onSavelnstanceState 来保存数据,重新创建以后在 onCreate 和 onRestorelnstanceState中都能够正确地恢复我们之前存储的字符串。 这个例子很好地证明了上面我们的分析结论。针对 onSavelnstanceState 方法还有一点需要说明,那就是系统只会在 Activity 即将被销毁并且有机会重新显示的情况下才会去调用它。 考虑这么一种情况,当Activity正常销毁的时候,系统不会调用 onSavelnstanceState,因为被销毁的 Activity 不可能再次被显示。

这句话不好理解,但是我们可以对比一下旋转屏幕所造成的 Activity 异常销毁,这个过程和正常停止 Activity 是不一样的,因为旋转屏幕后,Activity 被销毁的同时会立刻创建新的Activity实例,这个时候 Activity 有机会再次立刻展示,所以系统要进行数据存储。这里可以简单地这么理解,系统只在 Activity 异常终止的时候才会调用 onSavelnstanceState 和 onRestorelnstanceState 来存储和恢复数据,其他情况不会触发这个过程。

 

情况2:资源内存不足导致低优先级的Activity被杀死

这种情况我们不好模拟,但是其数据存储和恢复过程和情况1完全一致。这里我们描述一下Activity的优先级情况。Activity按照优先级从高到低,可以分为如下三种:

(1)前台 Activity——正在和用户交互的Activity,优先级最高。

(2)可见但非前台 Activity,比如 Activity 中弹出了一个对话框,导致Activity可见但是位于后台无法和用户直接交互。

(3)后台 Activity——已经被暂停的Activity,比如执行了 onStop,优先级最低。

当系统内存不足时,系统就会按照上述优先级去杀死目标 Activity 所在的进程,并在后续通过 onSavelnstanceState onRestorelnstanceState 来存储和恢复数数据。如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死。

因此,一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入 Service 中从而保证进程有一定的优先级,这样就不会轻易地被系统杀死。

 

当系统配置发生改变后,Activity不重新创建的情况

上面分析了系统的数据存储和恢复机制。我们知道,当系统配置发生改变后,Activity 会被重新创建,那么有没有办法不重新创建呢?

答案是有的,接下来我们就来分析这个问题。系统配置中有很多内容,如果当某项内容发生改变后,我们不想系统重新创建 Activity,可以给 Activity 指定 configChanges 属性。比如不想让 Activity 在屏幕旋转的时候重新创建,就可以给 configChanges 属性添加orientation 这个值,如下所示

android:configChanges="orientation"

如果我们想指定多个值,可以用 “ ”连接起来,比如 android: configChanges="orientation | keyboardHidden"。系统配置中所含的项目是非常多的,下面介绍每个项目的含义,如表1-1所示。 

 

1-1 configChanges的项目和含义                                    

从表1-1可以知道,如果我们没有在 Activity 的 configChanges 属性中指定该选项的话,当配置发生改变后就会导致 Activity 重新创建。上面表格中的项目很多,但是我们常用的只有 localeorientation 和 keyboardHidden 这三个选项,其他很少使用。需要注意的是 screenSize 和 smallestScreenSize,它们两个比较特殊,它们的行为和编译选项有关,但和运行环境无关。

 

指定configChanges属性——代码示例

下面我们再看一个demo,看看当我们指定了 configChanges 属性后,Activity 是否真的不会重新创建了。我们所要修改的代码很简单,只需要在AndroidMenifest.xml 中把  configChanges 属性加入到 Activity 的声明里面即可,代码如下:

<activity
   android:name="com.yyh.demo3.onConfigurationChanged.MainActivity"
   android:configChanges="orientation|screenSize"
   android:label="@string/app_name" 
   >
   <intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

需要说明的是,由于编译时笔者指定的 minSdkVersion 和 targetSdkVersion 有一个大于13,所以为了防止旋转屏幕时 Activity 重启,除了 orientation,我们还要加上 screenSize原因在上面的表格里已经说明了。

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19"/>

另外在 MainActivity 里面添加重写方法 onConfigurationChanged

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
    	// TODO Auto-generated method stub
    	super.onConfigurationChanged(newConfig);
    	Log.d(TAG, "onConfigurationChanged, newOrientation:" + newConfig.orientation);
    }

其他代码还是不变,运行程序,多次旋转屏幕,看看 log如图1-6所示。

     

由上面的日志可见,Activity的确没有重新创建,并且也没有调用 onSavelnstanceState 和 onRestorelnstanceState 来存储和恢复数据,取而代之的是系统调用了 Activity 的 onConfigurationChanged 方法,这个时候我们就可以做一些自己的特殊处理了

 

 

请别只做拿来主义者,如果觉得写的不错、对你有用,留下你的足迹:点赞 或 评论 支持下!

一直被模仿从未被超越,你们的支持是我们这些写博客博主们的动力!我们将继续分享干货!

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

被开发耽误的大厨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值