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

 

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

作为本书的第1章,本章主要介绍Activity相关的一些内容。Activity作为四大组件之 首,是使用最为频繁的一种组件,中文直接翻译为“活动",但是笔者认为这种翻译有些生硬,如果翻译成界面就会更好理解。正常情况下,除了 WindowDialogToast,我们能 见到的界面的确只有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必须先执行完,新ActivityonResume才会执行。
  6. onStop表示Activity即将停止,可以做一些稍微重量级的回收工作,同样不能太耗时。
  7. onDestroy表示Activity即将被销毁,这是Activity生命周期中的最后一个回调, 在这里,我们可以做一些回收工作和最终的资源释放。

正常情况下,Activity的常用生命周期就只有上面7个,图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:onStartonResumeonPauseonStop从描述上来看差不多,对我们来说有什么实质的不同呢?

问题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 中单击按钮可以跳转到 Second Activity,同时为了分析我们的问题,在生命周期方法中打印出了日志,通过日志我们就能看出它们的调用顺序。

代码:MainActivity.java

package com.yyh.demo1.activitysample;

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.activitysample;

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.情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建

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

 

 

 

                                       

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章