当前位置:网站首页>2022年互联网大厂Android高频面试题及答案及知识脉络的整理
2022年互联网大厂Android高频面试题及答案及知识脉络的整理
2022-07-21 03:14:00 【yihanss】
前言
这篇文章非常的干!覆盖了Android面试的大多数知识点,值得收藏反复查看!
安逸久了就容易迷失方向,多看看高质量的面试题找找差距,然后查漏补缺!
问题区:
1.Activity的启动过程,AMS、PMS源码
2.View的绘制过程,MeasureSpec测量模式分别代表什么意思,draw是哪里来的?自定义view
3.view的事件分发机制
4.hashmap原理,arraylist,linklist原理
5.你在开发过程中常用设计模式有哪些,单例设计模式的双重校验的目的?去掉第一个判空或第二个判空有啥不同?工厂模式解决了什么问题?使用了哪些设计原则?
6.retrofit,okhttp,rxjava原理,okhttp用到了哪些设计模式,连接池的实现原理,rxjava线程切换的原理,eventbus原理
7.jvm模型,java内存模型,垃圾回收机制,垃圾回收哪个区域,对象在内存哪个区域等等
8.startService和bindService区别,多次启动会调用哪些方法?
9.Activity旋转会调用哪些方法(横竖屏切换)
10.数据结构和算法,比较少会去写,要求手写 冒泡或者快速希尔排序等排序,最少要会一种
11.你都做过哪些内存优化,apk优化等
12.哪些会导致内存泄漏,如何检测,以及解决办法,内存泄漏和溢出有啥不同
13.图片优化,一个大图(10M,100M)如何去展示。
14.一些程序运行的结果,一般考的是重载,多态的,或者各种 i++ ++i 的结果的
15.图片缓存框架的原理,你字迹是否有实现过图片缓存框架,怎么实现的
16.mvp,mvc区别,mvvm有木有了解的?
17.适配方案
18.跨进程通信方式,以及AIDL原理
19.子线程与子线程通信方式,handler怎么去实现子线程之间的通信
20.Message、Handler、MessageQueue、Lopper,以及Looper既然是死循环的,为毛不会导致UI线程的阻塞
21…
答案区:
1.Activity的启动过程,AMS、PMS 源码
1).Activity启动过程:
1.点击APP图标后通过startActivity()远程调用到AMS中,AMS将进启动的Activity以 activityrecord 的结构压入Activity栈中,并通过远程binder回调到进程,使得原进程进入pause状态,原进程pause后通知AMS :“我 pause 了"
2).此时AMS再根据栈中Activity的启动 意图(intent) 中的flag是否含有 new_task 的标签判断是否需要启动新进程(启动新进程调用 startProcessXXX 方法)
3).启动新进程后通过反射调用ActivityThread的main方法,mian方法中调用looper.prepare 和 looper.loop 启动消息队列循环机制。最后远程告知AMS:“我启动了”,然后AMS再回调 handleLauncherActivity() 方法加载Activity,在该方法中通过反射调用Application的onCreate()和Activity的onCreate(),然后在handleResumeActivity() 中反射调用Activity的onResume()方法。
2. View的绘制过程,MeasureSpec测量模式分别代表什么意思,draw是哪里来的?自定义View
- View的绘制过程
关于View的绘制过程,可以简单的分成三个步骤:
1).measure过程:
- 作用:测量View的宽、高
- 流程:performMeasure() — measure() — onMeasure() — 子View的measure()
- 备注:在onMeasure() 方法中会对所有的子元素进行measure过程
2). layout过程:
- 作用:通过确定View四个顶点的位置,从而确定View的位置
- 流程:performLayout() — layout() — onLayout() —子View的layout过程
- 备注:在OnLayout()方法中会对所有子元素进行layout过程
3).draw过程:
- 作用:将View绘制在屏幕上
- 流程:performDraw() — draw() — onDraw() — 子View的draw过程
- MeasureSpec测量模式分别代表什么意思
- MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。
- View在测量过程中会使用到MeasureSpec
其中MeasureSpec有三个测量模式:
1.UNSPECIFIED 模式:父容器不会对子View有限制,子View要多大就给多大
2.EXACTLY 模式:表示精确模式,View的大小已经确定,为SpecSize(规格大小)所指定的值
3.AT_MOST 模式:表示不确定子View的大小,指定一个最大值,子View可在该范围内任意取值设为自己的大小
- draw是哪里来的
View在经过 测量大小(measure过程)和 位置确定(layout过程) 后接下来就是 View的绘制(draw过程)
- 自定义View
3. View的事件分发机制
- 事件的分发机制可以简单的分为
1.当点击屏幕时触发 MotionEvent.ACTION_DOWN事件 时,事件分发器(dispatchTouchEvent)开始对事件的分发
2.在分发器找到对应的View时,拦截器(onInterceptTouch)会对事件的分发进行拦截,停止分发器继续向下分发事件的操作
3.接下来我们通过移动接触屏幕的手指等操作触发 MotionEvent.ACTION_MOVE 或 MotionEvent.ACTION_UP事件这些事件都会回调给onTouch或onTouchEvent方法,对事件做出响应
- 分发机制的流程:Activity → ViewGroup → view
- 分发事件最开始从 Actvity的dispatchTouchEvent()方法 开始:
1.在该方法里面最主要的是判断 getWindow().superDispatchTouchEvent() 方法的返回结果(true或false),若返回true则点击事件停止传递,传递过程结束,即找到了对应的View 。其中 getWindow.superDispatchTouchEvent() 方法实际会调用 ViewGroup层的dispatchTouchEvent() 方法。
2.在ViewGroup层会判断是否对事件进行拦截,
- 若为true,则对事件进行拦截,然后调用 父类的dispatchTouchView() 方法,同时回调自身的onTouch()方法。
- 若为false,则遍历子View,找到点击对应的View。然后拦截分发,同时调用 View控件的dispatchTouchEvent
3.在View控件的 dispatchTouchEvent() 方法中主要对View控件进行3个条件的判断:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED\_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
- 条件1 mOnTouchListener !=null:即我们只要给View控件注册了Touch事件,mOnATouchListener就不为空
- 条件 2 (mViewFlags & ENABLED_MASK) == ENABLED:判断当前View控件是否enable(很多View默认enable)
- 条件 3 mOnTouchListener.onTouch(this,event):即控件注册Touch事件时的onTouch()
由此可见,只有给View控件添加点击事件同时在点击事件中的onTouch()返回true时,dispatchTouchEvent()方法才能返回true,分发才结束。否则进入onTouchEvent()方法。
4. HashMap、ArrayList、LinkList原理
5. 你在开发过程中常用设计模式有哪些,单例设计模式的双重校验的目的?去掉第一个判空或第二个判空有啥不同?工厂模式解决了什么问题?使用了哪些设计原则?
- 设计模式
- 单例模式
- Build建造者模式
- 观察者模式
- 原型模式
- 策略模式
- 工厂模式
- 单例模式双重校验的目的
在说单例的双重校验目的之前,先看一下单例的双重校验长什么样子
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
可以看到在getInstance()方法里面,先判断当前实例是否为空,然后进入同步处理后又判断一次实例是否为空。前后两次判断校验了两次。这个就是双重校验
去掉第一个判断为空:即懒汉式(线程安全),这会导致所有线程在调用getInstance()方法的时候,不管三七二十一就直接排队等待同步锁,然后等到排到自己的时候进入同步处理时,才去校验实例是否为空,这样子做会耗费很多时间(即线程安全,但效率低下)。
//去掉判断第一个为空
public static synchronized SingleTon getInstance(){
if (instance==null){
instance = new SingleTon();
}
return instance;
}
去掉第二个判断为空:即懒汉式(线程不安全),这会出现 线程A先执行了getInstance()方法,同时线程B在因为同步锁而在外面等待,等到A线程已经创建出来一个实例出来并且执行完同步处理后,B线程将获得锁并进入同步代码,如果这时B线程不去判断是否已经有一个实例了,然后直接再new一个。这时就会有两个实例对象,即破坏了设计的初衷。(即线程不安全,效率高)
//去掉第二个判断为空
public static SingleTon getInstance(){
if (instance==null){
instance = new SingleTon();
}
return instance;
}
双重校验的目的:除了第一次实例化需要进行加锁同步,之后的线程只要进行第一层的if判断不为空即可直接返回,而不用每一次获取单例都加锁同步,因此相比前面两种懒汉式,双重检验锁更佳。(双重校验锁结合了 两种懒汉式 的优点)
6. retrofit,okhttp,rxjava原理,okhttp用到了哪些设计模式,连接池的实现原理,rxjava线程切换的原理,eventbus原理
retrofit 、 okhttp 、 rxjava原理:…
7. jvm模型,java内存模型,垃圾回收机制,垃圾回收哪个区域,对象在内存哪个区域等等
- jvm模型、java内存模型:
- 垃圾回收机制:
主要分为两个步骤:
- 检测垃圾
- 回收垃圾
检测垃圾又有两种:
- 引用计数法(已过时)
给对象一个添加一个引用计数器,每当有一个地方引用该对象时,计数器加1,反之当引用无效时,计数器减1 。在任何时候,当计数器为0时,即没有任何地方引用该对象,表示该对象无用,即为回收器回收的对象。(因为这个方法无法解决对象之间相互循环引用的问题,所以被淘汰。) - 可达性分析
通过GC root根节点往外遍历(可以想象树形图),当一个与root根节点可达的节点A所代表的对象持有另外一个节点B所代表的对象的引用,则视节点B为可达的。反之,如果某个节点是不可达的,则为可回收的对象。
回收垃圾
- 标记 - 清除法(mark - sweep)
标记所有需要回收的对象,然后统一清除。该方法简单粗暴,但是清除完会导致内存空间中出现大量碎片。 - 复制(copying)
把内存中的空间平分为两个,然后每次只使用任意一个。当回收垃圾时,遍历当前该内存区域,将正在使用的对象复制到另外一个内存区域中(复制过来后会自动整理,不会出现碎片的问题),然后再清空原来的内存区域。该方法通过两个内存区域的方法解决了碎片的问题,同时又迎来了新的问题,即提高了内存的空间要求,舍弃了空间换取了效率。 - 标记 - 整理(mark - compact)
第一阶段:从根节点标记所有能被引用的对象,即标记有用的对象。
第二阶段:遍历整个堆中的对象,清除没有被标记的对象,并把剩下的 “压缩” 到堆中的其中一块,按顺序排放。
该方法避免了 “ 标记 - 清除 ” 所造成的碎片问题,也解决了 “ 复制 ” 对空间的要求高的问题。 - 分代收集算法
根据每个对象生命周期不同的特点,将对象划分到不同代上,使用不同的垃圾回收方式。
新生代:新创建的对象都是使用新生代分配内存。新生代里面又有三个区域(1个Eden区和2个Survivor区),新建的对象会放再Eden区,当Eden区满了就会执行 Minor GC ,然后把存活的对象转移到任意一个Survivor区。
老年代:经过多次 Minor GC后依然存活的对象便送到该代,当该代内存被占满时就会触发Full GC回收整个内存。
持久代:顾名思义。永生不死,相当于吸血鬼。用于存放java类等
- 垃圾回收在哪个区域:
- 要了解垃圾回收到底是回收哪个区域,就得先了解JAVA内存管理
- 内存的管理即对对象的分配和释放,释放即回收。
JAVA内存分配策略
1. 静态分配:主要存在静态变量,这块在编译时就已经分配好了,在整个程序运行期间存在。
2. 栈式分配:当方法被执行时,方法体内部的局部变量(基本数据类型,对象的引用)都会放进栈内存中。当方法执行结束,分配给该方法的内存空间也会被释放。
3. 堆式分配:又称动态分配,通常指对象的实例,这部分内存在不用的时候会被GC回收。
通过上面三个分配策略可知,静态分配在整个程序运行过程中都在存在,栈式分配的内存在方法体执行结束后会自动释放。使用这两种分配策略的对象都不用进行回收,只有使用堆式分配的对象需要进行GC回收。
8. startService和bindService区别,多次启动会调用哪些方法?
- startService和bindService的区别
startService:
作用:启动服务
生命周期:onCreate() → onStartCommand() → onDestory()
bindService:
作用:启动服务
生命周期:onCreate() → onBind() → onUnbind() → onDestory() - 区别:
- 从通讯角度看,使用startService()方法启动的服务不能与Activity进行通讯,而使用bindService()方法启动的服务可以与Activity进行通讯。
- 从生命周期看,startService()方法启动服务是通过startCommand()方法,而bindService()方法是通过onBind()方法。
- 通过startService()方法启动的服务,当调用者退出后,服务仍然可以运行,而使用bindService()方法启动的服务则不行。
- onCreate()方法在生命周期中只调用一次,若在服务已经启动的前提下,多次调用startService()方法或者调用bindService()方法,都不会再执行onCreate()方法,在使用starService()方法启动服务的情况下,会多次调用onStart()方法。
9. Activity旋转会调用哪些方法
Activity横竖屏切换的生命周期根据清单配置文件中的属性“ android:configChanges ”的值的不同而不同。
android:configChanges =" orientation "消除横竖屏的影响
android:configChanges=" keyboardHidden " :消除键盘的影响
android:configChanges=" screenSize " :消除屏幕大小的影响
情况1:**当android:configChanges =" orientation " 或者_android:configChanges =" orientation | keyboardHidden "或者不设置该属性时 **,其切换屏幕的生命周期如下:
onPause() → onSaveInstanceState() → onStop() → onDestory() → onCreate() → onStart() → onRestoreInstanceState() → onResume()
情况2:当_android:configChanges=" orientation | screenSize | keyboardHidden "时,其切换屏幕不会调用任何一个生命周期方法。
情况3:当_android:configChanges=" orientation | screenSize "时,其切换屏幕时不会调用任何一个生命周期方法,而是调用onConfigurationChanged()方法。
10.数据结构和算法,比较少会去写,要求手写 冒泡或者快速希尔排序等排序,最少要会一种
排序相对基础一点,这里本着复习的目的,就贴出冒泡排序的代码。使用Ecplise写的
Scanner sd =new Scanner(System.in);
String\[\] temp=sd.nextLine().split(" ");
//这里就是排序的代码
for(int i=0;i<temp.length;i++) {
for(int j=temp.length-1;j>i;j--) {
if(Integer.parseInt(temp\[j\])<Integer.parseInt(temp\[j-1\])) {
String str=temp\[j\];
temp\[j\]=temp\[j-1\];
temp\[j-1\]=str;
}
}
}
//这里是遍历打印出来
for(String str:temp) {
System.out.print(str+" ");
}
文末
有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
如果有需要下面的请扫描下面二维码即可领取~
《2022中高级Android面试必知百题》
第一章 Java 方面
第一节 Java 基础部分
- 抽象类与接口的区别?
- 分别讲讲 final,static,synchronized关键字可以修饰什么,以及修饰后的作用?
- 请简述一下String、StringBuffer和StringBuilder的区别?
- “equals”与“==”、“hashCode”的区别和使用场景?
- Java 中深拷贝与浅拷贝的区别?
- …
第二节 Java 集合
- 谈谈List,Set,Map的区别?
- 谈谈ArrayList和LinkedList的区别?
- 请说一下HashMap与HashTable的区别
- 谈一谈ArrayList的扩容机制?
- …
第三节 Java 多线程
- Java 中使用多线程的方式有哪些?
- 说一下线程的几种状态?
- 如何实现多线程中的同步?
- 谈谈线程死锁,如何有效的避免线程死锁?
- …
第四节 Java 虚拟机
- 谈一谈JAVA垃圾回收机制?
- 回答一下什么是强、软、弱、虚引用以及它们之间的区别?
- 简述JVM中类的加载机制与加载过程?
- JVM、Dalvik、ART三者的原理和区别?
- …
第二章 Android 方面
第一节 Android 四大组件相关
- Activity 与 Fragment 之间常见的几种通信方式?
- LaunchMode 的应用场景?
- BroadcastReceiver 与LocalBroadcastReceiver 有什么区别?
- 对于 Context,你了解多少?
- IntentFilter是什么?有哪些使用场景?
- 谈一谈startService和bindService的区别,生命周期以及使用场景?
- …
第二节 Android 异步任务和消息机制
- HandlerThread 的使用场景和用法?
- IntentService 的应用场景和使用姿势?
- AsyncTask的优点和缺点?
- 谈谈你对 Activity.runOnUiThread 的理解?
- 子线程能否更新UI?为什么?
- …
第三节 Android UI 绘制相关
- Android 补间动画和属性动画的区别?
- Window和DecorView是什么?
- 简述一下 Android 中 UI 的刷新机制?
- LinearLayout, FrameLayout,RelativeLayout 哪个效率高, 为什么?
- 谈谈Android的事件分发机制?
- 谈谈自定义View的流程?
- …
第四节 Android 性能调优相关
- 谈谈你对Android性能优化方面的了解?
- 一般什么情况下会导致内存泄漏问题?
- 自定义 Handler 时如何有效地避免内存泄漏问题?
- 哪些情况下会导致oom问题?
- ANR 出现的场景以及解决方案?
- 谈谈Android中内存优化的方式?
- …
第五节 Android 中的 IPC
第六节 Android 系统 SDK 相关
- 请简要谈谈Android系统的架构组成?
- SharedPreferences 是线程安全的吗?它的 commit 和 apply 方法有什么区别?
- Serializable和Parcelable的区别?
- 请简述一下 Android 7.0 的新特性?
- 谈谈ArrayMap和HashMap的区别?
- …
第七节 第三方框架分析
- 谈一谈LeakCanray的工作原理?
- 谈一谈EventBus的原理?
- 谈谈网络请求中的拦截器(Interceptor)?
- 谈一谈Glide的缓存机制?
- ViewModel的出现是为了解决什么问题?并简要说说它的内部原理?
- …
第八节 综合技术
- 请谈谈你对 MVC 和 MVP 的理解?
- 分别介绍下你所知道Android的几种存储方式?
- 简述下热修复的原理?
- 谈谈如何适配更多机型的?
- 请谈谈你是如何进行多渠道打包的?
- …
第九节 数据结构方面
- 什么是冒泡排序?如何优化?
- 请用 Java 实现一个简单的单链表?
- 如何反转一个单链表?
- 谈谈你对时间复杂度和空间复杂度的理解?
- 谈一谈如何判断一个链表成环?
- …
第十节 设计模式
- 请简要谈一谈单例模式?
- 对于面向对象的六大基本原则了解多少?
- 请列出几种常见的工厂模式并说明它们的用法?
- 说说项目中用到的设计模式和使用场景?
- 什么是代理模式?如何使用?Android源码中的代理模式?
- …
第十一节 计算机网络方面
- 请简述 Http 与 Https 的区别?
- 说一说https,udp,socket区别?
- 请简述一次http网络请求的过程?
- 谈一谈TCP/IP三次握手,四次挥手?
- 为什么说Http是可靠的数据传输协议?
- TCP/IP协议分为哪几层?TCP和HTTP分别属于哪一层?
第十二节 Kotlin方面
- 请简述一下什么是 Kotlin?它有哪些特性?
- Kotlin 中注解 @JvmOverloads 的作用?
- Kotlin中List与MutableList的区别?
- Kotlin中实现单例的几种常见方式?
- 谈谈你对Kotlin中的 data 关键字的理解?相比于普通类有哪些特点?
- 什么是委托属性?请简要说说其使用场景和原理?
边栏推荐
- Advanced C language (XIV) - Document Management
- 05 正则表达式语法
- What is the difference between session and cookies?
- Unity—Input类
- Filter listener
- Translation and interpretation of the paper: anytime bottom up rule learning for knowledge graph completion [anyburr]
- spark rdd,spark SQL的应用案例
- [experience sharing] mathematical modeling paper format requirements and summary of common problems
- Pytorch环境搭建
- 软件测试面试题:您认为在测试人员同开发人员的沟通过程中,如何提高沟通的效率和改善沟通的效果?维持测试人员同开发团队中其他成员良好的人际关系的关键是什么?
猜你喜欢
Clickhouse Fundamentals
【性能优化】MySQL常用慢查询分析工具
Usage of readshow database + National Library Reference Alliance
nexus 5X刷机并使用Magisk获取root权限
Arduino I2C for TCA9548A应答扫描程序
SuperMap iClient for OpenLayers图层组控制实现方法
How to realize copyright protection of Digital Collections
Configuration du Service FTP
Ardunio开发——I2C协议通讯——控制2x16LCD
FTP service configuration
随机推荐
Light the first LED
软件测试面试题:测试人员在软件开发过程中的任务是什么?
[QT入门篇]窗口类的应用
软件测试面试题:引用与指针有什么区别?
软件测试面试题:BUG管理工具的跟踪过程(用BugZilla为例子)
Pytoch environment construction
Ardunio开发——土壤传感器的使用
Emuelec Development Notes
How easyexcel exports files in a project
Brush notes - find
[information retrieval] implementation of information retrieval system
El table supports paging multiple selection
Features and choices among QT4, 5 and 6 versions
FTP服务配置
软件测试面试题:测试计划工作的目的是什么?测试计划文档的内容应该包括什么?其中哪些是最重要的?
Ardunio development - use of soil sensors
红枣科技CEO月度DDC说明会(第三期)——DDC网络新功能介绍及文昌链升级计划 精华回顾
ThreadLocal
C language introduction practice (10): module taking problem
KingbaseES V8R6有什么办法,默认不读取sys_catalog下的系统视图?