Android 面试题

232 阅读27分钟

Q: viewModel的本质

  • 双向绑定;数据驱动UI

  • vue(比较彻底 底层会做数据的映射)、flutter(setData)

  • TouchDelegate: 通过父View设置TouchDelegate来给子View扩大点击范围,

  • 字节码文件是class文件JVM能识别的文件,虚拟机校验后能通过就可以执行。

  • 动态代理:允许你在运行时动态的生成接口的实现类的class文件来执行

  • Looper.setMessageLogging 可以监听主线程的消息

Q:ANR

  1. 触摸事件或按键:5秒没有响应
  2. 广播:10秒
  3. 服务:20秒

Android性能优化

  1. 优化网络请求:避免频繁进行网络请求,使用缓存来优化。避免加载大量数据,占用过大内存,应该拆分成多个网络请求,并行加载。
  2. 使用轻量级数据结构,避免内存泄漏;避免过多bitmap
  3. 使用约束布局优化布局,减少层级;ViewStub可以延迟加载布局;写死尺寸避免频繁测量;在自定义view时使用缓存避免频繁计算和绘制
  4. 避免主线程阻塞,使用异步来处理耗时操作

什么是Android中的内存泄漏?你如何解决内存泄漏问题

内存泄漏是指分配使用的内存没有被正确的释放,导致内存不断增加,最终app性能下降或崩溃。常见如下:

  1. IO未关闭
  2. activity、fragment、view引用未及时释放
  3. 后台异步任务、未关闭数据库、广播
  • 合理使用bitmap;inSampleSize;及时recycle对象;使用弱或软引用去包裹它避免oom;使用对象池来缓存bitmap;利用inJustDecodeBounds来获取图片分辨率,然后利用inSampleSize去缩放显示图片避免内存溢出;只加载当前显示部分,使用BitmapRegionDecoder来局部加载

Q: Kotlin协程完全解析

  1. suspend关键字被编译之后就是CPS,其实本质上还是回调函数。
  2. suspend的方法 在编译后方法会多一个contination的参数,它在方法执行完调用了resume方法,把结果给回调给了上层,把执行权交给了上层。这里面利用了递归。(contination resume结果移交执行权)
  3. State Machine 状态机会缓存执行的步骤,本质是一个Switch方法状态机会在一个case执行完把状态改为下一个case的值,以实现步骤的切换。(每次递归走一个case)label

Q:kotlin 相关细节

  1. 扩展方法和成员方法都存在时,优先调用成员方法
  2. observe不能在子线程注册,observe在onStart、onResume 、onPause 都可以收到监听

Q:app的安全防护

  1. 资源和代码都加混淆
  2. 签名哈希值校验:根据签名生成一个哈希值key写死到so里,然后打包。每次app运行校验当前app的签名哈希值是否和本地写死的一致,如果不相同说明签名改变是二次打包的。直接杀死app
  3. 包名哈希值校验:根据包名来生成key,每次打开app校验是否包名有改变。
  4. 加固
  5. 使用https
  6. allowBackup 设置为false,避免app可以备份
  7. webView禁止保存密码
  8. 自定义键盘
  9. 检查手机是否root,如果root了就禁止某些核心功能
  10. 检测手机是否安装了Xposed
  11. System.getProperty(“http.proxyHost”) 判断是否挂代理了

Q:SurfaceView和TextureView区别

  1. SurfaceView: 底层使用双缓存机制,可以在子线程进行绘制操作不会阻塞UI线程。(双缓冲机制:一个负责绘制,一个负责显示,绘制完事会复制给显示的surface)
  2. TextureView: 可以使用硬件加速,并且可以配合openGLES可以实现更高级的图像处理和效果。

Q: MediatorLiveData

  1. 它可以实现addSource和removeSource。
  2. 当你有多个liveData数据结构,可以丢给它,它内部会包装你,监听你setValue事件,然后回调给MediatorLiveData的callback,它在设置自己的setValue,统一拿到所有liveData的回调事件。
  3. 场景就是多个liveData在不同地方发送事件,然后Mediator中介者可以拿到所有事件的回调,并且可以方便添加观察者和删除。

Q: http的特点

  1. 2.0比1.0多了 多路复用(一个tcp链接可以并发处理多个请求),服务器推送
  2. 原理是 建立连接-发送请求信息-处理信息-处理响应-断开连接

