Android开发工程师面试题大全
- java中 == 和equals的区别
- == 是运算符,用来比较值是否相同。当基本数据类型做比较时,比较的是数据值是否相同;当引用类型做比较时,比较的是引用类型所关联的对象在内存中的地址是否相同。
- equals方法是基类Object中的方法,不能用于基本数据类型。本来是用来比较引用的地址是否相同,主要是判断hashcode是否相等,但在诸如String、Double等类中都进行了重写equals方法和hashcode方法,更改为比较指向的对象所存储的内容是否相等。
- JVM
- 程序计数器:线程私有,用来选取下一条需要执行指令的字节码指令
- java栈:线程私有,用于存储局部变量、方法
- 本地方法栈:线程私有,存储native方法
- java堆:线程共享,用于保存对象和数组
- 方法区:线程共享,保存类信息,静态变量
- GC的基本原理
- 程序中无引用对象长期占有内存将会导致内存不足,因此java引入了垃圾回收机制
- 标记存活对象:早期使用的是引用计数法,之后引入了可达性分析算法
- 清楚死亡对象:目前最主要使用分代收集算法(G1收集器),根据存活的周期将内存分为多块,一般是新生代、老年代
- int和Integer的区别
- Integer是int的包装类,int是java的一种基本数据类型。
- Integer必须实例化后使用,int不需要实例化。
- Integer是数据对象的引用,int是直接存储数据值。
- Integer的默认值是null,int的默认值是0。
- 对多态的理解
-
运行时多态(动态绑定):父类引用指向子类对象,在执行期间判断所引用对象的实际类型,根据实际类型调用其相应的方法,通过重写实现。三个要素:继承、重写、父类引用指向子类对象。
-
编译时多态:通过重载实现。
-
作用:消除类型之间的耦合关系。
- String、StringBuffer、StringBuilder区别
- String的值是不可变,每次对String的操作都会生成新的String对象,不仅效率低,还耗费内存空间。
- StringBuilder可变,没加同步锁,因此线程不安全,但速度快。
- StringBuffer可变,加了同步锁,因此线程安全,但速度较慢。
- final、finally、finalize的区别
-
final:修饰类、成员变量和成员方法,使类不可继承,成员变量不可变,成员方法不可重写。
-
finally:在try…catch…的时候使用,确保代码被执行。
-
finalize:类的方法,垃圾回收前会调用这个方法,子类可以重写这个方法实现对资源的回收控制。
- Serializable和Parcelable的区别和使用
-
Serializable:java中的序列化技术,只需要实现Serializable接口即可。实现简单,但无法序列化静态变量,并且读写时有大量临时变量生成,效率低。当父类实现序列化后,子类就自动实现了序列化。
-
Parcelable:Android中的序列化技术,主要是为了解决Serializable消耗资源严重的问题,但是本身使用比较复杂,需要手动处理序列化和反序列化过程。
-
在使用内存的时候,一般使用Parcelable,效率更高。但需要长时间保存在磁盘上的时候,还是要用Serializable。Android上大多数情况下都用Parcelable。
- HashMap实现原理
- HashMap底层使用的是数组加链表的数据结构实现的。
- 存储时,根据hash值得到元素在数组中的位置,如果该位置上没有元素,就直接存放。如果该位置上已经存在其他元素,就比较key的值是否相同,相同则覆盖,不同则存放在链表上。
- 获取时,直接根据hash值找到元素在数组中的下标,然后判断key的值是否相同,不同则继续在链表上面找,直到找到和key值相同的值。
- jdk1.8后进一步优化,当链表长度大于8时,把链表转换成红黑树进行存储。
- HashMap和HashTable的区别
- HashMap允许key和value为null,HashTable不允许。
- HashMap线程不安全,HashTable线程安全,所以HashMap存取速度更快。
- 线程run和start的区别
- start方法真正启动了多线程,此时线程处于就绪状态并且在等待cpu,一旦得到cpu时间片,就开始执行run方法
- run方法只是一个普通的方法,如果直接使用run方法,那只是在主线程执行这个方法而已,并不是多线程执行。
- 线程sleep和wait的区别
-
sleep是Thread类中的方法,wait是Object类中的方法。
-
sleep方法会使当前线程进入阻塞状态指定时长,当指定时长结束后,会重新进入就绪状态,等待时间片划分。sleep方法被调用后,线程休眠,但是对象的锁没有被释放,其他线程依然不能访问这个对象。
-
wait方法一般是和notify方法连用,调用wait方法后,对象的锁被释放,然后进入等待获取该对象的锁的状态,当对象调用了notify方法后,线程才能准备获取对象锁然后进入运行状态。
- 线程如何关闭
- 设置一个停止线程的标记。
- 调用线程的stop方法,但一般不推荐,因为是线程不安全的。
- 使用interrupt进行中断。
- java中同步的方式
- 使用synchronize关键字。
- 使用volatile关键字。
- 使用可重入锁。
- ThreadLocal方式
- 死锁的必要条件
- 互斥条件。
- 请求和保持条件。
- 不可剥夺条件。
- 循环等待条件。
- 什么是线程池
- 一种管理线程的方式,可以让我们更方便地管理线程,也可以减少内存的消耗。
- 其中有核心线程,非核心线程,最大线程数量,非核心线程存活时间,任务队列,线程工厂,handler拒绝策略等部分。
- 执行过程:任务开始时,首先判断核心线程是否空闲,是则执行,否则判断任务队列是否有地方存放该任务,是则存放并等待执行,否则判断最大线程数量,如果没有超过最大数量,则使用非核心线程来执行任务,如果超出了最大数量,则使用handler的拒绝策略。
- 三次握手和四次挥手
-
三次握手:
- 第一次:客户端发送请求到服务器,服务器收到客户端的请求,知道自己接收正常。
- 第二次:服务器返回信息客户端,客户端收到后知道自己发送和接收都正常,同时也知道服务器发送和接收正常。
- 第三次:客户端再次发送信息给服务器,服务器收到信息后知道客户端发送接收都正常,自己发送接收也正常。
-
四次挥手:
- 第一次:客户端发送信息请求断开连接。
- 第二次:服务器确认客户端的断开请求。
- 第三次:服务器发送信息请求断开连接。
- 第四次:客户端确认服务器的断开请求。
- TCP和UDP区别
- TCP面向连接,是可靠的。UDP面向无连接,是不可靠的。
- TCP一对一,UDP可以一对一,也可以多对一或者一对多。
- TCP速度比UDP慢。
- 横竖屏切换时activity的生命周期
- Android 3.2 以前
- 不设置android:configChanges时,切屏会重新调用各个生命周期,切横屏会执行一次,竖屏执行两次
- 设置android:configChanges="orientation"时,切屏会重新调用各个生命周期,但切横竖屏都只执行一次
- 设置android:configChanges="orientation|keyboardHidden"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
- Android 3.2 以后
- 不设置android:configChanges,或者设置android:configChanges=“orientation”,或设置android:configChanges=“orientation|keyboardHidden”,切屏会重新调用各个生命周期,切横竖屏都只执行一次
- 设置android:configChanges="orientation|keyboardHidden|screenSize"时,不会销毁activity,只会调用onConfigurationChanged方法
- onCreate()和onRestoreInstanceState()恢复数据的区别
- 因为onSaveInstanceState只有在某些情况下才会调用,所以onCreate()里的Bundle参数可能是空的,因此如果使用onCreate()恢复数据,一定要进行判空处理,但onRestoreInstance则不需要进行判空,因为只有在上次activity被回收时它才会被调用,那么上次onSaveInstanceState肯定已经执行,Bundle不会为空。
- onRestoreInstanceState()是在onStart()之后被调用的。如果我们需要在onCreate()中做初始化之后再恢复数据,用onRestoreInstanceState()就不错,但如果想在初始化的同时恢复数据,就直接在onCreate()恢复数据即可
- Activity与Fragment生命周期对比
- activity四种启动模式
- 默认启动模式standard:activity按启动顺序压入栈中,无论栈中是否已经存在该activity。适用于大部分情况
- 栈顶复用模式singleTop:如果栈顶恰好为新建的activity,则不会重新创建,而是直接使用栈顶activity。适用于多应用开启调用的activity。
- 栈内复用模式singleTask:如果栈内已经存在该activity,则栈内此activity之上的所有activity出栈,将目标activity置于栈顶。适用于程序的主界面。
- 全局唯一模式singleInstance:创建一个新的栈,并且将该activity入栈,新的栈中只存在这一个activity。适用于全局唯一的情况
- activity与service绑定
- 创建service类和activity类
- 在service类中定义一个内部类继承自Binder类
- 实例化这个定义的Binder类
- 实现onBind()方法,返回binder
- 在activity中定义一个ServiceConnection内部类
- 最后在activity里调用bindService()完成绑定即可
- service的两种启动方式和对应的生命周期
- 通过startService()方式启动
- 生命周期:startService()→onCreate()→onStartCommon()→onDestory()
- 注意问题:onCreate()只会在第一次调用的时候执行,之后再次调用会再次执行onStartCommon()
- 通过bindService()方式启动
- 生命周期:bindService()→onCreate()→onBind()→unBind()→onDestroy()
- 注意问题:需要在activity中创建ServiceConnection对象,然后通过ServiceConnection来获取service中的Binder子类对象,然后就可以调用相应的方法
- context的理解
- context描述的是一个应用程序环境的信息,即上下文。
- 通过context可以获取应用程序的资源和类,也包括一些应用级别操作
- 创建Application、Activity和Service时都会创建一个context对象
- context的总数量为activity数量加上service数量再加一
- View、SurfaceView和GLSurfaceView区别
- View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等,必须在UI线程内更新画面,速度较慢
- SurfaceView:基于view视图进行拓展的视图类,更适合2d游戏的开发,是view的子类,类似使用双缓冲机制,在新的线程中更新画面,所以刷新界面速度比view快,但需要注意同步的问题
- GLSurfaceView:基于SurfaceView视图再次进行拓展的视图类,专用于3d游戏开发的视图,是SurfaceView的子类,openGL专用。
- Android两种序列化方式
- Serializable:java提供的序列化接口,只需要创建一个serialVersionUID即可,serialVersionUID唯一标识这个可序列化的类。如果不创建,则默认生成一个serialVersionUID。因为是用的反射机制,在序列化操作时产生大量的临时变量,导致频繁GC,因此性能较差。读写数据时是通过io流的形式将数据读写在硬盘上,做数据持久化时常有。
- Parcelable:android特有的序列化接口,需要实现四个特定的方法。通过Ibinder为信息载体,在内存上开支较小,性能更强。读写数据时在内存上直接进行,但Parcelable无法很好的进行数据持久化保存,因此多用于短时间的数据传输
- Android中数据存储方式
- SharedPreference储存:保存基于xml文件存储的key-value键值对数据,通常用于存储一些简单的配置信息
- 文件存储:使用io流读写文件
- SQLite数据库存储:小巧并且方便管理
- 网络存储:把数据保存到服务器或者别的介质上
- ContentProvider:可以在不同的应用程序之间共享数据
- invalidate和postInvalidate的区别
- invalidate()方法需要在UI线程里调用,通常需要搭配handle一起使用
- postInvalidate()可以在非UI线程里调用
- AMS、WMS的作用
- AMS:统一调度所有应用程序的activity,具体作用:
- 统一调度所有应用程序的activity的生命周期
- 启动或杀死应用程序的进程
- 启动并调度Service的生命周期
- 注册BroadcastReceiver,并接收和分发Broadcast
- 启动并发布ContentProvider
- 调度task
- 处理应用程序的Crash
- 查询系统当前运行状态
- WMS:管理整个系统所有窗口的UI,具体作用:
- 为所有窗口分配Surface
- 管理Surface的显示顺序、尺寸、位置
- 管理窗口动画
- 派发系统按键和触摸消息给最适合的窗口
- ContentProvider如何实现数据共享
- 以类似数据库中表的方式将数据暴露
- 以某种Uri的形式对外提供数据
- 其他应用通过ContentResolver接口访问共享的数据
- IntentService的原理以及作用
- IntentService是继承于Service并且用来处理异步请求的一个类,在IntentService内有一个工作线程用来处理耗时操作
- IntentService启动方式和传统Service一样,但是在任务执行完毕后,会自动停止
- 多次启动IntentService,每一个耗时操作都会不以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且每次只执行一个工作线程,执行完第一个再执行第二个
- 所有请求都在一个单线程中,并且不会阻塞主线程,同一时间只处理一个请求
- ThreadLocal原理
- 用ThreadLocal修饰的对象在不同的线程中具有不同的数据副本,因此虽然不同的线程访问的是同一个对象,但访问到的值却不一样
- ThreadLocal的set和get方法,所操作的对象都是当前线程的localValues对象的table数组,因此在不同线程访问同一个ThreadLocal的set和get方法,对ThreadLocal所做的读写操作仅限于各自线程的内部,就可以做到多个线程互不干扰地存储和修改数据
- Handler机制和底层实现
- ActivityThread:程序的启动入口,也就是主线程,对Looper进行操作
- Handler:sendMessage到MessageQueue和handleMessage操作控件更新,持有Looper和MessageQueue对象
- Looper:消息轮询器,轮询消息队列的消息并取出,交给handler处理
- MessageQueue:存放消息的队列,对Message进行插入和取出操作,取出的时候执行next方法返回消息
- Message:封装的消息体,包括一些字段:
- what:用户定义的识别码,用来分辨这个消息是做什么的
- obj:传输任意对象
- arg1、arg2:传递一些简单的数据类型
- 一个线程可以有多个handler,但只有一个Looper和MessageQueue
- 进入消息队列前,Message通过target与handler绑定起来,到时候取出消息就可以分发给绑定的handler进行处理
- 创建过程:
- ActivityThread执行looperMainPrepare(),该方法实例化MessageQueue对象,然后实例化Looper对象
- 执行loop()方法,死循环读取MessageQueue
- 调用sendMessage()方法,往MessageQueue中添加消息
- 执行next()方法取出消息,执行dispatchMessage()方法分发消息给该消息绑定的handler
- 执行handleMessage()进行控件更新
- 为什么主线程的Looper.loop()一直无限循环不会造成ANR
- 无限循环是为了从消息队列中读取消息,当读完所有消息时,主线程会阻塞,此时主线程会释放cpu资源进入休眠状态。
- 当下一个消息到达或者有事务发生,就会唤醒主线程工作
- 主要使用的是linux的pipe管道机制~
- View的事件传递及分发机制
- View的绘制流程
- onMeasure():测量视图大小。从顶层父view向子view的递归调用measure()方法,measure()方法调用完又回到onMeasure()
- onLayout():确定view的位置,进行页面布局,从顶层父view到子view的递归调用view.layout()方法的过程。即父view根据上一个measure子view所得到的布局大小和布局参数,将子view放在合适的位置上
- onDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用onDraw()。六个步骤:
- 绘制视图背景
- 保存画布图层
- 绘制视图内容
- 绘制子视图(如果没有则不需要)
- 还原图层
- 绘制滚动条
- AsyncTask的使用
- AsyncTask是Android本身提供的一种轻量级的异步任务类,可以执行后台任务并把进度和最终结果传递给主线程更新ui。内部封装了Thread和Handler,同时AnyncTask并不适合进行特别耗时的后台操作,对于特别耗时的任务,建议使用线程池
- 主要方法:
- onPreExecute():该方法在主线程中执行,在异步任务执行之前被调用,一般用于一些准备工作
- doInBackground():这个方法是在线程池中执行,用于执行异步任务。可以通过publishPrigress()方法来更新任务进度,publishProgress()方法会调用onProgressUpdate()方法,另外,任务的结果返回给onPostExecute()方法
- onProgressUpdate():该方法在主线程中执行,主要用于更新任务进度
- onPostExecute():在主线程中执行,在异步任务完成之后,该方法会被调用,返回最终结果
- 取消AsyncTask:
- 调用cancel()方法,但不会立刻执行,只会在doInBackground()方法执行完后,不再调用onPostExecute()方法,而是执行onCancelled()。这种方式不能及时停止线程,有时候无法达到要求
- 在doInBackground()中设置标志位进行判断是否需要停止
- 适用于:
- 执行过程单一,输入输出都只有一次的任务
- 耗时较短同时需要更新UI的任务
- 执行线程必须是UI线程的任务
- 不需要长期维护状态的任务
- 为什么不能在子线程里更新UI
- Android的UI访问是没有加锁的,多个线程可以同时访问更新操作同一个UI控件。因此访问UI的时候并不是线程安全的,这将导致在多线程模式下,会发生不可控的错误,所以规定只能在主线程中更新UI,就相当于用别的方式给UI访问上了锁。目的在于提高移动端更新UI的效率和安全性
- ANR产生原因和解决方案
-
根本原因:
- 主线程执行了耗时操作,未及时处理完毕
- 其他进程占用CPU导致本进程得不到CPU时间片
-
常见类型:
- 主线程对输入事件5秒内未完成处理
- 广播的onReceive()方法里10秒内未处理完毕
- service里各个生命周期20秒内未处理完毕
-
常见场景:
- 耗时的网络访问
- 大量的数据读写
- 数据库操作
- 硬件操作(如 camera)
- 其他线程异常崩溃导致主线程一直等待
- system server中发生WatchDog ANR
-
解决方案:
- 避免在主线程中进行耗时操作,应该在子线程里完成耗时任务然后再回到主线程更新UI
- 如果在广播的onReceive()里进行耗时操作,应该启动一个service去处理
- ANR原因的定位过程
- 查看trace.txt文件,分析堆栈log信息,可以知道发生ANR的是哪个进程,并且可以得知造成ANR的直接原因,例如输入分配超时
- 查看cpu的使用情况,避免由于创建大量进程而导致cpu负载过重导致ANR
- 搜索dalvik thread关键字,查看虚拟机信息日志,分析main进程调用方法的过程,找到代码中造成ANR的地方
- 常有检测工具或方式
- Android Studio的CPU Profiler:分析cpu的占用率和方法耗时
- 开启严格模式:系统日志打印更多重要信息
- 使用BlockCanary:通过计算执行时间是否超过阀值而判断ANR
- 什么是OOM
- OOM就是内存溢出,由于Android系统每个进程或虚拟机有最大内存限制,一旦超过这个限制就会抛出OOM的错误,但跟手机剩余内存是否充足没有多大关系
- 内存泄漏原因和解决方案
-
根本原因:占用的内存没有及时被回收
-
常见类型和解决方案:
-
单例中持有activity的context:当activity结束后,context也得不到释放造成内存泄漏。可以把context改为application的context
-
非静态内部类造成的内存泄漏:当非静态内部类创建静态实例,则当外部类被销毁时,就会造成内存泄漏。可以把内部类改成静态内部类
-
Handler或Runnable作为非静态内部类:会持有外部类的引用,如果在延迟操作尚未发生时销毁外部类,则这个被持有的外部类对象无法被回收,造成内存泄漏。可以把Handler或Runnable改为静态内部类,如果同时Handler或者Runnable还持有context对象,则可以使用弱引用的方式来引用context避免内存泄漏
-
注册和反注册未成对出现造成内存泄漏:如广播注册后未进行反注册
-
资源对象使用后未关闭造成内存泄漏:如数据库游标使用后未关闭
-
-
常用检测工具或方式
- Android Studio的Memory Profiler:分析内存占用大小和当前引用的对象
- 使用LeakCanary:基于WeakReference和ReferenceQueue进行检测的。使用弱引用包装activity或fragment,并且将其与ReferenceQueue相关联,判断ReferenceQueue是否有当前观察的Activity或者Fragment的引用对象,第一次判断如果不存在,就会手动触发一次GC,然后再判断一次,如果还是未存在,则表明出现了内存泄漏
- Android Studio的Memory Profiler:分析内存占用大小和当前引用的对象
- Android系统架构
- linux核心层
- linux内核:android平台的基础,直接和硬件打交道。硬件驱动、进程管理、内存管理、网络管理等功能都在这里实现
- 硬件抽象层(HAL):提供标准接口,包含多个库模块,其中每个模块都为特定类型的硬件组件实现一组接口,当框架api请求访问设备硬件时,Android系统将为该硬件加载相应的库模块
- 系统库和运行环境层
- 系统库:原生C/C++库,提供给其它层调用
- 运行环境:Android5.0以前使用的是Dalvik虚拟机,之后被ART取代
- Framework层
- Android开发最常用的组件和服务都集中在这层,提供开发所需的一系列类库
- 应用层
- 直接与用户交互的一层
- 大量使用Bitmap防止OOM的方法
- 选择适合的图片规格:默认一般是ARGB_8888,每个像素占用4字节内存,可以改为选择ARGB_4444等其他占内存更低的规格
- 降低采样率
- 复用内存:通过软引用复用内存块,不需要再重新给这个bitmap申请新内存,减少内存的分配和回收,改善运行效率
- 及时回收:使用完毕及时回收bitmap内存
- 压缩图片
- 热修复原理
- 代码热修复
- 类加载方案:把需要修复的类打包成一个dex文件,在app启动时通过反射,将这个dex文件放在BaseDexClassLoader的dexPathList中的dexElements集合的最前面,由于双亲委任模式,类加载的时候这个dex文件会代替后面存在bug的同名dex文件被使用,因此就修复了bug
- 底层替换方案:通过修改一个方法的入口地址让它指向新的方法从而达到修复的目的
- 资源热修复
- InstantRun
- so库热修复
- 本质是对native方法的修复和替换,和类加载方案类似,可以把补丁so库的路径插入到nativeLibraryDirectories数组的最前面,使得加载的时候优先加载补丁so库达到修复目的
- 插件化原理
- 插件化三个核心问题:类加载、资源加载和组件生命周期管理
- 类加载:利用DexClassLoader来加载未安装的apk的dex
- 资源加载:Android系统通过Resource对象加载资源,因此只需要添加apk所在的路径到AssetManager中,即可实现对插件资源的访问。但由于AssetManager的构造方法是hide的,需要通过反射创建
- 组件生命周期管理:通常做法是通过hook相应的系统对象,实现欺上瞒下
- 组件化原理
- 对软件系统的功能进行分割,基于可重用的目的将一个大的软件拆分成多个独立的组件,减少系统耦合度
- 通过一些设置可以编译成单独的app,一般只负责单一业务
- MVC、MVP和MVVM的原理和区别
- MVC:
- 视图(View):负责生成可交互的用户界面
- 控制器(Controller):负责业务处理
- 模型(Model):负责数据保存
- 优点:
- 耦合性低
- 模型重用性高
- 维护成本低
- 开发效率高
- MVP:
- 把Controller改成了Presenter
- 各部分通信是双向的
- View与Model不再有联系
- 优点:
- 模型和视图完全分离
- 更高效地使用模型,其他逻辑已经全部都放在了Presenter里
- 一个Presenter可以用在多个视图
- MVVM:
- 把Presenter改成了ViewModel
- View和ViewModel双向绑定,View的变动,自动反映在ViewModel,反之亦然。
- 优点:
- 开发者不用处理接收事件和View更新的工作
- app优化
-
性能分析工具:
-
严格模式:用来限制应用做的一些不符合性能规范的事情,一般用来检测主线程中的耗时操作和堵塞,然后输出警告log
-
android profile:可以检测内存占用,cpu占用,运行耗时,网络流量等
-
leakcanary:可以检测内存泄漏
-
lint:分析静态代码,根据规范提出改进建议
-
LayoutInspector:检测布局是否过渡嵌套,可以看到布局的层次结构和视图属性
-
-
布局优化:
- 减少布局嵌套层级
- 去除无用背景
-
响应优化
- UI线程不做耗时操作
-
内存优化:
- 避免内存溢出和内存泄漏
-
启动优化
- Application中尽量不做耗时操作,假如要做就启动一个IntentServce去执行
- onCreate过程中也尽量减少耗时操作
-
耗电优化
- 优化联网请求,减少请求次数和时间
- 不是必要时避免使用屏幕常亮属性keepScreenOn
- 减少使用GPS定位的更新频率,不需要时及时关闭GPS定位
-
网络优化
- 压缩传输的内容,减少流量消耗和加快速度
- 适当地建立缓存,减少重复下载次数
- 进程间通信方式
- Bundle/Intent传递数据
- 文件共享
- Messenger
- AIDL(Android Interface Definition Language)
- ContentProvider
- 广播
- LruCache原理
- 把最近使用的对象用强引用存储在链表中,当缓存满时,把最近最少使用的对象从内存中移除,LruCache使用了synchronize关键词,因此是线程安全的
- Glide图片库原理
- Glide使用了多级缓存机制,能够更好地实现图片、动图的加载。主要有内存缓存和磁盘缓存,内存缓存主要用于防止将重复的图读入内存,磁盘缓存主要用于防止从网络或者其他地方重复下载和数据读取
- Glide开始一个新的图片请求前会检查多级缓存
- 活动资源:如果当前对应的图片资源正在使用,则被放入活动资源缓存
- 内存缓存:如果图片最近被加载过,并且当前没使用,则放入内存缓存
- 资源类型:被解码后的图片写入磁盘文件中,下次读取同规格图片将不再解码
- 原始数据:图像的原始数据
- 如果缓存不存在,就会使用Bitmap复用池,可以减少频繁申请内存带来的性能问题。Bitmap复用池也是用LRU来管理的,每次解析一张图片为Bitmap的时候,会从BitmapPool中查找一个可以复用的Bitmap
- Glide和activity或者fragment绑定生命周期
- 未完待续…