【Android FrameWork】综合面试问题

1. 怎么跨进程传递大图片

考察点

  • 了解各种跨进程传输数据的方式及各自优缺点
  • 了解TransactionTooLargeException的触发原因和底层机制
  • 了解Bitmap传输底层原理

跨进程传大图有哪些方案

  • 给图片保存到固定的地方,传key值(路径)给对方
    • 问题:性能差,把图片写到文件需要耗时,对方读取文件也需要耗时
  • 通过IPC的方式转发图片数据
    • 不经过文件系统,但是需要多次拷贝

IPC方式传图

  • Binder:性能好,使用方便,但是大小有限制
  • Socket、管道:两次拷贝,也有大小限制
  • 共享内存:性能不错
  • 主要注意点
    • 1.性能,减少拷贝次数
    • 2.内存泄漏,资源及时关闭

TransactionTooLargeException

  • 发出去的或者返回的数据量太大
  • Binder缓存用于该进程所有正在进行中的Binder事务
    • 进程弃用binder机制会映射一块内存,大小是1M
    • 跨进程通信申请的缓冲区大小是不能超过1M的
    • 所有binder事务共享这1M内存空间,应该尽量避免同时跑多个事务,尤其是数据量很大的事务
  • 大数据量打碎分批发,或按需发(官方建议)

总结 跨进程传递大图片的方式

  • 图片写到文件,路径传到另一个进程,再读出来
  • intent传图,但是容易抛异常,原因是什么
  • binder调用传图,底层ashmem机制

2. ThreadLocal原理

关于更多面试题资料,可以前往 GitHub 自行查阅。

考察点

  • ThreadLocal的适用于什么场景?
  • ThreadLocal的使用方式是怎样的?
  • ThreadLocal的实现原理是怎样的?

ThreadLocal在FrameWork中的使用

  • Looper

     

  • Choreorgapher

原理

  • 每一个线程都有一张表,其实就是一个数组,key和value都存储在数组中
  • key存储在weakReference中,value对应对象如Looper、Choreorgapher等
  • 一个应用里可以定义多个ThreadLocal,ThreadLocal都有自己的哈希值,哈希值根据hashCounter计算
  • 算出hash值之后,对表的size取余数,就能算出key:value在表中的index值
  • 如果发生了hash冲突,就会从当前index开始向下遍历数组,放在空闲的位置上
  • 代码如下:

总结

  • 同一个ThreadLocal对象,在不同线程get返回不同value
  • Thread对象里有张表,保存ThreadLocal到value的映射关系
  • 这张表是怎么实现的?(见上面的原理分析)
  • 是如何解决hash冲突的?(见上面的原理分析)

3. 说说Looper的副业(待完善)

  • Looper里可以监听其它描述符
  • 创建管道,跨进程传数据,用looper监听描述符事件

4. 怎么检查线程有耗时任务

两种情况

  • 正常的,轻微阻塞
  • 不正常的,严重阻塞

检测机制

  • WatchDog:
    • Framework自带,检查system_server中系统服务是否正常
    • 用于检查死锁或者线程异常
  • BlockCanary
    • 开源框架,用于检查线程是否有耗时任务

WatchDog WatchDog的作用上面说过:一是检查是否发生了死锁,二是检查线程是否被任务blocked

  • WatchDog是一个线程
  • 看下WatchDog的关键变量:
  • mHandlerCheckers是核心列表,添加了多个HandlerChecker
  • 每一个Checker都对应了一个线程,比如主线程、前台线程、IO线程等,这几个线程都是system_server中非常重要的工作线程
  • 每一个HandlerChecker下都有一个Monitor列表
  • addMonitor实质上是向mMonitorChecker加了一个BinderThreadMonitor,用于检测binder线程是否正常

看下下图

  • 第一个MonitorChecker用户检查系统服务是否发生了死锁,在单独的线程中检查,

    • 原理就是在另外的线程中去尝试拿到锁,拿到了就正常返回
    • 如果一直拿不到,就可能是产生了死锁问题
    • 如图AMS中代码细节
  • 同时服务还会把自己的工作线程new一个HandlerChecker,也注册到watchDog,每一个handlerChecker都会对应一个线程,在线程中派发handlerChecker

BlockCanary 原理就是通过messageLogging在消息分发前后的时间戳打印,利用时间戳的差算出消息执行耗时

5. 怎么同步处理消息

「同步处理消息」:我们发了一个消息到另外一个线程去处理,并没有直接返回,而是挂在那里等待消息处理完毕,有些时候还需要拿到消息处理结果,这就是同步处理消息

适用场景如图124

应用要访问另一个进程的服务,请求会调到服务进程的binder线程池中,binder线程切换到工作线程处理请求,工作线程工作的时候binder线程会一直等待,应用端也会一直等着,等到处理完成后返回结果给给应用

6. 你去了解framework是为了解决一个什么样的问题,怎么解决的?

