Android面试题总结

一、设计模式

https://www.jianshu.com/p/4cb58907042d

责任链模式

责任链闭环,前面不处理就交给链上后面处理,典型的View事件分发机制

单例模式

懒汉模式、饿汉模式、DCL双重验证

工厂模式

静态工厂、工厂方法

建造者模式

用于多个参数方法的拆解,适用于构造参数特别多的对象上

代理模式

代理一个类,做一些下额外的事情

观察者模式

典型的应用EventBus、RxJava。注册观察者,被观察者循环观察者列表实现全局回调。

二、Java知识点

HashMap

HashMap的数据结构、HashMap的put方法原理



HashMap与ArrayMap的区别

HashMap是底层是数组+链表+红黑树,ArrayMap底层是2个数组。ArrayMap更加节省内存,大数据存储不适合使用还是使用HashMap

强引用、软引用、弱引用、虚引用的区别

  • 强引用:强引用是Java的默认类型,对象只要强引用存在,就不会被GC回收
  • 软引用:如果一个对象具有软引用的,在JVM发生OOM之前是不会被GC,只有到JVM内存不足时,才会被GC,通常软引用和引用队列(ReferenceQueue)配合使用。如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。
  • 如果一个对象只具有弱引用,那么这个对象就会被GC掉,被弱引用所引用的对象只能生存到下一次GC之前,当发生GC的时候无论当前内存是否足够,弱引用所引用的对象都会被GC掉
  • 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

Class类加载机制

Android中的类加载顺序和Java中的类加载有一定的区别,BootStrapClassLoader、ExtClassLoader、AppClassLoader分别对应BootClassLoader、PathClassLoader、DexClassLoader

Java反射的原理

反射的原理就是ClassLoad类加载,通过类名加载相应的class文件到JVM虚拟机中

锁的升级:无锁-偏量锁-轻量锁-重量锁
锁的分类:读写锁与排它锁、乐观锁和悲观锁、公平锁和非公平锁、可重入锁与非可重入锁
https://blog.csdn.net/youngjre/article/details/123033111

死锁的原因,怎么避免死锁

产生死锁的4个条件

  • 互斥使用:即当资源被一个线程使用(占有)时,别的线程不能使用
  • 资源不可抢占:资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  • 请求和保持 :即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
  • 循环等待:即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路

死锁的预防

  • 加锁顺序:线程按照一定的顺序加锁
  • 加锁时限:线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁
  • 死锁检测

String、StringBuffer、StringBuild的区别

  • String不可变、StringBuffer、StringBuild可变
  • StringBuild 非线程安全,执行效率高
  • StringBuffer 线程安全,执行效率低

静态内部类和内部类的区别

  • 静态内部类相对与外部类是独立存在的,在静态内部类中无法直接访问外部类中的变量、方法。如果要访问的话,必须要new一个外部类的对象,使用new出来的对象来访问。 但是可以直接访问静态变量,调用静态的方法
  • 普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法
  • 如果外部类要访问内部类的属性或者调用内部类的方法,必须要创建一个内部类的对象,使用该对象访问属性或者调用方法。
  • 如果其他的类要访问普通内部类的属性或者调用普通内部类的方法,必须要在外部类中创建一个内部类的对象作为一个属性,外部类可以通过属性调用普通内部类的方法或者访问普通内部类的属性
  • 如果其他的类要访问静态内部类的属性或者调用静态内部类的方法,直接创建一个静态内部类对象即可。

GC

GC原理
触发GC的时候会继续一次暂停操作,然后进行一次可达性分析,如果对象不可达会进行一次标记,在第二次标记的时候就会被回收

垃圾回收算法

  • 标记-清除算法


  • 标记-复制算法


  • 标记-压缩算法


Java内存模型

方法区、堆区、Java虚拟栈、Native虚拟栈、程序计数器

Java程序初始化顺序

  • 静态对象(变量)优先于非静态变量对象初始化,其中,静态对象(变量)只初始化一次,非静态对象可能多次初始化
  • 父类优先于子类初始化
  • 按照成员变量定义顺序进行初始化。即使变量定义散布于方法定义中,他们依然在任意方法被调用前进行初始化

Synchronized(对象锁)和Static Synchronized(类锁)的区别

  • synchronized是对类的当前实例加锁
  • static synchronized是某个类的范围,防止多个线程同时访问这个类的静态方法