Q: okhttp Interceptor 拦截器

  1. 它允许你在发送请求和接受响应之后做一些处理。
  2. 重试拦截器
  3. 连接拦截器,它是最后一个拦截器
  4. cache拦截器
  5. 日志拦截器
  6. 责任链模式:它让每个拦截器没有耦合,一个请求发出后,从第一个拦截器开始如果没有人拦截,而一直到最后一个拦截器来处理。

Q: Kotlin 协程库中的 launch 函数的定义

  1. context上下文:调度器和异常处理器
  2. 启动方式:立即启动或是懒启动(当调用join或start)
  3. block:回调接口,协程的执行体

Q: 锁

  1. 实例对象锁,成员方法上加synchronized和synchronized(this)是等价的
  2. 类锁,静态方法上加锁或synchronized(类.class )等价的
  3. 同步代码块锁,Object lock = new Object(); synchronized(lock.class )

Q: ViewModel的clear方法,什么时候执行

  1. 当Activity销毁时,finish方法执行后,onDestory执行前。
  2. 切换屏幕不会执行clear
  3. addBack 为true ,fragment返回后不会直接销毁Activity。

Q: Binder它相比传统的 IPC 机制有什么优势

Binder 通信采用 C/S 架构,

  1. 安全性:为每个app分配Uid 和 pid
  2. 内存映射机制 : 只进行一次内存拷贝,而共享内存不需要,其他的需要二次如 socket、信号

Q: 注解

编译时检查:可以在编译期对代码进行静态检查,以确保代码的正确和安全性。

运行时处理:利用运行期反射去实现一些逻辑。

Q: 假如只有一个cpu,单核,多线程还有用吗

尽管只有一个CPU,多线程依然有效。虽然不能同时执行多个线程,但可以通过轮转调用, 这样操作系统在多个线程来回执行,给人看上去是多个线程在同时执行。提高程序的运行效率。

Q: ViewModel如何创建的、实现切换屏幕保存数据

  1. 当前屏幕旋转、配置发生变化后,Activity会销毁重新创建而ViewModel并不会。
  2. 所以当你Activity重建时,会重新对ViewModel里的livedata进行监听,监听时会创建新的LifecycleObserver对象,它内部的lastVerions会变成初始值-1,而liveData并没有重新初始化它的mVersion会大于你,所以会再次有回调进行设置数据。
  3. 单一实例 在生命周期中只有一个
  4. ViewModel如何没有自定义工厂来创建对象,则使用的是newInstanceFactory反射来创建实例的。
  5. ViewModelProvider 需要传一个lifeOwnerStore ,这个接口在Activity默认有实现,它会创建Store对象只会创建一次,Stores使用map来缓存ViewModel,所以验证了Activity里ViewModel的实例只会存在一个。
  6. Activity构造方法监听了其getLifecycle的生命周期在onDestory时调用getStore().clear(),mConfigChanage字段当切换横竖屏不会调用clear方法

Q: inline noinline crossinline

inline 对函数使用优化避免高频调用栈,会把代码关联进来使用,并且包含形参。(内联优化)

noinlin对参数使用可以让其不优化,不把形参关联进来使用,如果不加这个参数 你无法把这个形参作为返回值来使用,如例子中的post。(关闭字段的内联优化)

crossinline 当想在inline的函数中的回调去调用形参的高价方法时需要加crossinline(如例子中想在runOnUiThread的回调中使用preaction),因为这个参数可能存在局部return。

inline fun hello(crossinline preaction: () -> Unit, noinline post: () -> Unit): () -> Unit {  
    preaction()  
    println("111")  
    post()  
runOnUiThread({  
    preaction()  
})  
    return post  
}

Q: Intent传输大小有限制吗?

有大概1m的限制,intent底层使用binder来实现传输,binder会把数据放到binder的缓冲区中。在binder的C++文件里有定义binder_VM_Size 字段定义了大小的限制。binder本身是为了多进程灵活的交互而设计的。

Q: Serializable和Parcelable的区别

Serializable Java会使用反射来判断类是否可以序列化,性能较差。 Parcelable 把一个完整的对象进行分解,分解后每一步都支持传输,保存到Parcel专为利用binder传输到另一个进程中,另一个进程取出基本类型的数据,再组装成对象。专为Android设计

