前言

Hi,大家好,又见面啦,上一期我们讲了如何安装AS,是不是已经有小伙伴迫不及待的创建了自己的项目并开始尝试了呢?那么这一期我们主要为大家介绍Activity。作为Android的四大组件之一,Activity占据着非常重要的作用。本文将围绕Android的生命周期、启动模式、基本配置等方面进行介绍。

简介

应用程序的每一个界面都是一个Activity,所以也有人称其为视图界面。从字面的意思去理解,Activity具有活动的意思,我们在应用中进行的操作都是集中在Activity上面完成,例如拨号、拍照、发送email、看地图。每一个activity被给设置到一个窗口,在上面可以绘制交互界面。 一个应用程序通常由多个activities组成,他们通常是松耦合关系,通常一个应用程序包含有一个主Activity,即点击桌面图标的时候首先进入的Activity。

Android创建与启动

以一个简单的Activity的创建与使用示说明:

创建

Android 四大组件之Activity-LMLPHP

在Androd Studio 新建项目完成后,会自动创建一个 Java 文件,这个文件就是 Activity,因为它继承系统 framework 层提供的 Activity,这里 AppCompatActivity 是 Activity 的子类,我们的 MainActivity 间接继承 Activity。

并且你会看到 MainActivity 强制重写了 onCreate 方法,在 onCreate 中,通过 setContentView 为 Activity 设置我们自定义的页面布局文件。

注册Activity

在 manifest 中注册Activity

     <activity
            android:name=".MainActivity"
            android:configChanges="orientation|screenSize"
            android:label="@string/app_name"
            android:launchMode="standard">
            <intent-filter>
                <action android:name="myactoin2" />

                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
      </activity>

Activity 必须在 AndroidManifest.xml 注册,如果没有注册这 Activity 就不能正常运行,其实在正常情况下在使用Android 中的四大组件(Activity,Service,BrocastReceiver,ContentProvider)时都需要再 AndroidManifest.xml 中注册。在上面的 xml 注册信息中,其中 **<inten-filter>**中指定

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

表明这个Activity是主Activity,在Android系统点击应用图标首先进入主Activity。

启动

Activity是通过Intent用来在应用程序的Activity间启动、停止和传输。

启动Activity的三种方法:

1、显示启动

在这里注册了第二个Activity ---- SecondActivity ,并且牢记前面的注册操作,我们把 SecondActivity 注册到 Manifest 文件中。现在就可以在 MainActivity 中启动 SecondActivity。

Intent intent = new Intent(this, SecondActivity.class);
//通过上下文Context和SecondActivity.java类的Class对象,创建 Intent 对象
startActivity(intent);//调用系统Framework 层提供的方法,启动 SecondActivity。

2、隐式启动

若 SecondActivity 在AndroidManifest.xml文件中配置 intent-filter 的 action 和 category、data,如下:

<intent-filter>
   <action android:name="myaction2"/>
   <category android:name="android.intent.category.DEFAULT"/>
   <category android:name="mycategory" />
</intent-filter>

那么可以通过如下方式启动 SecondActivity :

Intent intent = new Intent("myaction2");
startActivity(intent);

3、默认启动

通过桌面图标点击应用图标进入程序的第一个Activity,因其启动方式有别上述两个方式,将其划分为第三类的启动方式。 若Activity在AndroidManifest.xml文件的intent-filter的action和category,如下:

<intent-filter>
   <action android:name="android.intent.action.MAIN" />
   <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

那么,点击桌面的应用图标即可启动Activity。

注:如果在N个Activity中都配置上述的action和category,那么桌面会有N个应用的图标,点击不同的图标会进入对应的Activity。

Activity的生命周期

Activity 的生命周期如下图:

Android 四大组件之Activity-LMLPHP

(1) onCreate

表示Activity正在被创建,这是第一个执行的方法,在Activity的生命周期中只执行一次。在这个方法中做一些初始化工作,比如调用setContentView去加载界面布局,初始化Activity所需要的数据等。后续调用onStart()。

(2) onRestart

表示Activity正在重新启动,一般情况下,当前的Activity从不可见的状态变为可见状态时,onRestart就会被调用。这种情形一般是用户操作出现所致,比如用户按Home键回到桌面或者用户打开了一个新的Activity,这时候Activity就会暂停,接着用户又回到该Activity。后续调用onStart()。