synchronized与volatile的区别

  • synchronized和volatile都可以保证内存可见性。线程解锁前,必须把贡献变量的最新值刷新到主内存中。线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值
  • 从volatile的内存语义上来看,volatile可以保证内存可见性且禁止重排序通过内存屏障来实现。
  • 在保证内存可见性这一点上,volatile有着与锁相同的内存语义,所以可以作为一个“轻量级”的锁来使用。但由于volatile仅仅保证对单个volatile变量的读/写具有原子 性,而锁可以保证整个临界区代码的执行具有原子性。所以在功能上,锁比volatile更强大;在性能上,volatile更有优势

线程的状态以及转换

线程池


常用的线程池

  • newCachedThreadPool:核心线程数为0,非核心线程超时时间60s.适用于任务较多并且耗时较短的任务
  • newFixedThreadPool:核心线程数与最大线程数一致,所以只会创建核心线程。适合对个长时间的任务,占用资源大,线程池不会回收
  • newSingleThreadExecutor:与newFixedThreadPool一致,只不过核心线程数为1。适用于先进先出的任务队列。
  • newScheduledThreadPool:创建一个定长的线程池,支持定时以及周期性任务执行

三、Kotlin知识点

Kotlin比Java的优势

  • Kotlin基本杜绝了空指针
  • Kotlin语法更加简洁
  • Kotlin支持扩展函数
  • Kotlin与Java是100%兼容的,最后都会编译为class
  • Kotlin借助协程可以轻易的实现面向过程编程
  • Kotlin支持对象的解构

Kotlin扩展函数的原理

Kotlin扩展函数就是一个静态扩展类的一个静态方法

Kotlin协程和Java线程有什么区别

  • Kotlin协程其实就是运行在线程上的一个代码片段
  • Kotlin协程支持挂起和恢复,并且挂起时不会阻塞线程
  • Kotlin协程支持线程切换,配合挂起函数有效的避免了回调地狱等问题

Kotlin lazy和lateinit变量的区别

  • lazy 只能用在val类型 修饰不可变的常量,lateinit 只能用在var类型 修饰变量
  • lateinit不能用在可空的属性上

Kotlin Any与Java中Object的区别

  • 在Java中,Object是所有引用类的根,而基本类型int、float、boolean不是类层级结构的一部分,所以当你使用Object类型时,你必须使用Java.lang.Integer这样的包装类来表示基本数据类型的值。
  • 在Kotlin中,Any是所有非空类型的根,包含像Int这样的基础类型。当直接赋值时,会进行自动装箱操作

Kotlin中的构造函数

  • 主构造函数
    Kotlin中任意Class都会有一个默认的构造函数,可以显示的使用constructor声明
class Student constructor(name:String){}
  • 次构造函数
    Kotlin中任意Class中都可以使用constructor去声明一个次级构造函数
class ConstructorDemo(c: String) {//主构造
    constructor(a: String, b: Int) : this(a + b) {
        println("次构造,${a + b}")
    }
}

Kotlin特殊的注解有什么用

  • @JvmStatic:这个注解用来告诉编译器该方法是静态方法,可以在Java代码中使用。
  • @JvmOverloads:要在 Java 代码中使用 Kotlin 代码中作为参数传递的默认值,我们需要使用@JvmOverloads注解。
  • @JvmField:要在不使用任何 getter 和 setter 的情况下从 Java 代码访问 Kotlin 类的字段,我们需要@JvmField在 Kotlin 代码中使用。

说一说Kotlin的伴生对象原理

Kotlin中的伴生对象,其实就是Java中的静态内部类

Kotlin中inline关键字的作用

  • 如果一个方法被inline修饰,那么这个方法就是内联函数
  • 内联函数可以减少调用栈,但会增加编译时间。对于普通函数不建议使用,只有函数作文入参的时候使用是一种比较场景的使用场景
  • 内联函数减少调用栈是一方面,更重要的是可以减少对象的创建

Kotlin中with、run、apply、let函数的区别?一般用于什么场景?

  • let: 作用域使用it代表对象访问其属性,通常结合?使用,对象非空才执行作用域。返回值为最后一行
