面试Android

Android面试题整理

2022-06-02  本文已影响0人  鲨鲨指挥官

为面试积累经验,很多都是平时用不到,但面试考人的知识点

目录

数据结构和算法

数组:

一组相同的数据组成,存储在连续的空间内,用索引可以找到元素地址,包含一维数组和多维数组,也最常用。

链表:

使用一组节点来表示一个数列,包含头和尾节点,不是循环结构,所以尾就是终止。

双向链表:

链表的每个节点有一组前后指针,前后指针分别指向前驱结点和后继节点,最后一个节点的后继节点即指向第一个节点。

堆栈:

后进先出,最后一个入栈的第一个出栈,一个下压结构

队列:

先进先出,最后一个入栈的最后一个出栈,一个线性结构

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生命周期:

  1. onCreate Activity初始化工作
  2. onStart将Activity展现给用户
  3. onResume Activity与用户交互的时候触发
  4. onPause Activity转换到后台时触发,在这里可以进行状态持久化
  5. onStop Activity终止,但没有销毁
  6. onRestart Activity重新展示给用户时触发
  7. onDestroy Activity销毁时触发,如果内存紧张时会直接结束而不会触发

简单说说四大组件:

Activity的LaunchMode的特点

LaunchMode特点补充(根据扔物线大佬的视频学习而来):

Task的概念非常重要,他的结构是一个典型的栈结构,遵循先进后出原则,一个应用通常只有一个栈,但也可以通过在清单文件中指定activity运行的栈,默认的栈即应用的包名。
如果设置standard和singleTop本质上是在一个应用栈上操作的,设置standard时新启动的activity会堆叠在这个栈上,回退依次出栈,singleTop本质上也是这个原理,但设置后他会清空该栈目标以上的activity,保证其位于栈顶,并回调onNewIntent方法刷新页面数据。
singleTask和singleInstance这两个略有不同,他们即可以运行在当前应用栈也可以运行在其他应用栈中,来保证其全局的Activity唯一性。
这里还需要了解到前台栈和后台栈的概念,当切换到应用抽屉或点击home键回到桌面时,前台栈会转换为后台栈,当两个栈位于前台时,前一个结束会自动切换到下一个栈,但如果下一个是后台栈,则会直接回到首页。
设置singleTask后,栈中仅存在一个实例,只要存在栈里就不会创建新的,而是复用已经存在的,并且调用onNewIntent,而设置singleInstance后,该栈中仅有一个Activity,Activity结束栈即关闭,要根据不同的业务需求选择不同的启动模式。

阐述一下Activity和Fragment的区别:

  1. 生命周期不同
    Activity的生命周期
    onCreate→onStart→onResume→onPause→onStop→onDestroy
    Fragment的生命周期
    onAttach→onCreate→onCreateView→onActivityCreated→onStart→onResume→onPause→onStop→onDestoryView→onDestory→onDetach

  2. 相比而言fragment更轻,他依赖与Activity可以直接通过fragment标签定义在xml文件里,也支持通过fragment事务动态更换。

  3. Activity总是一个一个的单独存在,数个Fragment依附于一个Activity,如果Activity销毁所属的Fragment也将销毁

Android中的内存泄漏是怎么引起的:

内存泄漏的本质是本该被回收的对象因为某些原因,没有被回收还停留在堆内存里。

  1. 如果持有该对象的强引用,该对象是不会被回收的,最常见的就是Context,比如Activity的Context就包含了大量的引用,万不可把Context或View设为static;
  2. 内部类持有Activity引用,导致Activity;
  3. 监听器绑定后没有及时注销,引起泄露;
  4. Bitmap对象没有及时释放,在使用完后需要及时释放

Service类型和启动方式:

Android 触摸事件如何传递:

由Activity向下传递,经过ViewGroup到View,如果onInterceptorTouchEvent为true则表示拦截事件,进行处理,如果onTouchEvent事件为true则表示事件消费,如果到最后都不处理就会返回到Activity的onTouchEvent处理,整体是向下传递的过程。

View绘制流程:

  1. OnMeasure:先测量出视图大小,从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure
  2. OnLayout:确定View的位置,进行页面的布局,从顶层父View向子View的递归调用View.Layout方法的过程
  3. OnDraw: ViewGroup创建一个Canvas对象,调用OnDraw()方法进行绘制

Android同/跨进程通信方式:

同进程
intent、broadcast、事件总线、接口、存储机制等
跨进程
跨进程调用其他activity、ContentProvider、broadcast

app冷启动优化:

检查冷启动时间:在logcat中搜索Displayed,此值表示启动过程和完成在屏幕上绘制相应活动之间所经过的时间量
优化方案:

  1. 优化闪屏页,通过设置windowBackground图片,将白屏/黑屏抹去,用户点击App的图标就展示启动图,让用户先产生启动很快的“错觉”
  2. Application优化,一些不用在主线程里初始化的SDK,放到子线程去初始化,一些能放到启动页生命周期中初始化的可以延迟,一些用的时候再初始化的
  3. 优化启动页

handler线程切换原理:

Handler的主要作用就是将一个任务切换到指定的线程中去执行。
首先主线程和子线程共用一个looper,在主线程中创建Handler时在构造方法里传入了当前线程的looper,所以子线程获得了主线程的looper,进而获得消息队列,并可以插入消息,最后主线程就可以从消息队列中获取到子线程传来的Message了。

onSaveInstanceState调式时机和恢复数据方法

避免系统回收activity导致数据丢失,用来保存和恢复数据时回调

调用时机

  1. 用户按下Home键时;
  2. 长按Home键或菜单键时;
  3. 手机息屏;
  4. 打开新Activity时;
  5. 横竖屏切换时;

恢复数据
onRestoreInstanceState只有在activity确实是被系统回收,重新创建activity的情况下才会被调用,如果被调用了,则说明activity被回收。
onCreate中也有bundle参数,但onSaveInstanceState不一定被调用,所以在恢复数据的时候需要非空判断,而onRestoreInstanceState回调时其bundle值一定不为空。

应用log捕捉

  1. 定义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 相关

说说协程:

什么是 const? 和val有什么区别?

Val是在运行时被 设置, 加一个const 修饰符在val上将会变成编译时常量。 Const不能修饰var,不能用于局部变量

Lazy和lateinit的区别

两者都延迟属性的初始化 lateinit 是用于var的修饰符, 可以用于 晚点来进行赋值。 Lazy更像一个方法或者一个lambda表达式。只用于val。 在需要时被创建。

文章参考:
stormzhang/android-interview-questions-cn: 最全面的高质量 Android 面试指南。 (github.com)

上一篇 下一篇

猜你喜欢

热点阅读