(3) onStart

表示Activity正在被启动,即将开始。这个时候Activity是可见的,但是还没有出现在前台,不能和用户进行交互。这个时候可以理解为Activity已经显示出来,但是我们还看不到。后续的方法是onResume()。

(4) onResume

表示Activity可见,并且已经出现在前台并开始活动,能和用户正常进行交互。需要注意的是onStart和onResume的区别,二者都是Activity可见,但是onStart时Activity还在后台,而onResume时Activity到了前台了,这时候可以开启动画或者获取独占性设备的操作如打开相机、获取麦克风等。

(5) onPause

表示Activity由前台转到后台,正常情况下,紧接着onStop就会被调用。这时仍然可见。如果这时候快速地回到当前Activity,那么onResume会被调用,这类情况属于极端情况,用户操作很难重现这一场景。此时可以做一些存储数据,停止动画等操作,但是注意不能太耗时,如果太耗时会影响到新的Activity的显示。onPause是先执行完,新的Activity的onCreate才会执行。onResume和onPause相对应。

(6) onStop

表示Activity即将停止,当前的Activity对用户不在可见。可稍微做些重量级的回收操作。后续的操作可能是onRestart或者onDestroy或者一直保持这个状态。

(7) onDestory

表示Activity正在被销毁,是生命周期的最后一个回调,也是只调用一次。发生的条件是Activity本身已经执行完毕,或者系统资源不足需要回收资源将Activity销毁。

我们考虑如下几类情况: 1、当一个Toast弹出的时候,会发生回调么? No 2、当一个AlertDialog弹出的时候,会发生回调么? No, 如果AlertDialog获取焦点,Activity会触发onWindowFocusChanged回调 3、当一个PopWindow弹出的时候,会发生回调么? No, 如果PopWindow获取焦点,如mPopupWindow.setFocusable(true),Activity会触发onWindowFocusChanged回调。 4.横竖屏切换时,会造成Activity被销毁然后重新创建。若在Activity配置android:configChanges="orientation",横竖屏切换时,只触发onConfigurationChanged( )回调,Activity不会被重新创建。

启动模式

Activity 有四种启动模式,不同的模式,对应这 Activity 对象的创建于复用策略,可以在 Manifest 和 代码中指定 Activity 的启动模式。

在探究什么是启动模式之前要弄请几个问题:

  1. 启动模式原理(什么是任务栈)?
  2. 为什么会需要启动模式?
  3. 四种任务栈的特点?
  4. 使用方式
  5. 应用场景
什么是任务栈

Android 任务栈又称为 Task,它是一个栈类型的数据结构:先进先出。它用于存储我们的 Activity 组件。

每次打开一个新的 Activity 或 退出一个 Activity 都会在任务栈的结构中添加或减少一个 Activity,一个任务栈包含了一个 Activity 集合。Android 系统可以通过 Task 有序的管理每个 Activity ,并决定那个 Activity 与用户进行交互:只用在栈顶的 Activity 才可以跟用户进行交互。

在应用程序退出时,必须把所有任务栈中的 Activity 清除栈时,任务栈才会被销毁。当然任务栈可以移动到后台,并且保存每个 Activity 的状态。可以有序的给用户列出Activity的任务,同时也不会丢失 Activity 的信息。

应用程序中可能不止一个任务栈,某系情况情况下,单独的一个 Activity 可以独享一个任务栈,也会存在一个任务栈的 Activity 可以来自不同的 App,同一个 App 中的 Activity 可能在不同的任务栈当中。

为什么会需要启动模式

在应用程序开发过程中,一般都需要在多个 Activity 组件之间跳转,也可能需要在本应用中打开其他应用的可复用的 Activity。在开发过程中需要跳转到原来已经开启的 Activity 实例,此时我们希望这个 Activity 可以被重用而不是再重新创建一个新的 Activity 实例,但根据 Android 系统的默认行为,每次都会为我们创建一个新的 Activity 实例对象并添加到任务栈中,而且 Activity 的数据和信息状态都将会被保留