object?.let{//表示object不为null的条件下,才会去执行let函数体
   it.todo()
}
  • with:传入对象,可以直接引用对象的公有方法或者公有属性,返回值为函数最后一行
// 此处要调用people的name 和 age属性
// kotlin
val people = People("carson", 25)
with(people) {
println("my name is $name, I am $age years old")
}

// Java
User peole = new People("carson", 25);
String var1 = "my name is " + peole.name + ", I am " + peole.age + " years old";
System.out.println(var1);
  • run:let和with的结合作用域,返回值为最后一行
// 此处要调用people的name 和 age属性,且要判空
// kotlin
val people = People("carson", 25)
people?.run{
    println("my name is $name, I am $age years old")
}

// Java
User peole = new People("carson", 25);
String var1 = "my name is " + peole.name + ", I am " + peole.age + " years old";
System.out.println(var1);
  • apply:与run函数类型,区别在于返回值,返回值为传入对象本身
// run函数
val people = People("carson", 25)
val result = people?.run{
    println("my name is $name, I am $age years old")
    999
}
// 最终结果 = 返回999给变量result

// Java
val people = People("carson", 25)
val result = people?.run{
    println("my name is $name, I am $age years old")
    999
}
// 最终结果 = 返回一个people对象给变量result

使用场景:

  • 用于初始化对象或更改对象属性,可使用apply
  • 如果将对象进行空检查并访问或修改其属性,可使用let
  • 如果想要计算某个值,或者限制多个本地变量的范围,则使用run

四、Android知识点

Android应用安全

  • 客户端程序安全:安装包签名、应用完整性校验、反编译保护、调试安全、任意备份风险、屏幕截屏安全漏洞
  • 数据安全:SharePrefence不安全使用MMKV代替、数据库安全
  • 通讯安全:使用Https协议、使用https证书并进行证书校验、请求入参使用算法加密,核心算法写在so文件里(常用加密算法:AES、DES、RSA)
  • 组件安全:四大组件启动时检查权限是否匹配、四大组件android:exported设置为false

Handle机制

  • Handle原理:Looper、MessageQueue
  • 怎么利用Handle从主线程中发消息到子线程:使用Looper循序
  • Handle sendMessage和post有什么区别:都一样,底层调用的都是一个函数,只是sendMessage没有延迟,post有延迟

View的事件分发机制

View的绘制原理

  • onMeasure()、onLayout()、onDraw()
  • PhoneWindow、DocerView、ViewRoot、WindowManager




    常见问题
  • 1、为什么在Activity生命周期onResume()方法里获取不到视图的尺寸?
    因为在onResume方法里View还没有与ViewRootImp进行绑定,还没有进行计算、布局、绘制等操作
  • 2、 刷新视图方法invalidate()和requestLayout()有什么区别?
    invalidate()只会引起重绘,requestLayout()会重新计算、布局、绘制
  • 3、子线程为什么不能更新UI?
    因为ViewRootImpl绘制的时候会检测当前线程是否是主线程不是则抛出异常

网络知识

TCP与UDP的区别、TCP的三次握手与4次挥手、http与https的区别、http1.0与1.1、1.2的区别
https://www.jianshu.com/p/a0996dff4041

Okhttp知识点


Okhttp在IO上的优化

Okhttp底层IO操作都是使用自家的OKio库来进行优化的。它补充了java.io和java.nio的不足,使访问、存储和处理数据更加容易

OKio的原理
https://www.jianshu.com/p/f5941bcf3a2d

MMKV

MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今,在 iOS 微信上使用已有近 3 年,其性能和稳定性经过了时间的验证。近期也已移植到 Android 平台,一并开源。


原理

  • 内存准备
    通过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操作系统负责将内存回写到文件,不必担心 crash 导致数据丢失。
  • 数据组织
    数据序列化方面我们选用 protobuf 协议,pb 在性能和空间占用上都有不错的表现
  • 写入优化
    考虑到主要使用场景是频繁地进行写入更新,我们需要有增量更新的能力。我们考虑将增量 kv 对象序列化后,append 到内存末尾。
  • 空间增长
    使用 append 实现增量更新带来了一个新的问题,就是不断 append 的话,文件大小会增长得不可控。我们需要在性能和空间上做个折中。

更多原理细节:https://github.com/Tencent/MMKV/wiki/android_ipc

