美文网首页
Android 基础面试题总结

Android 基础面试题总结

作者: 彭旭锐 | 来源:发表于2021-04-18 17:49 被阅读0次

    点赞关注,不再迷路,你的支持对我意义重大!

    🔥 Hi,我是丑丑。本文 「Android 路线」| 导读 —— 从零到无穷大 已收录,这里有 Android 进阶成长路线笔记 & 博客,欢迎跟着彭丑丑一起成长。(联系方式在 GitHub)


    四大组件

    说一下 Activity 启动模式

    要想真正理解 Activity 启动模式,一定要先理解启动模式背后的 Task 工作模型,这样才能更好地记住每种启动模式的特点,以及为什么需要启动模式这种设计。

    当我们点击最近任务键,可以查看最近任务列表并在多个应用之间进行切换。不过,更加准确的说法既不是在多个应用间切换,也不是在多个 Activity 之间切换,而是在多个 Task 之间切换。Task 工作模型主要有以下三个重要特点:

    • 回退栈: 每个 Task 都有回退栈,它按照先进后出的顺序维护 Activity 的顺序,当回退栈清空时,Task 的生命周期就结束了。不过,已经消亡的 Task 依然会保留在最近任务列表中,目的是方便用户 “切回去”,不过此时 “切回去” 不是一般的任务切换,而是重新启动 App(可以观察到执行的是启动 App 的动画,而不是切换任务的动画)。

    提示: 最近任务列表里的任务不一定是活着的。

    • Task 栈: Activity 可以叠成栈,Task 也可以叠成栈。不过 Task 叠加适用于前台 Task,当 Task 由前台进入后台,叠加的 Task 会在第一时间被拆开。Task 进入后台的场景有两种:
      • 1、按 Home 键返回桌面;
      • 2、按最近任务键查看最近任务列表。

    注意: 查看最近任务列表的时候 Task 就已经进入后台,而不是在切换任务之后才进入后台。

    • taskAffinity(任务相关性): 每个 Activity 都有 taskAffinity,Activity 默认取 Application,而 Application 默认取应用包名,每个 Task 的 taskAffinity 又取自栈底 Activity 的 taskAffinity。既然 Task 的 taskAffinity 取自栈底 Activity,那么说明可能会存在多个 taskAffinity 相同的Task。需要注意:taskAffinity 冲突的 Task 只会在最近任务列表里出现一个。

    提示: 最近任务列表里的看不到的任务不一定是消亡的。

    理解了 Task 工作模型的三个重要特点,现在我们来讨论不同启动模式的特点。

    • Standard(默认)的行为规则总结起来是 “在启动的 Task 创建”:
      在 Task A 启动 Activity,Activity 会被直接进入到 Task A 的栈顶,在不同的 Task 里打开同一个 Activity,Activity 会被创建多个实例分别放进每一个 Task,互不干扰。这是符合大部分产品逻辑和用户心理的,所以这是默认的设置。当你的产品逻辑和默认规则不同时,你就需要使用其他的启动模式。

    • SingleTask 的行为规则是总结起来是 “在固定的 Task 创建 + 在 Task 内唯一”:

      • 在固定的 Task 创建:在 Task A 启动 Activity,系统会判断 Task A 的 taskAffinity 和新 Activity 的 taskAffinity 是否相同。如果相同,新 Activity 入栈,如果不同,新 Activity 会进入和自己 taskAffinity 相同的 Task(可以观察到执行了任务切换的动画,而不是一般的 Activity 入场动画)。这样就保证了 SingleTask Activity 一定会在固定的 taskAffinity Task 里创建。

      • 在 Task 内唯一:如果要启动的 Acitivity 已经在 Task,那么会直接复用并回调 onNewIntent() 来刷新界面。另外,如果该 Activity 上方叠加了其他 Activity,那么其他 Activity 都会弹出栈。

    在 SingleTask 的两条规则限定之下,SingleTask Activity 事实上在全局只有一个单例对象,那么名称为 “单例” 的 SingleInstance 的行为规则是怎样的呢?

    • SingleInstance 的行为规则与 singleTask 基本一致,但增加了更加严格独占性 “Task 里只有这一个 Activity”:
      「Task A」打开「Task B」的 Activity,Activity 不会进入到 Task A 里,而是会创建一个新的 Task,然后随着整个 Task 一起压在 Task A 上面。随后用户继续启动 Activity,新的 Activity 并不会进入同一个 Task 里,而是会在另一个 Task 里创建,然后整个 Task 拿过来压在上面。

    提示: 当我们使用 SingleInstance 打开 Activity 时,我们会发现最近任务列表里找不到这个任务,这是因为 taskAffinity 冲突了。

    • SingleTop 的行为规则和 Standard 基本一致,但增加了额外 “栈顶复用” 规则:
      如果 Task 栈顶的 Activity 恰好就是要启动的 Activity,那么系统不会创建新的 Activity,而是会回调 onNewIntent()。

    除了四种启动模式,还有其他方式可以影响 Activity 的行为规则:

    • Allow Reparenting 行为规则是 “在启动的 Task 创建 + 迁移”:
      • 在启动的 Task 创建:「Task A」打开「Task B」的 Activity,Activity 会被直接进入到 Task A 的栈顶,这与 Standard 模式类似;
      • 迁移:有两个时机会进行 Activity 迁移:
        1、Task B 启动时;
        2、Task A 进入后台并且 Task B 已经存在,该 Activity 会自动迁移到 Task B 中。

    注意: android:allowTaskReparenting 属性 在 Android 9 和 Android 10 系统上是失效的。

    启动模式 中文翻译 行为规则
    Standard 默认 在启动的 Task 创建
    SingleTop 栈顶复用 与 Standard 基本一致,增加 “栈顶复用” 规则
    SingleTask 栈内复用 在固定的 Task 创建 + 在 Task 内唯一
    SingleInstance 栈内独占 与 SingleTask 基本一致,增加 “栈内独占” 规则
    • 应用场景:

    Standard 和 SingleTop 是 Task 内的启动规则,而 SingleTask 和 SingleInstance 是跨 Task 的启动规则(虽然不一定会跨 Task)。因此通常的选择是:用于 在 App 内部交互时使用 Standard 和 SingleTop,在开放给外部共享的 Activity 使用 SingleInstance,而 SingleTask 是内部交互和外部共享都可以用。

    在更具体的场景上:SingleTop:推送消息(如果当前页面和推送消息点击跳转的页面相同,那么可以考虑复用这个 Activity);SingleTask:应用主页(因为无论从什么场景进入主页,都应该清空上面的 Activity);SingleInstance:开放给外部共享的页面,例如打电话,写短信等页面。


    说一下 Activity 生命周期


    说一下 Fragment 生命周期


    Intent 和 PendingIntent 的区别?

    在创建PendingIntent对象时是跟system_process(系统服务进程,AMS、PMS、WMS都在这个进程)有互动的

    因为PendingIntent的相关数据都保存在【系统服务进程】,那么就算创建这个对象的进程已经被kill了,只要这个PendingIntent对象还存在,那它还是能够起到作用的。

    PendingIntent 可以理解为延迟执行的 Intent,在某个特定条件下才会执行该 Intent。

    Flags 的类型:

    FLAG_ONE_SHOT:得到的 PendingIntent 只能使用一次,使用一次后自动调用 cancel 方法解除 PendingIntent 和 Intent 的关联
    FLAG_NO_CREATE:当 PendingIntent 不存在时,不进行创建,直接返回 null
    FLAG_CANCEL_CURRENT:当 PendingIntent 已存在时,执行 cancel 进行解除,再创建一个新的
    FLAG_UPDATE_CURRENT:不存在时就进行创建,创建后每次使用会对数据进行更新
    FLAG_IMMUTABLE:创建好 PendingIntent 后就保持一成不变
    send 方法:

    调用 send 方法时会启动包装的 Intent

    cancel 方法:

    调用 cancel 方法时会解除 PendingIntent 和被包装 Intent 之间的关联,只有创建该 PengdingIntent 的程序才有权解除


    如何判断 App 位于前台还是位于后台,如何监听?

    PendingIntent

    https://developer.android.google.cn/guide/topics/ui/notifiers/notifications

    https://www.youtube.com/watch?v=nRcYhsDTyjo
    https://www.youtube.com/watch?v=Bh9qg9NivtU

    相关深入文章:


      1. ActivityA -> Activity B -> Activity A
      2. Activity A 启动模式为 singleTask
      3. Activity B 启动模式为常规模式
      4. 问 A 启动 B,B 又启动 A 的生命周期调用顺序?

    2、数据存储

    Serializable 和 Parcelable 的区别?

    我们一定要分清楚什么是协议协议和什么是实现:举个例子,Json 是序列化协议,而 Gson、fastJson、jackJson 这些是对 Json 协议的实现 (序列化目的是将对象转换为可以存储或传输的形式,在将来,反序列化就是把数据重新创建为新的对象。为了保证序列化和反序列化前后的对象信息一致,就需要定义序列化协议,只要你遵循序列化协议,你也可以创建一个新的序列化实现)。现在,我们再回过头来讨论 Serializable([ˈsɪərɪəlaɪzəbl]) 和 Parcelable ([ˈpɑːsləbl]) 的区别:

    • 协议:

    先讨论两者的协议:

    严格来说,Serializable 只是一个序列化实现,它背后是 JDK 序列化协议(另一个实现有 FST(fast-serialization),它是兼容 JDK 序列化协议的,但效率和产物优于 Serializable)。这种协议的序列化大体上是一种 “TLV(Type-Length-Value)” 编码,也就是说编码中会带有字段类型 / 长度等信息,有效载荷低。而 Parcelable 的编码更紧凑,大体上是通过一个游标指针 nativePtr 来操作序列化数据:序列化写入数据后,游标移动数据长度(超出空间时会扩展),反序列化读取数据,也移动数据长度,有效载荷高(牺牲了扩展性和便利性)。

    • 实现:

    再讨论两者的实现细节:

    严格来说,虽然两者的目的都是序列化,但是它们的使用场景是不一样的,Serializable 做的事情就是把 JDK 序列化协议实现出来,再对外提供一套 API(ObjectInput/OutputStream),这和 Protobuf / Gson 等做的事情是类似的。而 Parcelable 除了序列化这个目的之外,还是对 Android 进程间数据传输做的针对性优化:通过共享内存(memcpy)作为进程间传输的媒介,Android 中的 IBinder 通信的数据载体,用的就是 Parcelable。

    • Serializable 的其他实现细节:
      • 1、Serializable / Externalizable 只是标记接口,真正的序列化实现在 ObjectInput/OutputStream;
      • 2、序列化产物会带有当前类的 SerialVersionUID 版本,反序列化时会对比类的 SerialVersionUID 版本与序列化数据中的版本是否一致,如果不一致会抛出 InvalidClassException;
      • 3、反序列化使用反射创建对象;
      • 4、网上说 Serializable 序列化过程有文件 I/O,这个说法是不严谨的,应该说序列化本身是没有文件 I/O 的,但要进程间传输序列化数据,可能就需要文件 I/O。如果使用 Parcelable 就通过进程共享内存传输,不需要经过磁盘文件;
      • 5、序列化产物举例:
    类定义:
    class TestSerial implements Serializable {
        public byte version = 100;
    }
    ------------------------------------------------------------------
    序列化产物:
    AC ED (序列化协议)
    00 05 (序列化版本)
    73     (TC_OBJECT. 新的对象)
    72     (TC_CLASSDESC. 这是一个新类描述) 
    00 0A  (类名的长度)
    53 65 72 69 61 6C 54 65 73 74 (类的名称) 
    05 52 81 5A AC 66 02 F6 (SerialVersionUID)
    02     (Various flags,0x02代表这个对象支持序列化) 
    00 01  (类有几个字段) 
    49     (代表是int类型)
    00 07  (字段名称的长度)
    76 65 72 73 69 6F 6E (version, 字段的名称)
    78     (TC_ENDBLOCKDATA, 描述的结束符)
    70     (TC_NULL)
    00 00 00 64 (version的值)
    
    • Parcelable 的其他实现细节:
      • 1、Parcelable 真正的序列化实现在 native 层 Parcel.cpp
      • 2、write 和 read 的顺序要保持一致;
      • 3、反序列化未使用反射??

    相关深入文章:
    Parcelable vs Serializable
    Parcelable 最强解析
    Parcelable 为什么效率高于 Serializable ?


    3. 资源

    mipmap 和 drawable 的区别?

    mipmap 文件夹存放应用启动图标,而其他类型的图片资源(如位图、点 9 图、矢量图)都应该存放在 drawable 文件夹。Launcher(桌面)在显示应用图标时,可以选择显示比当前屏幕更高密度的图片,以获取更高的清晰度和质量。

    mipmap 和 drawable 的另一个区别体现在发布应用时的优化差异:在发布应用时,开发者可以向 Google Play 上传 App Bundle,Google Play 会创建优化的 Apk。比如对于 xhdpi 的设备,它会删除所有其他密度的 drawable 文件夹,只保留 xhdpi 的 drawable 文件夹和通用的 drawable 文件夹。因此用户下载的 Apk 包体积更小。mipmap 文件夹不同,优化 Apk 时所有的 mipmap 文件夹都会保留。

    https://gitee.com/xcy001/AndroidInterview/blob/master/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%80%BB%E7%BB%93%EF%BC%88Android%E9%83%A8%E5%88%86%EF%BC%89.md

    相关文章

      网友评论

          本文标题:Android 基础面试题总结

          本文链接:https://www.haomeiwen.com/subject/bgfhlltx.html