Android面试题整理
为面试积累经验,很多都是平时用不到,但面试考人的知识点
目录
- 数据结构和算法
- Java 相关
- Android 相关
- Android Jetpack
- Kotlin 相关
- 其他
数据结构和算法
数组:
一组相同的数据组成,存储在连续的空间内,用索引可以找到元素地址,包含一维数组和多维数组,也最常用。
链表:
使用一组节点来表示一个数列,包含头和尾节点,不是循环结构,所以尾就是终止。
双向链表:
链表的每个节点有一组前后指针,前后指针分别指向前驱结点和后继节点,最后一个节点的后继节点即指向第一个节点。
堆栈:
后进先出,最后一个入栈的第一个出栈,一个下压结构
队列:
先进先出,最后一个入栈的最后一个出栈,一个线性结构
Java相关
抽象:
一个类没有包含足够的信息,不是一个具体的类,就是抽象类
继承:
用TextView举例,他是继承自View,拥有View的特征,但也实现了自己独特的方法,简而言之就是继承并拥有自己独特的部分
封装:
降低耦合,良好的封装可以更好的维护代码
多态:
相同的父类实现不同的状态,继承、重写、向上转型
接口:
一个蓝图(契约)所有实现接口的类的共同模板
序列化:
将对象转化为字节流,目的是将对象存储到内存里,等后面再构建的时候就能获取到之前的对象信息了,在Java里使用Serializable接口,而在Android中应该使用Parcelable,因为Serializable使用反射机制,会频繁触发垃圾回收,相比而言Parcelable更高效
单例:
一个类只初始化一次,只有一个实例
==和equals对比字符串:
==比较字符串地址,String的equals是对比字符串内容
Java内存泄露:
1. 静态集合类引起的;
2. 监听器;
3. 各种数据连接;
4. 单例;
Integer和int的区别:
integer是int的封装类,而int是java的基础类型,Integer是对象默认为null,int是基本数据类型默认为0
Java中强引用,软引用,弱引用:
1. 强引用:不会被GC轻易回收,只要引用存在就永远不会回收;
2. 软引用:非必要引用,内存溢出前会回收;
3. 弱引用:第二次垃圾回收时回收
synchronized的作用:
通过synchronized设置同步锁,每当有线程需要访问时,线程会首先获取这个对象的锁,并在成功获取到对象锁后才能正常访问,访问过程中其他线程无法获得该锁,也就是会优先保证持有锁的代码执行完毕
Android相关
Activity生命周期:
- onCreate Activity初始化工作
- onStart将Activity展现给用户
- onResume Activity与用户交互的时候触发
- onPause Activity转换到后台时触发,在这里可以进行状态持久化
- onStop Activity终止,但没有销毁
- onRestart Activity重新展示给用户时触发
- onDestroy Activity销毁时触发,如果内存紧张时会直接结束而不会触发
简单说说四大组件:
-
Activity:
所有程序流程都运行在Activity上,整个Android程序中最基本的模块之一,一般一个程序有多个Activity组成,概念和网页类似 -
Service:
级别和Activity差不多,但不能自主运行,只能待在后台,Service是没有界面的长维持代码 -
BroadcastReceiver:
广播接收器,可以用来接收其他应用程序或应用程序本身发出的广播,相对的发送广播可以使用sendBroadcast进行,既可以在manifest注册,也可以在registerReceiver注册 -
Content Provider:
第三方数据访问方案,使用uri作为访问标识,提供访问接口
Activity的LaunchMode的特点
-
standard:默认启动模式,启动新activity时会创建新的实例
-
singleTop:栈顶复用模式,如果该Activity存在栈顶,则不会创建新的而是会调用onNewIntent方法,避免重复创建,如果不在栈顶还是会被创建,一般常见于社交应用中的通知栏行为功能。
-
singleTask:栈内复用模式,栈中仅存在一个实例,只要存在栈里就不会创建新的,而是复用已经存在的,并且调用onNewIntent,一般用作应用的首页,例如浏览器主页
-
singleInstance:设置了这个后只能存在这一个Activity,可以用来不影响用户行为的应用,比如闹钟
LaunchMode特点补充(根据扔物线大佬的视频学习而来):
Task的概念非常重要,他的结构是一个典型的栈结构,遵循先进后出原则,一个应用通常只有一个栈,但也可以通过在清单文件中指定activity运行的栈,默认的栈即应用的包名。
如果设置standard和singleTop本质上是在一个应用栈上操作的,设置standard时新启动的activity会堆叠在这个栈上,回退依次出栈,singleTop本质上也是这个原理,但设置后他会清空该栈目标以上的activity,保证其位于栈顶,并回调onNewIntent方法刷新页面数据。
singleTask和singleInstance这两个略有不同,他们即可以运行在当前应用栈也可以运行在其他应用栈中,来保证其全局的Activity唯一性。
这里还需要了解到前台栈和后台栈的概念,当切换到应用抽屉或点击home键回到桌面时,前台栈会转换为后台栈,当两个栈位于前台时,前一个结束会自动切换到下一个栈,但如果下一个是后台栈,则会直接回到首页。
设置singleTask后,栈中仅存在一个实例,只要存在栈里就不会创建新的,而是复用已经存在的,并且调用onNewIntent,而设置singleInstance后,该栈中仅有一个Activity,Activity结束栈即关闭,要根据不同的业务需求选择不同的启动模式。
阐述一下Activity和Fragment的区别:
-
生命周期不同
Activity的生命周期
onCreate→onStart→onResume→onPause→onStop→onDestroy
Fragment的生命周期
onAttach→onCreate→onCreateView→onActivityCreated→onStart→onResume→onPause→onStop→onDestoryView→onDestory→onDetach -
相比而言fragment更轻,他依赖与Activity可以直接通过fragment标签定义在xml文件里,也支持通过fragment事务动态更换。
-
Activity总是一个一个的单独存在,数个Fragment依附于一个Activity,如果Activity销毁所属的Fragment也将销毁
Android中的内存泄漏是怎么引起的:
内存泄漏的本质是本该被回收的对象因为某些原因,没有被回收还停留在堆内存里。
- 如果持有该对象的强引用,该对象是不会被回收的,最常见的就是Context,比如Activity的Context就包含了大量的引用,万不可把Context或View设为static;
- 内部类持有Activity引用,导致Activity;
- 监听器绑定后没有及时注销,引起泄露;
- Bitmap对象没有及时释放,在使用完后需要及时释放
Service类型和启动方式:
- Service类型
Service分为前台和后台,前台会通过通知的形式让用户感知,如音乐播放通知、消息通知,让用户知道有这么个东西在,在Android8.0,Google要求如果程序在后台,那么就不能创建后台服务,已经开启的后台服务会在一定时间后被停止。后台服务使用JobScheduler,他仅在一个特定条件下去完成任务。 - Service启动方式
启动方式分为startService和bindService,通过startService启动后即在后台无限期运行,即使启动组件被销毁也不会影响他;通过bindService启动时,当与其绑定的组件取消绑定,service将停止。在Android8.0后推荐使用startForeground启动前台Service。
Android 触摸事件如何传递:
由Activity向下传递,经过ViewGroup到View,如果onInterceptorTouchEvent为true则表示拦截事件,进行处理,如果onTouchEvent事件为true则表示事件消费,如果到最后都不处理就会返回到Activity的onTouchEvent处理,整体是向下传递的过程。
View绘制流程:
- OnMeasure:先测量出视图大小,从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure
- OnLayout:确定View的位置,进行页面的布局,从顶层父View向子View的递归调用View.Layout方法的过程
- OnDraw: ViewGroup创建一个Canvas对象,调用OnDraw()方法进行绘制
Android同/跨进程通信方式:
同进程
intent、broadcast、事件总线、接口、存储机制等
跨进程
跨进程调用其他activity、ContentProvider、broadcast
app冷启动优化:
检查冷启动时间:在logcat中搜索Displayed,此值表示启动过程和完成在屏幕上绘制相应活动之间所经过的时间量
优化方案:
- 优化闪屏页,通过设置windowBackground图片,将白屏/黑屏抹去,用户点击App的图标就展示启动图,让用户先产生启动很快的“错觉”
- Application优化,一些不用在主线程里初始化的SDK,放到子线程去初始化,一些能放到启动页生命周期中初始化的可以延迟,一些用的时候再初始化的
- 优化启动页
- layout层级优化,避免多层级布局,针对特定业务初始化组件
- 避免IO操作阻塞主线程
- 注意Bitmap和图片加载,避免因图片过大而引起启动缓慢
- 启动图可以使用VectorDrawable矢量图,利用矢量图省时间空间的特点
- 注意Activity的生命周期回调,可以将网络请求放到onWindowFocusChanged回调中,这是真正的所有组件初始化完成回调
handler线程切换原理:
Handler的主要作用就是将一个任务切换到指定的线程中去执行。
首先主线程和子线程共用一个looper,在主线程中创建Handler时在构造方法里传入了当前线程的looper,所以子线程获得了主线程的looper,进而获得消息队列,并可以插入消息,最后主线程就可以从消息队列中获取到子线程传来的Message了。
onSaveInstanceState调式时机和恢复数据方法
避免系统回收activity导致数据丢失,用来保存和恢复数据时回调
调用时机
- 用户按下Home键时;
- 长按Home键或菜单键时;
- 手机息屏;
- 打开新Activity时;
- 横竖屏切换时;
恢复数据
onRestoreInstanceState只有在activity确实是被系统回收,重新创建activity的情况下才会被调用,如果被调用了,则说明activity被回收。
onCreate中也有bundle参数,但onSaveInstanceState不一定被调用,所以在恢复数据的时候需要非空判断,而onRestoreInstanceState回调时其bundle值一定不为空。
应用log捕捉
- 定义CrashHandler处理线程抛出的异常,并保存在本地
Android Jetpack
Lifecycle
实现和activity、fragment生命周期感知的框架,实现数据层和view层销毁的时候解绑。原理是Lifecycler为每个活动组件添加了一个没有界面的Fragment,利用Fragment周期会根据活动声明周期变化的特性实现的特性,从而实现生命周期的感知,然后根据注解的Event查找执行相应的方法。
LiveData
提供了一种数据改变的同时,主动去告诉ui,让ui层做出相应的逻辑判断。原理是内部保存了LifecycleOwner和Observer,利用LifecycleOwner感知并处理声明中期的变化,Observer在数据改变时遍历所有观察者并回调方法。
ViewModel
它是我们view层和model层的桥梁,是数据驱动界面的关键地方,只有在Activity或Fragment真正销毁的时候数据才会消失,不会因为横竖屏切换等状态丢失数据。
Room
Google官方提供的基于SQLite的ORM数据库方案,通过将数据实体映射到数据库进行操作,相较于GreenDAO他的特点是通过SQL语句操控CRUD,相比而言我更喜欢使用GreenDAO那样通过函数控制的。
DataBinding
数据绑定避免频繁的findViewById,大大简化代码结构和模板代码编写,并且可以通过他实现双向数据绑定还挺不错的,只是只能改变组件的值而不能改变其本身。
Navigation
谷歌后来推出的管理Fragment跳转的框架,可以在xml中配置fragment间的关系,也可以引入SafeArgs来安全传值,相比操作fragment事务他的中心思想更像activity跳转。
Hilt
Google官方推出的基于Dagger的依赖注入框架,减少我们创建对象的次数,降低耦合,采用@Inject、@Bind、@Providers注解添加依赖,类似Spring中的@Autowire。
Kotlin 相关
说说协程:
- 协程:切线程,通过withContext(Dispatchs.XX)指定运行的线程
- 挂起:自动切回来的切线程,可以通过suspand指定挂起函数,suspand本身只作为提示作用,提示该函数是挂起函数,一般用在CPU耗时操作和IO耗时操作上
- 非阻塞式:协程可以用看起来阻塞的办法来写出非阻塞式的方法,java中的线程本质上也是非阻塞式的,只是在单线程中他阻塞,而kotlin的协程因为会自动切回来所以是非阻塞式的。
什么是 const? 和val有什么区别?
Val是在运行时被 设置, 加一个const 修饰符在val上将会变成编译时常量。 Const不能修饰var,不能用于局部变量
Lazy和lateinit的区别
两者都延迟属性的初始化 lateinit 是用于var的修饰符, 可以用于 晚点来进行赋值。 Lazy更像一个方法或者一个lambda表达式。只用于val。 在需要时被创建。
文章参考:
stormzhang/android-interview-questions-cn: 最全面的高质量 Android 面试指南。 (github.com)