我们可以在 MainActivity 中添加一个按钮,点击按钮跳转到 SecondActivity,然后在 SecondActivity 中添加两个按钮,点击一个按钮跳转到 MainActvity,在另一个按钮的点击事件中添加如下代码:

//获取ActivityMananger
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
//获取任务栈 可能是多个
List<ActivityManager.RunningTaskInfo> runningTasks = am.getRunningTasks(5);
//获取当前的任务栈
ActivityManager.RunningTaskInfo runningTaskInfo = runningTasks.get(0);
//获取当前任务栈中 Activity 个数,即当前没有换存活的 Activity 实例个数。
int numRunning = runningTaskInfo.numRunning;
//Log 打印信息
Log.e("SecondActivity", "numRunning" + numRunning);

上面代码不用深究,主要目的是想说明系统默认情况下,当前任务栈中存活的 Activity 实例个数。当我们在 MainActvity 中点击按钮跳转到 SecondActivity ,当跳转到 SecondActivity 在点击第一个按钮跳转到 MainActivity ,不断重复这个操作。最后当跳转的 SecondActivity 点击第一个按钮获取当前任务栈中的 Activity 个数。下面是方法和其对应运行时对应结果。

Android 四大组件之Activity-LMLPHP

这样造成数据沉余,重复数据太多,最终可能还会导致内存溢出(OOM)。为解决这些问题,Android 系统提供了一套 Activity的启动模式来修改默认的 Activity 启动模式。

四种任务栈的特点
  • Standard 模式(一般模式)

系统默认模式,每次启动一个Activity都会重新创建一个新的实例,而不管Activity是否已经创建了一个实例。

  • SingTop (栈顶复用模式)

栈顶复用模式,系统启动时,系统会启动当前栈顶Activity是不是要启动的Activity,如果是则不需要创建新的Activity而直接引用这个Activity,如果不是那么创建新的Activity。系统会回调Activity的onNewIntent()的方法。

  • SingTask(栈内复用模式)

栈内复用模式,如果栈内已经存在了一个Activity的实例,那么Activity不会被重新创建,同时这个Activity的onNewIntent()方法会被回调,并将该Activity实例置于栈顶,原先处于该实例顶部的Activity实例会被出栈销毁。如果是其他程序启动Activity,那么它会重新创建一个任务栈。

  • SingleInstance(单一实例模式)

单实例模式,是singleTask的加强版,具有singleTask所有特点,并且此种模式下Activity只有一个实例,并且只能单独的存在一个任务栈中。

使用方式

启动模式一般分为两种:

  1. 在 AndroidManifes.xml 中 ,找到声明 Activity 的位置,在 Actvity XML 属性 android:launchMode="standard",其他模式(singleTop,singleTask,singleInstance) 声明。
  2. 在代码中跳转 Activity 时,利用 Intent 指定 Flag 标志位来使用启动模式。示例代码如下:
Intent intent = new Intent(this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//Flag
startActivity(intent);

setFlags方法说明

//使用一个新的Task来启动Activity,但每个Activity都将在一个新的Task中。
Intent.FLAG_ACTIVITY_NEW_TASK
//使用singleTop模式来启动一个Activity。
Intent.FLAG_ACTIVITY_SINGLE_TOP
//使用singleTask模式来启动一个Activity。
Intent.FLAG_ACTIVITY_CLEAR_TOP
//使用singleTask模式来启动一个Activity,使用这种方式启动Activity,当Activity启动其他Activity的时候,该Activity会被销毁,不入栈。
Intent.FLAG_ACTIVITY_NO_HISTORY
//方式无法指定 SingleInstance 模式,SingleInstances 只能在 AndroidManifest.xml 中声明。
Intent.setFlags
常见使用场景

这里是一些在开发中常见的业务场景页面使用的页面启动模式:

Android 四大组件之Activity-LMLPHP

结语

作为Android的四大组件之一,并且也是项目开发过程中最常用到的,小伙伴们要深刻理解,并把它灵活的运用到项目中,当然这些也是面试中会问到的最基础问题。

PS:如果还有未看懂的小伙伴,欢迎加入我们的QQ技术交流群:892271582,里面有各种大神回答小伙伴们遇到的问题哦~

Android 四大组件之Activity-LMLPHP

08-06 11:25