ANR

常见问题
1、什么情况下会导致应用ANR?

  • 主线程中做了耗时操作会导致ANR,前台超时5s、广播超时10s、服务超时20s。
  • 如果其他线程和主线程抢夺同一个资源时造成了死锁也会造成应用ANR。
  • 网络访问数据量大、IO流过大
  • 数据库操作频繁

2、ANR怎么排查定位?

  • 本地发生ANR,通过ANR日志排查,一般发生了ANR都会在/data/anr/traces.txt目录下生成ANR日志。然后分析ANR日志找到产生ANR的原因。
  • 线上的ANR通过三方工具排查,例如Sentry、Bugly

内存优化

https://www.jianshu.com/p/82a31de0eca4
常见问题
1、内存优化从哪几个方面入手?

  • 使用Android Studio自带工具Profile查看内存,有没有频繁GC,一般在短时间内创建了大量对象会导致频繁GC
  • 使用Profile工具查看有没有内存泄漏,如果内存一直往上涨则证明有内存泄漏,一般导致内存的泄漏的原因有:Handle内存泄漏、单例内存泄漏、非静态内部匿名类内存泄漏、注册/反注册内存泄漏、资源对象没有关闭导致的内存泄漏

2、优化内存常用的工具有哪些?
LeakCanary、MAT、Android Profile

Handle内存泄漏的解决方案

  • 静态内部类+弱应用
   private static class MyHandler extends Handler {
        //弱引用,在垃圾回收时,activity可被回收
        private WeakReference mWeakReference;

        public MyHandler(MainActivity activity) {
            this.mWeakReference = new WeakReference<>(activity);
        }

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    }
  • 在Activity的onDestory里对Handle管道和队列进行清空处理
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //清空handler管道和队列
        mHandler.removeCallbacksAndMessages(null);
    }

LeakCanary原理

https://blog.csdn.net/sk719887916/article/details/73571846

什么是ReferenceQueue?
我们常用一个 WeakReference reference = new WeakReference(activity);,这里我们创建了一个 reference 来弱引用到某个 activity,当这个Activity 被垃圾回收器回收后,这个 reference 会被放入内部的 ReferenceQueue 中。也就是说,从队列 ReferenceQueue取出来的所有 reference,它们指向的真实对象都已经成功被回收了。

是如何判断Activity泄漏的?
在一个 activity 传给 RefWatcher 时会创建一个唯一的 key 对应这个 activity,该 key 存入一个集合 retainedKeys 中。也就是说,所有我们想要观测的 activity 对应的唯一 key 都会被放入 retainedKeys 集合中。
基于我们对 ReferenceQueue 的了解,只要把队列中所有的 reference 取出来,并把对应 retainedKeys 里的 key 移除,剩下的 key 对应的对象都没有被回收

LeakCanary是怎么同步触发GC的?
在 LeakCanary 里,需要立即触发 gc,并在之后立即判断弱引用是否被回收。这意味着该 gc 必须能够立即同步执行。

常用System.gc()只是建议垃圾回收器来执行回收,但是不能保证真的去回收。从代码也能看出,必须先判断 shouldRunGC 才能决定是否真的要 gc。

LeakCanary参照了AOSP的源码,在触发了gc之后暂停100ms,再去执行System.runFinalization()这样就能保证gc立即执行,System.runFinalization()会去执行被挂起对象的finalize方法执行内存回收操作。

interface GcTrigger {

  /**
   * Attempts to run garbage collection.
   */
  fun runGc()

  /**
   * Default implementation of [GcTrigger].
   */
  object Default : GcTrigger {
    override fun runGc() {
      // Code taken from AOSP FinalizationTest:
      // https://android.googlesource.com/platform/libcore/+/master/support/src/test/java/libcore/
      // java/lang/ref/FinalizationTester.java
      // System.gc() does not garbage collect every time. Runtime.gc() is
      // more likely to perform a gc.
      Runtime.getRuntime()
          .gc()
      enqueueReferences()
      System.runFinalization()
    }

    private fun enqueueReferences() {
      // Hack. We don't have a programmatic way to wait for the reference queue daemon to move
      // references to the appropriate queues.
      try {
        Thread.sleep(100)
      } catch (e: InterruptedException) {
        throw AssertionError()
      }
    }
  }
}

