Android面试必问的Activity,初阶,中高阶问法,你都掌握了吗?(要求熟读并背诵全文)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cXs1wGDx-1605843173409)(https://upload-images.jianshu.io/upload_images/24142630-84668ed4a42819ee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

Activity是我们常用App开发中最重要的组件,主要用于展示界面和用户交互。本文分为三个部分:

Activity源码和常见的问题

  1. Activity的生命周期,正常情况和异常情况?
  2. Activity的四种启动模式,启动页设置singleTask/singleInstance可能造成什么后果?
  3. 任务,任务栈,前台任务栈,后台任务栈,返回栈分别是什么?
  4. startActivityForResult导致的一系列问题?
  5. 清除返回栈(Clearing the back stack)的一些概念
  6. allowTaskReparenting的使用
  7. Activity的隐式启动
  8. Activity启动流程

Activity深层次问题

  1. Activity生命周期的变化对进程的优先级有什么影响?
  2. 如果App还存在缓存进程,这个时候启动App,应用Application的onCreate方法会执行吗?
  3. 一个Activity A启动另一个Activity B,为何会先走A的onPause方法,等到B执行完onResume方法后,才会走A的onStop方法呢?
  4. 为什么要这么设计Activity生命周期?

第三方App中一些Activity的设置

  1. 今日头条极速版-新闻界面打开的一些限制和首页
Activity源码和常见的问题
1.Activity的生命周期,正常情况和异常情况?

首先来看看官网上Activity的生命周期,如下图所示

Android面试必问的Activity,初阶,中高阶问法,你都掌握了吗?(要求熟读并背诵全文)_第1张图片

生命周期在开发中会常常被我们用到,比如在界面的恢复和销毁等回调具体的方法,我们在这些方法做一些数据的处理等。当然这里面还少了方法onSaveInstance和onRestoreInstance方法用于状态的保存和恢复,还有一个方法onConfigurationChanged()用于配置变更后的回调。

下面是一些常用的生命周期回调流程

  • 启动Activity:onCreate()->onStart()->onResume()

  • 点击返回键:onPause()->onStop()->onDestroy()

  • 点击Home键:onPause()->onSaveInstanceState()->onStop()注意在API28之后onSaveInstanceState()方法的执行放在了onStop()之后

  • 用户再次回到原Activity:onRestart()->onStart()->onResume()

  • A Activity启动B Activity:A#onPause()->B#onCreate()->B#onStart()->B#onResume()->A#onStop()

再来看一下异常情况下的生命周期分析

  1. 系统配置发配置变化时生命周期的回调(API28+)

    onPause()->onStop()->onSaveInstanceState()->onDestroy(),然后当Activity被重新创建后执行onCreate()->onStart()->onRestoreInstanceState()->onResume()

    这里的配置发生变化可以指屏幕发生旋转或者切换到多窗口模式等等。

    系统配置发生改变时,如果不想重新创建Activity,可以通过在AndroidManifest.xml中配置android:configChanges属性,如果想做一些额外的操作可以在onConfigurationChanged回调中处理

  2. 资源内存不足导致低优先级进程被回收,当系统资源不足时,会杀死低优先级进程,此时会调用onSaveInstanceState()和onRestoreInstanceState()进行数据的存储和恢复。

2.Activity的四种启动模式,启动页设置SingleTask/SingleInstance可能造成什么后果?

在清单文件中声明 Activity 时,可以使用 Activity 元素的 launchMode 属性指定 Activity 应该如何与任务关联。

  • stardard:默认模式,系统在启动该 Activity 的任务中创建 Activity 的新实例,并将 intent 传送给该实例。Activity 可以多次实例化,每个实例可以属于不同的任务,一个任务可以拥有多个实例。注意在该模式下配合FLAG_ACTIVITY_NEW_TASK 与 FLAG_ACTIVITY_CLEAR_TOP,单独/一起配合,都会重新创建实例

  • singleTop:栈顶复用模式,如果当前任务的顶部已存在 Activity 的实例,则系统会通过调用其 onNewIntent() 方法来将 intent 转送给该实例,而不是创建 Activity 的新实例。在该模式下配合FLAG_ACTIVITY_CLEAR_TOP是用哪个,不会重新创建实例,会有类似SingleTask的效果,但是如果再加上FLAG_ACTIVITY_NEW_TASK,还是会创建新实例。

  • singleTask:栈内复用模式,系统会创建新任务,并实例化新任务的根 Activity。但是,如果另外的任务中已存在该 Activity 的实例,则系统会通过调用其 onNewIntent() 方法将 intent 转送到该现有实例,而不是创建新实例。Activity 一次只能有一个实例存在。该模式默认具有clearTop的效果。

  • singleInstance:单实例模式,与 "singleTask" 相似,唯一不同的是系统不会将任何其他 Activity 启动到包含该实例的任务中。该 Activity 始终是其任务唯一的成员;由该 Activity 启动的任何 Activity 都会在其他的任务中打开。

    关于singleInstance有个特殊的情况,如果一个A Activity(standard)启动B Activity(singleInstance),这个时候用户点击了手机最近访问列表,然后在再点击该App所在的界面(卡片),然后这个时候点击返回键竟然就直接退出了App,而不是我们预期的退到A Activity界面。其实最近访问列表也是一个Activity(假设为C Activity),当我们从这个C Activity点击App卡片显示我们的singleInstance所在的界面B,这个时候就相当于C启动了B,所以我们点击返回键,就直接回到了桌面(有兴趣可以自己看看源码)。

    还有一个特殊的情况(来自扔物线大佬的文章),就是在最近任务里看见的 Task 未必还活着,最近任务里看不见的 Task,也未必就死了,比如 singleInstance。当我们查看最近任务的时候,不同的 Task 会并列展示出来,但有一个前提:它们的 taskAffinity 需要不一样。在 Android 里,同一个 taskAffinity 可以被创建出多个 Task,但它们最多只能有一个显示在最近任务列表。这也就是为什么刚才例子里 singleInstance 的那个 Activity 会从最近任务里消失了:因为它被另一个相同 taskAffinity 的 Task 抢了排面。

    同理,你在一个App从首页Activity新建一个Activity(singletask/singleInstance),如果没有指定taskAffinity,这个Activity的taskAffinity和其他界面一样,所以在最近的范围列表,你也只能看到一个App的卡片,但是如果你taskAffinity设置的不一样,就可以看到在最近列表中看到两个了。

上面讲到的任务对应的是TaskRecord(栈结构),其内部维护了一个ArrayList用来保存和管理ActivityRecord,ActivityRecord包含了一个Activity的所有信息

通常我们的App都会设置启动页(SplashActivity通常是一张图片),然后进入我们的主界面(MainActivity),在主界面中通常有很多逻辑会导致该界面异常庞大,占据的内存很大,所以很多时候我们都会给该界面设置为SingleTask栈内复用模式。

场景一:如果为了达到快速启动的效果,将我们的App的闪屏页(SplashActivity显示固定图片)移除掉,换成MainActivity(SingleTask/SingleInstance)的背景(windowBackground),最后再替换成App的主题,给用户快速响应的体验;

场景二:如果给启动页SplashActivity设置为SingleTask/SingleInstance模式,同时你的启动页没有及时的关闭。

以上两种场景会导致你的App无论冷启动还是热启动,每次点击图标都是从启动页开始启动的,具体的原理可以看我这篇文章的分析和解决方案。切记,不要在你的App启动界面设置SingleTask/SingleInstance。

3.任务,任务栈,前台任务栈,后台任务栈,返回栈分别是什么?

首先来看官网的说明Understand Tasks and Back Stack,(A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack—the back stack)—in the order in which each activity is opened. )任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中。前面说过任务对应的是TaskRecord(栈结构),其内部维护了一个ArrayList用来保存和管理ActivityRecord,ActivityRecord包含了一个Activity的所有信息。所以其实任务就是任务栈(TaskRecord是栈结构)。