Q: GC是什么时候触发的 ?

  1. 内存不足时
  2. 永久代内存不足
  3. system.gc()
  4. 堆中某一区域占用特别多内存

Q: JVM运行时数据区

  1. 堆:对象实例和数组地址
  2. JVM虚拟机栈: 存储方法的一些信息,变量、方法地址
  3. 本地方法栈:存储方法信息,只不过为本地方法服务
  4. 程序计数器: 线程私有存储当前线程执行的状态与位置
  5. 方法区: 类的信息、静态方法
  6. 运行时常量池:常量
  7. 直接内存

Q: Java 中的垃圾回收机制?

Java的垃圾回收机制是使用自动内存管理的,Java会定期扫描程序不再引用的对象,回收它并释放内存区域,供其他的对象来使用。

  1. 引用计数法:无法解决引用链
  2. 可达性分析算法:从一组称为GC roots的起始对象开始,通过对象之间的引用链,递归遍历所有对象,并标记所有能够直接或间接引用到的对象为存活对象,未被标记的会被垃圾回收机制给清除掉。GC roots(静态对象、线程中的变量)

Q: Java 回收的算法

  1. 标记-清除: 标记存活的对象,未被标记的会被清除
  2. 复制:内存区域分为2个区,存活的复制到一边,垃圾在一边。
  3. 标记整理:标记存活放到一边,清除剩下的。结合了标记-清除和复制
  4. 分代回收:将内存分为年轻代和老年代。不同代采用不同的算法。新创建的对象放年轻代这里,尽可能快速回收掉哪些生命周期短的对象;N次回收仍然存在放到老年代里。

Q: 确保线程安全的方法

  1. 同步方法:synchronizedReentrantLock
  2. 线程安全集合:CopyOnWriteArrayList
  3. 原子类:AtomicInteger
  4. 不可变对象:一旦创建内容不再改变
  5. 避免共享状态:减少并发的问题
  6. 使用线程局部变量:将变量限制在线程的局部变量中,从而每个线程都有自己的数据副本,从而避免线程安全问题。
  7. 使用并发工具:CountDownLatch

Q: 避免死锁的方法

  1. 避免使用多个锁
  2. 按顺序处理锁对象
  3. 锁的超时处理
  4. 死锁的检测:如果有问题及时释放锁

Q: 线程的状态

  1. 新建:刚创建对象
  2. 就绪:调用start
  3. 运行:获取执行权
  4. 阻塞:未获取到某个资源阻塞了;Latch方法或Synchronized没找到锁
  5. 等待:调用wait方法
  6. 超时等待:wait(timeout)
  7. 终止:run方法执行完

wait会释放锁,sleep不会。

Q: Java代码是如何在app执行的

  1. Java代码通过编译器编译成dex格式的文件
  2. dex文件加载:需要通过Dalvik虚拟机进行加载
  3. 解释器执行,把dex解释成机器码
  4. 机器码执行硬件

Q:加固的原理

  1. 代码混淆:对代码重命名、加入一些代码、删除一些无用代码、改变一些代码结构,增加逆向难度。
  2. 虚拟机保护:将一些敏感代码转化为一些虚拟指令,这些指令不会转化为二进制代码来操作机器指令,由虚拟化引擎去执行这些虚拟化指令。
  3. 动态加壳:会在原始apk外面增加一层代码,增加反编译和逆向难度。在app启动时dex壳会解密,然后在执行里面的原始代码。
  4. 资源文件加密:图片、视频、音频等加密。在运行时动态解密,防止文件被直接替换或访问。
  5. 安全检测和防御:会对app进行静态分析和动态分析,进行防御防止反调式和反注入。

Q:堆和栈在内存中的区别是什么

堆:动态分配的内存区域,用于存储在运行期间动态分配的内存,堆中的对象是无序存储的,可以动态的分配和释放;由操作系统的内存管理器负责管理,内存大小不固定,且分散,容易有内存碎片。

栈:有序存储的,后进先出(桶),存储方法的局部变量、参数等由编译器自动分配和释放的。

Q: IdleHandler到底有啥用

可以在app启动时添加一些可以等界面渲染完再做的初始化操作。放这里的代码会等消息队列执行完毕后(除了延迟消息)就会执行

MyIdleHandler myIdleHandler = new MyIdleHandler(); Looper.myQueue().addIdleHandler(myIdleHandler);