卡顿优化

常见问题
1、导致UI卡顿的原因有哪些?

  • Layout布局过于复杂
  • 执行动画过多,导致GPU和CPU负载严重
  • View的过度绘制
  • 频繁的内存GC
  • 在UI线程中做了耗时操作

2、应用启动过慢怎么办?

  • 在应用启动的时候,把一些耗时操作放入异步任务或者子线程中处理
  • 如果有一些模块不是必须的,不要在应用刚启动的时候就去加载模块或者SDK,移动到使用的地方再进行操作

3、Android中怎么进行帧率检测
开发者工具里自带GPU呈现模式分析,可以显示实时帧率



Android中的布局优化

https://www.jianshu.com/p/4e665e96b590

四大组件

  • Activity、Service、ContentProvider、BroadcastReceive
  • Activity、Fragement的生命周期



  • 四大的组件的启动过程:https://www.jianshu.com/p/c84af9fa26af

Service的2种启动方式

AsyncTask的原理

AsyncTask底层就是2个线程池+Handle,默认的阻塞队列为LinkedBolckingQueue

Android应用架构设计

  • MVC模式
  • MVP模式
  • MVVM模式

Android版本特性

https://www.jianshu.com/p/88409d6f5795

Android中APT的使用以及原理

https://www.jianshu.com/p/e1002d012863

Android中进程间通讯有哪几种方式?

Binder、ContentProvider、Socket、消息队列、管道

阐述一下Binder的原理,以及为什么要用Binder好处是什么?

原理
BInder的原理就是通过内核空间共享数据,底层通过mmap()函数进行内存映射。实现进程间的通讯。


优势

  • 从性能角度上来说,Binder的性能更占优势,因为数据拷贝只需要一次。而管道、消息队列、Socket都需要2次
  • 从稳定性角度,Binder采用C/S架构,使得其稳定性更好
  • 从安全性角度来说。Binder访问都需要有权限控制,需要用户授权方可访问。
  • 从封装易用性角度来说,Binder上层使用了AIDL,使得开发人员无需关注底层实现

AIDL

AIDL是Android封装的Binder机制的上层表现,用于进程间通讯。它的使用步奏如下:

  • 服务端应用定义抛出的接口.aidl文件,如果需要实体则定义实体aidl文件
  • 服务实现Service,定义完接口aidl文件之后Rebuild工程之后会生成一个接口同名Stub子类,在Service里的onBind方法中返回抛出给客户端。
  • 服务端在AndroidMainfast.xml文件中注册服务
  • 客户端拷贝服务端所有的AIDL文件,Rebuild工程,在需要使用的地方创建Service服务,使用bindService方法绑定服务端Service,实现通讯。

Android系统的启动过程

https://www.jianshu.com/p/ba4323ee2c4c

AMS

https://www.jianshu.com/p/7305a8a89374


常见问题
1、AMS是如果管理Activity堆栈的?
http://events.jianshu.io/p/6993c042a14b

WMS

WMS(WindowManagerService)是一个重要的系统服务,用于窗口管理,由SystemService启动,发生异常时自动重启,直到系统关机时才能退出。
在WMS没有运行之前,开机动画由BootAnimation直接通过OpenGL ES与SurfaceFlinger的配合来完成。



在《深入理解Android内核设计思想》一书中看到一个比喻非常好,整个界面就像由N个演员参与的话剧:SurfaceFling是摄像机,它只负责客观的捕捉当前的画面,然后真实的呈现给观众;WMS就是导演,它要负责话剧的舞台效果、演员站位;ViewRoot就是各个演员的长相和表情,取决于它们各自的条件与努力。可见,WMS与SurfaceFling的一个重要区别就是——后者只做与“显示”相关的事情,而WMS要处理对输入事件的派发

IMS

IMS是Android系统中事件处理的中枢神经系统,它的主要工作是读取设备节点中的原始事件,将其加工封装,然后派发给一个指定的窗口以及窗口中的控件。


内核将原始事件写入设备节点中,InputReader不断地通过EventHub将原始事件取出来并翻译加工成Android输入事件,然后交给InputDispatcher。InputDispatcher根据WMS提供的窗口信息将事件交给合适的窗口。窗口的ViewRootImpl对象再沿着控件树将事件派发给感兴趣的控件。控件对其收到的事件做出响应,更新自己的画面、执行特定的动作。所有这些参与者以IMS为核心,构建Android输入体系。