那么返回栈是什么,首先展示一张Gityuan博客的图片。

Android面试必问的Activity,初阶,中高阶问法,你都掌握了吗?(要求熟读并背诵全文)_第2张图片

  • 一般地,对于没有分屏功能以及虚拟屏的情况下,ActivityStackSupervisor与ActivityDisplay都是系统唯一;
  • ActivityDisplay主要有Home Stack和App Stack这两个栈;
  • 每个ActivityStack中可以有若干个TaskRecord对象,当前只会有一个获得了焦点的ActivityStack
  • 每个TaskRecord包含如果若干个ActivityRecord对象;
  • 每个ActivityRecord记录一个Activity信息。

一个返回栈可能只包含一个任务,但在特殊情况下,可能引入多个任务。这个概念非常重要,这里引用官方的图

Android面试必问的Activity,初阶,中高阶问法,你都掌握了吗?(要求熟读并背诵全文)_第3张图片

这里先说一下操作流程,依次启动ActivityX,ActivityY,Activity1,Activity2;ActivityY,ActivityX(这两个都是SingleTask)在后台任务中,Activity2,Activity1在前台任务中,这两个任务的taskAffinity不同,当从Activity2中启动ActivityY的时候,返回栈如第二列所示,然后点击返回键可以一个个退出。

再普及一个概念在 Android 里,每个 Activity 都有一个 taskAffinity,它就相当于是对每个 Activity 预先进行的分组。它的值默认取自它所在的 Application 的 taskAffinity,而 Application 的 taskAffinity 默认是 App 的包名。当然也可以手动指定taskAffinity。

但是图中并没有指明Activity2,Activity1是什么启动模式,实际上我如果我们指定为standard标准模式根本模拟不出这个场景,这一点有点坑,因为这四个Activity分别按2,1,X,Y排列,也即是说启动是从Y,X,

你可能感兴趣的:(android进阶,面试,Android开发,android,面试)