Q: recyclerView的四级缓存

  1. mAttachedScrap 一级缓存屏幕当前可见的item
  2. mCachedViews 二级缓存 移除屏幕的默认2个
  3. mViewCacheExtension 留给用户自定义的缓存策略
  4. mRecyclerPool 根据itemType缓存默认5个

按照缓存的等级从1到4去取viewHolder。如何都没有取到会执行createViewHolder。

Q: 死锁发生通常需要满足以下四个必要条件

2个线程都无法持有到对方的资源,而造成互相等待的现象。(因争抢资源造成互相等待的现象)

  1. 互斥条件: 至少有一个资源是无法被同时共享的
  2. 请求和等待条件:线程至少持有一个资源,并且等待获取另一个线程的资源
  3. 不可剥夺条件:资源不能被持有它的线程所夺取,只能等持有者自愿释放。
  4. 循环等待条件:存在一个线程等待链,等待链中每个线程都在等待下一个线程释放资源。

Q: List、Set、Map

List:有序(被添加的顺序排列)、可重复、申请的内存上是连续的、clear方法是把数组的每个元素置为null size置为0、添加尾部O(1)中间位置O(n) 修改元素是O(1) 删除尾部O(1)中间O(n) 因为需要复制数组

Set: 无序,不重复 HashSet的操作都是O(1)因为是利用hashmap来实现的。TreeSet是基于红黑树来实现的,其操作是O(log n)

Map:利用hash表来存储元素的。默认添加、删除都是O(1),但有冲突时对链表操作是O(n);无序、key唯一

Q: Android性能优化有哪些

  1. 优化网络请求:避免频繁进行网络请求,使用缓存来优化。避免一次性请求加载大量数据,占用过大内存,应该拆分成多个网络请求,并行加载。
  2. 使用轻量级数据结构,避免内存泄漏;避免过多bitmap
  3. 使用约束布局优化布局,减少层级;ViewStub可以延迟加载布局;写死尺寸避免频繁测量;在自定义view时使用缓存避免频繁计算和绘制。
  4. 避免主线程阻塞,使用异步来处理耗时操作。

Q:什么是Android中的内存泄漏?你如何解决内存泄漏问题

内存泄漏是指分配使用的内存没有被正确的释放,导致内存不断增加,最终app性能下降或崩溃。常见如下:

  1. IO未关闭
  2. activity、fragment、view引用未及时释放
  3. 后台异步任务、未关闭数据库、广播
  • 合理使用bitmap;inSampleSize;及时recycle对象;使用弱或软引用去包裹它避免oom;使用对象池来缓存bitmap;利用inJustDecodeBounds来获取图片分辨率,然后利用inSampleSize去缩放显示图片避免内存溢出;只加载当前显示部分,使用BitmapRegionDecoder来局部加载
  • 使用轻量级数据结构:SparseArray代替hashMap

Q:异步消息

Handler.createAsync()可以创建异步消息,它不会受消息屏障的影响,会被立即放入消息队列,会被立即处理的。 handler的回调msg的callback优先最高,其次构造方法的回调,最后是重写的handleMessage。

Q:什么是消息屏障

消息屏障是一种机制,用于确保Android消息队列中的消息是按照正确是顺序执行的。

  1. UI更新顺序保证。比如列表更新一半肯定不会去执行其他消息,从而避免出现UI不一致的现象。
  2. 事件处理顺序的保证。比如点击事件肯定是按钮先有反应(颜色的变化)才会进行跳转到下一个界面。

Q:为什么LiveData的postValue会丢失数据

最好不要使用postValue,它是放到异步的消息队列里,因为如果多次快速调用postValue方法,观察者不会收到中间的值,只能收到最后一次更新的回调。setValue只要你确保在主线程调用,它会立即生效。

Q: 绘制流程?

  1. 界面布局是通过 ViewRootImpl setView 添加的decorView(它是在phoneWindow的installDecorView被创建的)开始的,它会调用requestLayout。
  2. ViewRootImpl的requestLayout调用scheduleTraversals并执行同步屏障保证绘制工作在looper中优先执行。
  3. 然后又执行Choreographer -> postCallBack() ->scheduleFrameLocked()方法里调用nativeVsnc,来注册vsync信号。
  4. 这样每16ms屏幕硬件发送同步信号FrameDisplayEventReceiver中onVsync可以拿到回调后,然后通过handler回调执行doFrame方法, doFrame 从mCallbackQueues取出callback回调给ViewRootImpl执行doTraversal进行三大绘制流程,递归执行measure,draw,layout

