前言
一转眼,2023就过去了,金三银四又要拉开序幕,那么我们的Android程序员关于Android开发岗位的面试,大家知不知道要对哪些知识点做准备呢?
本文给大家整了一套涵盖Android所有技术栈的快速学习方法和bi记。目前已经收到了七八个网友的反馈,说是面试问到了很多这里面的知识点。
一、你了解 Android 系统启动流程吗?
当按电源键触发开机,首先会从ROM中预定义的地方加载引导程序BootLoader 到 RAM中,并执行BootLoader程序启动Linux Kernel,然后启动用户级别的第一个进程: init 进程。init 进程会解析init.rc脚本做一些初始化工作,包括挂载文件系统创建工作目录以及启动系统服务进程等,其中系统服务进程包括 Zygote、service manager、media 等。在 Zygote中会进一步去启动 system_ server进程,然后在 system_server 进程中会启动AMS、WMS、PMS等服务,等这些服务启动之后,AMS中就会打开Launcher应用的home Activity,最终就看到了手机的“桌面”。
二、system_server 为什么要在 Zygote 中启动,而不是由 init 直接启动呢?
Zygote 作为一个孵化器,可以提前加载一些资源,这样 fork()时基于 Copy-On-Write 机制创建的其他进程就能直接使用这些资源,而不用重新加载。 比如 system_server 就可以直接使用 Zygote 中的 JNI 函数、共享库、常用的类、以及主题资源。
三、为什么要专门使用 Zygote 进程去孵化应用进程,而不是让system_server去孵化呢?
首先 system_server 相比 Zygote 多运行了 AMS、 WMS 等服务,这些对一个应用程序来说是不需要的。另外进程的fork()对多线程不友好,仅会将发起调用的线程拷贝到子进程,这可能会导致死锁,而system_server 中肯定是有很多线程的。
四、能具体说说是怎么导致的死锁的吗?
在POSIX 标准中,fork的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略,所以可以实现的速度快)以及所有系统对象,然后仅复制当前线程到子进程。这里:所有父进程中别的线程,到了子进程中都是突然蒸发掉的对于锁来说,从 OS 看,每个锁有一个所有者,即最后一次 lock 它的线程。假设这么一个环境,在 fork 之前,有一个子线程 lock 了某个锁,获得了对锁的所有权。fork 以后,在子进程中,所有的额外线程都人间蒸发了。而锁却被正常复制了,在子进程看来,这个锁没有主人,所以没有任何人可以对它解锁。 当子进程想 lock 这个锁时,不再有任何手段可以解开了。
五、Zygote 为什么不采用 Binder 机制进行 IPC 通信?
Binder机制中存在Binder线程池,是多线程的,如果Zygote采用Binder的话就存在上面说的fork()与多线程的问题了。其实严格来说,Binder机制不一定要多线程,所谓的Binder线程只不过是在循环读取Binder驱动的消息而已,只注册一个Binder线程也是可以工作的,比如service manager 就是这样的。实际上Zygote尽管没有采取Binder机制,它也不是单线程的,但它在fork()前主动停止了其他线程,fork()后重新启动了。
六、Binder有什么优势
- 性能方面
共享内存 0次数据拷贝 Binder 1次数据拷贝 Socket/管道/消息队列 2次数据拷贝
- 稳定性方面
Binder:基于C/S架构,客户端 (Client) 有什么需求就丢给服务端 (Server) 去完成,架构清晰、职责明确又相互独立,自然稳定性更好 共享内存:虽然无需拷贝,但是控制复杂,难以使用 从稳定性的角度讲,Binder机制是优于内存共享的。
- 安全性方面
传统的IPC没有任何安全措施,安全依赖上层协议来确保。 传统的IPC方法无法获得对方可靠的进程用户ID/进程UI (UID/PID) ,从而无法鉴别对方身份。 传统的IPC只能由用户在数据包中填入UID/PID,容易被恶意程序利用。 传统的IPC访问接入点是开放的,无法阻止恶意程序通过猜测接收方地址获得连接。 Binder既支持实名Binder,又支持匿名Binder,安全性高。
七、跨进程通信的几种方式
1 Intent 比如拨打电话 2 ContentProvider 数据库存储数据 (底层同样也是Binder,专门用于不同应用间共享数据。支持一对多进程数据 共享) 3 Broadcast 广播通信 4 ALDL 通信,通过接口共享数据 (支持一对多通信,支持RPC)
八、多进程应用使用场景
1 需要向其他应用获取数据 2 某些应用模块需要单独运行在进程中(WebView优化,后台进程) 3 加大一个应用可使用的内存(获取多份内存空间)
九、Binder 机制 (进程间的通信)
Binder 是 Android 为我们提供的 IPC(Inner Process Commnuication进程间通信)的一种方式,Android 中的 。
Activity 、Service、Broadcast、ContentProvider 都是运行在不同的进程中,Binder 是他们之间进行通信的桥梁。
原理:
首先要定义要传输的对象,并进行序列化,反序列化,然后在相同包名下定义 AIDL 接口、要传输的对象,然后定义 好了以后,
再 makeProject ,生成对应的类,然后再创建一个 Service 写服务端的代码,然后再客户端使用 bindService 方法进行连接,
再 ServiceConnection 的回调方法中拿到服务端的接口对象,之后就可以往服务端发送消息。
十、说一说 ANR
在 Android 系统中,如果应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程 序无响应(ANR:ApplicationNotResponding)对话框。即页面无响应, 用户可以选择让程序继续运行,但是,他 们在使用你的应用程序时,并不希望每次都要处理这个对话框。因此 ,在程序里对响应性能的设计很重要,这样系 统就不会显示 ANR 给用户。
触发条件:
Activity:5 秒。应用在 5 秒内未响应用户的输入事件(如按键或者触摸)
BroadCastReceiver :10 秒。BroadcastReceiver 未在 10 秒内完成相关的处理
Service:20 秒(均为前台)。Service 在20 秒内无法处理完成
引起条件:
主线程被 IO 操作(从 4.0 之后网络 IO 不允许在主线程中)阻塞;
主线程中存在耗时的计算;
主线程中错误的操作,比如 Thread.wait 或者 Thread.sleep 等。
查看方式
C盘: /data/anr/traces.txt
避免建议:
将所有耗时操作,比如访问网络,Socket 通信,查询大量 SQL 语句,复杂逻辑计算等都放在子线程中去,然后通过
handler.sendMessage、runOnUIThread、 AsyncTask、RxJava 等方式更新 UI。无论如何都要确保用户界面的流畅
度。如果耗时操作需要让用户等待,那么可以在界面上显示度条。
十一、什么是OOM,如何避免解决
oom就是我们常说的Out of Memory内存溢出,它是指需要的内存空间大于系统分配的内存空间,oom后果就是项目程 序crash;
原因:
1、加载大图片导致内存溢出
2、大量内存泄露
3 线程数量过多,队列容量设置过大,导致OOM
解决
1 使用软引用,弱引用,当堆内内存不足时,就可以自动释放缓存的Bitmap对象
2 使用过的图并且不再使用,可以调用Bitmap.recycle()加速回收
3 考虑使用文件缓存
4 使用统一的线程池管理类进行线程管理。
扩展
由于手机设备的限制,一般一个应用使用的内存不能超过默认值 32M(不同设备略有差异,通过adb shell getprop |
grep dalvik.vm.heapgrowthlimit命令查看),这也就是说,当在DVM上申请的堆内存大于默认阀值的时候,我们的应
用就会抛出OutOfMemoryError
十二、内存泄露,内存溢出,内存抖动
内存抖动
原因:大量的对象被创建又在短时间内马上被释放,导致内存使用量上下波动 GC 处理帧率锅过快
解决:避免在循环中创建临时对象 避免在onDraw方法中创建Paint或Bitmap对象 避免在onDraw方法中做耗时操作
内存溢出
原因:内存溢出是指当对象的内存占用已经超出分配内存的空间大小,这时未经处理的异常就会抛出。比如常见的内
存溢出情况有:bitmap过大;引用没释放;资源对象没关闭
1 由于我们程序的失误,长期保持某些资源(如Context)的引用,垃圾回收器就无法回收它,当然该对象占用的内
存就无法被使用,这就造成内存泄露。
2 占用内存较多的对象 保存了多个耗用内存过大的对象(如Bitmap)或加载单个超大的图片,造成内存超出限制。
扩展:
Android中常见就是Activity被引用,在调用finish之后却没有释放,第二次打开activity又重新创建,这样的内存泄露不 断的发生,则会导致内存的溢出。
内存泄漏
原因:有些对象只有有限的生命周期。当它们的任务完成之后,它们将被垃圾回收。如果在对象的生命周期本该结束 的时候,这个对象还被一系列的引用,这就会导致内存泄漏。随着泄漏的累积,app将消耗完内存。(工具 leakcanary)比如当你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的 那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序
问:常见的内存泄露有哪些?如何解决?
1 资源对象没关闭如Cursor,File等资源。他们会在finalize中关闭,但这样效率太低。容易造成内存泄漏
SQLiteCurost,当数据量大的时候容易泄漏
2 使用Adapter时,没有使用系统缓存的converView
3 没有即时调用recycle()释放不再使用的bitmap
4 context引起的内存泄漏
解决:使用application的context来替代activity相关的context,不要让生命周期长于Activity的对象持有到Activity的
引用
5 广播注册没取消造成内存泄露
6 Handler引起的内存泄偶
原因:匿名内部类持有外部类对象,Handler持有当前activity:
解决:应该申明为静态对象, 并在其内部类中保存一个对外部类的弱引用。
7 单例模式中使用Activity作为Context引起的内存泄漏
原因:单例模式中应该避免使用Activity作为传入的Context,否则,单例模式会持有这个Activity的引用,导致它无
法释放,造成内存泄漏。
解决:应该使用ApplicationContext作为Context传入。如果一定要使用Activity的话,要使用弱引WeakReference
8 webview导致的内存泄漏
原因: 开启一个webview 也就是开启了一个线程来展示webview,当activity 加载webview的时候,因activity 的生命周期无法控制Webview ,导致webview 一直持有当前的activity引用不能回收。
解决:1 创建独立的进程
2 动态添加webview,对传入webview中的context 使用弱引用,动态添加webview的意思就是在布局中创建个
viewGroup来放置webview
问:为什么单例模式会持有当前activity的引用,无法释放
单例模式使用的是 static 修饰的,我们知道,static 修饰的对象会一直存在内存中当持有的activity被销毁的时候,但
单例一直持有当前的activity ,并不会被销毁
十三、Webview 常见的坑
1 Android api 16 之前存在远程代码执行安全漏洞,源于开发者没有正确使用Webview.addJavaScriptInterface方法
2 Webview 在布局文件中使用时,在生命周期结束时先要在布局文件中Webview在调用webview的销毁方法
3 后台耗电问题 在关闭webview 页面时要及时销毁
4 硬件加速导致页面渲染问题 关闭当前页面的硬件加速
十四、屏幕旋转后的生命周期
1 用户首次进入页面后执行的生命周期回调方法 onCreate – onStart – onResume
2 屏幕发生旋转后,切换成了横屏,原先的活动被销毁,会重新创建新的活动 onPause – onStop – onDestroy – onCreate – onStart - onResume
3 将横屏切回为竖屏,执行的生命周期回调方法为,横屏的活动被销毁,之后重新创建竖屏的活动界面 onPause – onStop – onDestroy – onCreate – onStart - onResume
最后
由于面试内容比较多,篇幅有限,已经被整理成了PDF指南,有需要2024年Android中高级最全面试真题答案 完整指南的可
更多鸿蒙指南可以关注公众号:Android老皮
目录
第一章 Java方面
- Java基础部分
- Java集合
- Java多线程
- Java虚拟机
第二章 Android方面
- Android四大组件相关
- Android异步任务和消息机制
- Android UI绘制相关
- Android性能调优相关
- Android中的IPC
- Android系统SDK相关
- 第三方框架分析
- 综合技术
- 数据结构方面
- 设计模式
- 计算机网络方面
- Kotlin方面
第三章 音视频开发高频面试题
- 为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?
- 怎么做到直播秒开优化?
- 直方图在图像处理里面最重要的作用是什么?
- 数字图像滤波有哪些方法?
- 图像可以提取的特征有哪些?
- 衡量图像重建好坏的标准有哪些?怎样计算?
第四章 Flutter高频面试题
- Dart部分
- Flutter部分
第五章 算法高频面试题
- 如何高效寻找素数
- 如何运用二分查找算法
- 如何高效解决雨水问题
- 如何去除有序数组的重复元素
- 如何高效进行模幂运算
- 如何寻找最长回文子串
第六章 Andrio Framework方面
- 系统启动流程面试题解析
- Binder面试题解析
- Handler面试题解析
- AMS面试题解析