考察点

  • 你有没有解决复杂问题的经验
  • 你有没有深入研究底层原理的习惯
  • 你的知识体系是否有一定深度

7. 应用组件相关题目

  • 为什么Activity在onResume之后才会显示出来
    • ActivityThread handleResumeActivity时WindowManager才会addView并makeVisible
  • bindService的时候Rebind总是调不到,研究原理
    • TODO
  • 广播onReceive的context可否启动Activity, 显示Dialog?
    • TODO
  • 发现provider的onCreate比Application还早,研究一下
    • Application 构造方法 –> Application.attachBaseContext –> ContentProvider.onCreate –> Application.onCreate –> Activity.onCreate
    • 看下 ActivityThread.java 的 handleBindApplication方法:
    • 图130

8. 消息通信相关题目

  • intent带的数据量大了会异常,研究原因
    • binder机制 1m限制
  • 需要跨进程传大图,研究Bitmap传输原理,Ashmem机制
  • 想知道Handler消息延时的精度怎么样,去了解原理
    • 延迟处理不是延迟发送,精度不太准确
  • 为什么有时候IdleHandler调不到,去了解原理
    • 主线程繁忙,一直在处理消息
    • 比如:
      • 在View的onDraw方法里面无限制的直接或者间接调用View的invalidate方法。
      • 做一个无限轮询的View动画。

9.性能优化相关题目

  • ANR了,看主线程是否有耗时任务
  • 卡顿掉帧,了解屏幕刷新机制,研究Choregrapher
  • 启动速度优化,了解应用启动原理
  • 内存优化,清理不必要的资源

10. Android FrameWork用到了哪些设计模式?请举例说明

单例模式

  • Framework中:SingleTon类,应用IAM

  • 线程内:

  • 线程间/进程内:Choreographer,ThreadLocal线程私有,不同线程获取不同的实例,同一线程获取同一实例

    • 图125

      125.png

  • 进程间:

    • 进程间的内存是相互隔离的,如何保证在进程间的单例呢,这个时候就需要一个中间人,所谓的中间人,就是他可以和所有进程进行通信,由其确保这个对象是单例的
    • 下面的例子中,单例是ServiceManager,中间人是binder驱动
    • 对所有的进程来说,ServiceManager对应的binder句柄都是一个即0,这样binder驱动在转发请求的时候都会送往同一个目标,即ServiceManager所在的进程,这样就能保证ServiceManager是跨进程单例的
    • 图127

观察者模式

  • 分为进程内和跨进程两种
  • 应用:
    • 广播(同时支持进程内和进程间)
    • 推送(基于网络)
  • 应用层
    • Rxjava
    • RecyclerView:Adapter的notifyChanged、notifyItemChanged等

代理模式

  • 静态代理

    • ActivityManagerProxy
      • IActivityManager的接口调用都会转交给mRemote
      • 128

  • 动态代理

11.Framework中有什么你觉得设计很巧妙的地方,请举例说明

  1. Binder调用,模糊进程边界,让IPC调用像在一个进程中一样简单,使开发者不必过分关注跨进程通信的事,将重点放在业务上

    • 请求的转发:请求由应用端发起,通过binder驱动转发给目标进程去处理,目标进程处理完毕之后,会把返回结果通过binder驱动传回Client端
    • binder对象的传递:包含实体对象和引用对象,这两种实体在经过binder驱动层的时候会自动转换,比如binder实体对象跨进程之后就变成了代理对象,代理对象回到实体对象所在的进程之后又会还原为实体对象;不论实体对象还是代理对象,应用层拿到的都是统一的接口对象,方便调用
  2. Bitmap大图传输,高性能

    • 传递匿名共享内存的句柄,到了目标进程之后映射内存,这样目标进程就能获取bitmap的像素数据
  3. Zygote预加载资源创建应用进程,共享资源,这样应用进程就不必重复加载资源

    • init进程启动后,会启动一些系统服务和Zygote进程zygote预加载资源(系统主题资源,常用的类等 )并启动system_server,并开启循环等待socket请求,孵化应用进程
    • 图129

      129.png

  4. Intent解耦,模糊进程边界,把调用者和组件之间进行解耦

    • 应用只需要表达自己的意图,由AMS选择处理意图的组件
    • 调用者和组件之间可能是同一进程的,也可能不是同一进程的
    • 比如广播,发广播的时候带intent,由AMS解析intent,匹配接收的receiver,receiver可能是同一进程的,也可能是不同进程的,只要用intent表达意图就可以了

最后

在这里我也分享一份由几位大佬一起收录整理的 Flutter进阶资料以及Android学习PDF+架构视频+面试文档+源码笔记 ,并且还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料……
这些都是我闲暇时还会反复翻阅的精品资料。可以有效的帮助大家掌握知识、理解原理。当然你也可以拿去查漏补缺,提升自身的竞争力。
如果你有需要的话,可以前往 GitHub 自行查阅。

猜你喜欢

转载自blog.csdn.net/Androidbye/article/details/114805331