Q: handler流程?

当前你在主线程创建handler时,会取出当前线程的looper对象,此looper在main方法时已经调用loop方法在死循环的调用next方法来取消息,一旦拿到消息会回调到主线程的handler的回调方法handleMessage方法中。send方法会把消息入队到当前线程所持有的looper对象(成员变量)消息队列的数据结构里(消息队列它是一个单链表),虽然是子线程调用但不重要主要看handler是否是主线程创建的。(nativePollOnce 底层是调用epoll_wait来实现的)

Q: handler用于线程间通信,怎么保证线程安全?

当enqueMessage入队和出队next时都有使用synchronized锁来处理。

Q: 静态变量的初始化

private static class InnerHandler extends Handler {
    private static int count = 10;
  
    static int cnt = 6;

    static {
        cnt += 9;
    }

    public static void main(String[] args) {
        System.out.println(cnt);
    }

    static {
        cnt /= 3;
    }
    

类的生命周期分为 装载、链接、初始化、使用、卸载;一个静态变量在加载时为0,初始化阶段(当你访问时或创建对象等场景)会被赋值,而使用阶段也就是创建对象时不会再赋值了;所以如上面InnerHandler类,想要每次创建对象count都是10,那么count不能声明为静态。

我当时对count值进行修改变为0后,重新打开一个界面再次创建对象发现不是初始化10而是0。一个类生命周期装载、链接、初始化、卸载只执行一次,使用环节是可以多次的。

比如我们使用so或是加载c文件里的代码,都是一般使用静态代码块调用 System.loadLibrary(),那么这个静态代码块是什么使用调用的呢?类默认不使用是不会调用的;当你new、调用静态成员或方法时就会触发静态代码块的调用(被 静态final修饰的成员除外)。

Q: Android 大图加载

裁剪或缩放:BitmapRegionDecoder来加载指定图片的一部分区域 优化内存占用:RGB565减少内容一半,质量压缩 网络库加载:glide RecycleView等控件:让一屏只显示一部分,从而减少压力

Q:什么是ThreadLocal?

它可以实现在每一个线程存东西,在每一个线程取出存储的东西;每个线程互不影响的存取东西;但每个线程只存一个对象的数据;无须同步就能保证线程之间不出现数据争用

set(object)会取出当前线程,线程持有一个ThreadLocalMap对象,它保存了你传的数据,key是ThreadLocal,value是你传对应的值;

用完调用remove,避免内存泄漏

Q:内存优化?

好处:避免oom,提高流畅度,减少内存占用提高后台存活率,减少异常发生

  • 尽量使用静态内部类,避免使用非静态的内部类;(非静态的内部类会默认持有外部类的引用,占用内存多,容易有内存泄漏)
  • 能使用Application就不要使用activity
  • 资源未close
  • 监听器未注销
  • 经典handler不是静态的
  • webview使用多进程
  • LeakCanary
  • HashMap和spareArray;spareArray2个数组保存key和value,key是int避免装箱,查找key使用二分效率高,省内存轻量,删除只做标记,添加会复用已删除的位置,尽量做到不扩增数组
  • 慎用枚举类型占用内存大,dex包体积大
  • 太多的线程会造成OOM(任务队列不要设置无限大)。每个线程是占资源的
  • 使用Android studio profile 的查看内存泄漏

Q:电量优化?

  1. 电量优化和绘制优化是关联的,如果cpu占用高,电量自然也就耗电量高
  2. 定位根据需要来使用
  3. 尽量避免无限轮询网络请求
  4. 一些重量级操作,只有在手机充电时执行
  5. 当用户是WiFi时才后台预下载一些任务和自动播放视频;

Q:绘制优化?

  1. 开发者模式里 GPU Overdraw
  2. Hierarchy Viewer,LayoutInspector
  3. ConstraintLayout 和 Merge
  4. bitmap压缩inSimpleSize=2 宽和高都缩小一半(内存占用缩小4倍) 和 565 内存缩小一半
  5. RelativeLayout比线性布局测量次数多

Q:启动速度优化?