PMS

https://blog.csdn.net/Alexwll/article/details/102777742

Glide的原理

https://juejin.cn/post/6882536990400020494

LRUCache的原理

https://blog.csdn.net/qq_39431405/article/details/121424482

LruCache的核心思想很好理解,就是要维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰,有点像职场里末尾淘汰。

JetPack

Jetpack是一套库、工具和指南,可以帮助开发者更轻松地编写优质应用。这些组件可以帮助开发者遵循最佳做法、
让开发者摆脱编写样板代码的工作并简化复杂任务,以便开发者将精力集中放在所需的代码上。


常见问题
1、Recycleview和Listview的优劣势?

  • RecycleView自带ViewHodler,可以通过设置不同的LayoutManager切换不同的布局
  • RecycleView可以轻易的实现横向显示横向滚动
  • 可以定向刷新某一条数据

2、RecycleView的原理
https://zhuanlan.zhihu.com/p/165939600

3、使用ViewModel和LiveData快速实现MVVM架构


LiveData
从官网的介绍可以看到, LiveData 作用跟RxJava类似,是观察数据的类,相比RxJava,它能够在Activity、
Fragment和Service之中正确的处理生命周期。那么 LiveData 有什么优点呢?

  • 数据变更的时候自动更新UI
  • 没有内存泄漏
  • 不会因为停止Activity奔溃
  • 无需手动处理生命周期
  • 共享资源

ViewModel
众所周知, MVVM 层中 ViewModel层 用来作逻辑处理的,那么我们 Android Jetpack 组件中 ViewModel 的作用是否也一致呢?

ViewModel 同样具有生命周期意识的处理跟UI相关的数据,并且,当设备的一些配置信息改变(例如屏幕旋转)它的数据不会消失。

通常情况下,如果我们不做特殊处理,当屏幕旋转的时候,数据会消失,那 ViewModel 管理的数据为什么不会消失? 是因为 ViewModel 的生命周期,就算屏幕旋转,其生命周期并没有发生改变

ViewModel 的另一个特点就是同一个 Activity 的 Fragment 之间可以使用ViewModel实现共享数据。

MVVM代码样例

class HomePageViewMode : ViewModel() {
    private val recommendProductService = RecommendProductServiceImpl()

    val recommendList: MutableLiveData> by lazy {
        MutableLiveData>()
    }

    fun fetchRecommendList(pageSize: Int = 10, pageNo: Int = 1, searchId: String = "") {
        //开启协程请求数据,然后直接塞值到LiveData
        viewModelScope.launch {
            withContext(Dispatchers.IO) {
                //加载中
                recommendList.postValue(Resource.loading(null))
                //获取数据
               recommendList.postValue(recommendProductService.getProductList(pageSize, pageNo, searchId))
            }
        }
    }
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.frag_home_page, null)
        //注册ViewModel  
        model = ViewModelProvider(this)[HomePageViewMode::class.java]
        //监听值变化
        observeRecommendList()
        ..
}

private fun observeRecommendList() {
        model.recommendList.observe(viewLifecycleOwner, Observer> {
            when (it.status) {
                Status.SUCCESS -> {
                    val recommendProductList = it.data
                    val productList = it.data?.resultData ?: listOf()

                    productTotalCount = recommendProductList?.totalCount ?: 0
                    productCurrentPage += 1
                    //更新UI等一些操作
                    ....
}
//某个地方发起请求
model.fetchRecommendList(pageSize = PAGE_SIZE, pageNo = productCurrentPage + 1, searchId = mSearchId)

LifeCycle

LifeCycle 是一个可以感知宿主生命周期变化的组件。常见的宿主包括 Activity/Fragment、Service 和 Application。LifeCycle 会持有宿主的生命周期状态的信息,当宿主生命周期发生变化时,会通知监听宿主的观察者。

LifeCycle 的出现主要是为了解决: 系统组件的生命周期与普通组件之间的耦合性。

  • 系统组件指:Activity/Fragment、Service 和 Application。
  • 普通组件指:将代码按照功能或者作用封装成的组件。


你可能感兴趣的:(Android面试题总结)