共计 6795 个字符,预计需要花费 17 分钟才能阅读完成。
前言
Hi,大家好,又见面啦,上一期我们讲了如何安装 AS,是不是已经有小伙伴迫不及待的创建了自己的项目并开始尝试了呢?那么这一期我们主要为大家介绍 Activity。作为 Android 的四大组件之一,Activity 占据着非常重要的作用。本文将围绕 Android 的生命周期、启动模式、基本配置等方面进行介绍。
简介
应用程序的每一个界面都是一个 Activity,所以也有人称其为视图界面。从字面的意思去理解,Activity 具有活动的意思,我们在应用中进行的操作都是集中在 Activity 上面完成,例如拨号、拍照、发送 email、看地图。每一个 activity 被给设置到一个窗口,在上面可以绘制交互界面。一个应用程序通常由多个 activities 组成,他们通常是松耦合关系,通常一个应用程序包含有一个主 Activity,即点击桌面图标的时候首先进入的 Activity。
Android 创建与启动
以一个简单的 Activity 的创建与使用示说明:
创建
在 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 的生命周期如下图:
(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 的启动模式。
在探究什么是启动模式之前要弄请几个问题:
- 启动模式原理(什么是任务栈)?
- 为什么会需要启动模式?
- 四种任务栈的特点?
- 使用方式
- 应用场景
什么是任务栈
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 个数。下面是方法和其对应运行时对应结果。
这样造成数据沉余,重复数据太多,最终可能还会导致内存溢出(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 只有一个实例,并且只能单独的存在一个任务栈中。
使用方式
启动模式一般分为两种:
- 在 AndroidManifes.xml 中,找到声明 Activity 的位置,在 Actvity XML 属性 android:launchMode=”standard”, 其他模式(singleTop,singleTask,singleInstance)声明。
- 在代码中跳转 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 的四大组件之一,并且也是项目开发过程中最常用到的,小伙伴们要深刻理解,并把它灵活的运用到项目中,当然这些也是面试中会问到的最基础问题。
PS: 如果还有未看懂的小伙伴,欢迎加入我们的 QQ 技术交流群:892271582,里面有各种大神回答小伙伴们遇到的问题哦~