  1. 思路以空间换时间
  2. 有了StartUp可以把所有contentprovider去掉使用这个一个来做初始化,并且它还可以控制执行顺序;(利用meta-data设置了数据,反射获取其实例,然后调用其方法)
  3. splash预加载数据和预加载view(AsyncLayoutInflater)
  4. 预判有缓存广告再去使用
  5. 自定义view使用clipRect,避免绘制不必要的地方
  6. ViewStub 按需加载
  7. draw和measure不要创建大量或过大对象,避免大GC和内存抖动
  8. 无限动画view高度写死,避免重新测量和layout布局,会导致父布局层层进行layout。应该只重写onDraw,每次动画执行就重绘就可以了;

Q: 抽象类与接口的区别?

抽象类表示整体的一个东西;不是具体化,抽象一些。通常 子类 is a 抽象类;如果动物是抽象类,则狗是动物,猫是动物。userViewModel是ViewModel;一般是先有子类然后抽取共同的方法,到父类中,父类这时就诞生了。

接口定义了某些功能和行为。一个类实现了接口代表它有这些行为和功能。先有接口后有实现类。

Q:volatile关键字的作用?

  • 保证不同线程对这个变量的可见性
  • 禁止进行指令的重新排序
  • 什么是原子操作?
  • m = 6 是原子性操作,而 int m = 6; 不是原子性操作,它有2个操作,声明m变量和对m赋值为6

在双重检查的单例中,

  1. instance = new Singleton(),不是原子操作会有3个步骤
  2. new Singleton() 在堆申请内存
  3. Singleton调用构造方法初始化变量
  4. instance局部变量指向Singleton实例在堆中的内存

所以如果JVM重新排序执行代码,1-2-3 也可能是 1-3-2,这样多个线程访问,可能会取到一个instance是不为null, 但构造方法还没有执行的;

所以instance成员变量应该加volatile字段,这样在写操作时会有内存屏障,只有写完了,它是实例才不会为null。

Q:如何启动其他应用的Activity?

其activity在清单文件里 android:exported="true" 才允许其他APP访问; intent必须同时匹配activity的intent-filter中的 action 、category、data; 如果有多个intent-filter匹配任意一组就可以

Q:Activity的启动过程?

主线程调用到IPC,IPC回调回主线程;

startActivity一些列方法调用到ActivityManagerService的startActivity,与ActivityManagerService交互是进程间通信,ActivityManagerService回调到activityThread的内部类ApplicationThread的scheduleLaunchActivity()其方法调用Handler H , 执行了handleLaunchActivity()和performLaunchActivity()对activity对象的创建和启动;

Q: 谈谈Error和Exception的区别

exception 是Java运行可以预料的异常,受检异常必须trycatch捕获,非受检异常可以不捕获一般可以使用代码规范来避免

error是系统错误一般不进行捕获,一般不可恢复,比较严重的错误。

Q:谈一谈Fragment的生命周期?

onAttach fragment和activity建立关联

onCreateView 创建视图

onDestroyView fragment布局被移除

onDetach fragment和activity解除关联

Q:ThreadPoolExecutor的工作策略?

先使用核心线程池,没有空闲时,放任务队列,队列放不下时,开非核心线程去执行,当核心线程+非核心线程>最大线程数量,就会走拒绝策略(默认拒绝策略是抛异常)

Q:ThreadPoolExecutor的构造方法?

核心线程池大小

最大线程数量

非核心线程闲置存活时间

非核心线程闲置存活时间的单位

任务队列

拒绝策略

Q:为什么协程比线程更轻量?

  • 并发设计模式,简化执行异步代码
  • 轻量:可以在一个线程中运行多个协程,支持挂起不会阻塞当前协程的运行,支持多个并行操作
  • 内存泄漏更少:结构化
  • jetpack是集成的
  1. 更少的内核调用:每个线程都有自己的堆栈空间(存放变量、方法调用的信息),是由系统分配内存和管理这些资源。
  2. 更少的资源占用:协程是kotlin在运行时管理,不是由操作系统所以不涉及系统调用,创建、切换、销毁都是在应用层完成,相对于线程内核层更轻量级。协程是共享线程的内存空间的,而不是每次需要系统分配。
  3. 更快的切换速度:协程在用户态就完成了切换(只使用CPU就可以),而线程需要每次start都会进行系统调用,所有协程速度更快。
  4. 更灵活的调度策略:协程是在编程语言运行时环境管理的,更容易实现非抢占式调度、协作式等策略,从而更好的适应使用场景。

协程比线程更轻量,更适合大量的并发操作。

Q: 非抢占式调度、协作式策略是什么意思

  1. 非抢占式调度:一个协程在执行过程中,只有执行完毕或是主动挂起,才会让出CPU的执行权,其他协程有机会去执行。
  2. 协作式策略:在协作式调度中,调度器会根据优先级、和状态,决定是否让出执行权给其他协程。协程在执行时,会有周期性问调度器的是否要给其他协程去执行。

实际场景中,非抢占式调度更适合快速的响应处理任务的一些场景,而协作式策略适合一些需要灵活配合调度策略的环境,它可以更好的控制协程之间的执行顺序。

Q:什么是Drawable?

抽象类,有很多不同场景的子drawable,shapeDrawable; 默认res/drawable aapt 构建工具会进行无损压缩,256真彩PNG转成8位PNG,res/raw不会进行压缩;

Q:引用对象?

new Object()表示在堆申请了一个内存地址空间比如0x001,它被保存到了给Java虚拟栈里的一个地址上,这个地址是局部变量a。

Object a = new Object();

Java虚拟机栈(b)指向了堆内存地址上0x001, a == b == 0x001

Object b = a;

所以堆内存0x001有2个栈内存指向它(也可以理解为保存了),a=null 切断了 a 指向堆内存的引用0x001,所以只剩下b指向堆了,b不会因为a=null变为null的; 0x001 因为有b的引用,GC是无法回收它的,一旦b为null了,0x001无引用了(没有人存储它了),GC就会回收它

a=null;

Q:Android事件分发机制?

dispatchTouchEvent:进行事件的分发(传递)。默认常见控件是没有重写的(如LinearLayout,button),走的都是父类ViewGroup或是View;

当是ViewGroup分发时,会调用onInterceptTouchEvent默认是false, 这时就会逆序遍历它的所有孩子,一旦触发点击到孩子了,在其点击范围内,并且孩子的dispatchTouchEvent返回true,停止循环则这个事件就交给它处理了;孩子的dispatchTouchEvent是ViewGroup则重复走上面的逻辑,如果是View类型则直接会调用View的dispatchTouchEvent,默认直接调用onTouchEvent,onTouchEvent根据有点击事件就会返回true;

当是ViewGroup分发时,调用onInterceptTouchEvent返回true,则不会向孩子分发事件了,直接调用super.dispatchTouchEvent也就是View的方法,View的dispatchTouchEvent会调用onTouchEvent,ViewGroup自行可以重写它写自己的逻辑;

onInterceptTouchEvent:对事件进行拦截。该方法只在ViewGroup中有,View(不包含 ViewGroup)是没有的。

onTouchEvent 事件处理,View的实现默认有设置onClick onTouchListener 它都会返回true

场景:当一个父容器包裹2个子view,因为是逆序遍历,所有上层也就是父容器数组中后面的元素,会优先判断是否有消费这个事件,一旦消费了则会break循环;

一旦有子view消费触摸事件,mFirstTouchTarget() 就会被赋值,后序事件就直接利用mFirstTouchTarget来分发事件。一旦子View触摸过程中,划出范围到父view,它onInterceptTouchEvent拿到事件,在viewGroup里会包装一个cancel的Action分发给子view。

down事件的起点,决定后序事件交给谁。

Q:Android滑动冲突处理?

内部拦截: 父类在onInterceptTouchEvent中除了down返回false其余返回true; 子类根据场景在move中调用requestDisallowInterceptTouchEvent(true)表示要自己处理事件,down中写死requestDisallowInterceptTouchEvent(true)表示要事件的;

外部拦截: 父类根据onInterceptTouchEvent在move中判断是否要拦截事件;

Q:2个图片分辨率尺寸一样,但后缀是PNG和JPG,把他们转成bitmap占用内存大小是否一致?

一样,因为比如ARGB8888是占4字节,100 * 100 * 4 = 40000字节,40kb; 内存占用和文件格式无关,文件格式和文件在硬盘的大小有关,PNG文件转成JPG占用硬盘会小;

Q:基本类型的成员变量存储在JVM哪里?

虽然是基本类型但是成员变量是对象的属性,对象创建是存储到堆中的,自然它的属性也会随之存储到堆中。 当基本类型在方法中时是存储到Java虚拟机栈中的。