Android面试题
Android面试题除了Android基础之外,更多的问的是一些源码级别的、原理这些等。所以想去大公司面试,一定要多看看源码和实现方式,常用框架可以试试自己能不能手写实现一下,锻炼一下自己。
BAT2020面试题
一、基础篇
1、要做一个尽可能流畅的 ListView,你平时在工作中如何进行优化
的?①Item 布局,层级越少越好,使用 hierarchyview 工具查看优
化。
②复用 convertView
③使用 ViewHolder
④item 中有图片时,异步加载
⑤快速滑动时,不加载图片
⑥item 中有图片时,应对图片进行适当压缩
⑦实现数据的分页加载
2、对于 Android 的安全问题,你知道多少
①错误导出组件
② 参数校验不严③WebView 引入各种安全问题,webview 中的 js 注入
④不混淆、不防二次打包
⑤明文存储关键信息
⑦ 错误使用 HTTPS
⑧山寨加密方法
⑨滥用权限、内存泄露、使用 debug 签名
3、如何缩减 APK 包大小?
代码
保持良好的编程习惯,不要重复或者不用的代码,谨慎添加 libs,移除使用不到的 libs。使用 proguard 混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小。native code 的部分,大多数情况下只需要支持 armabi 与 x86 的架构即可。如果非必须,可以考虑拿掉 x86 的部分。
资源
使用 Lint 工具查找没有使用到的资源。去除不使用的图片,String,XML 等等。assets 目录下的资源请确保没有用不上的文件。生成 APK 的时候,aapt 工具本身会对 png 做优化,但是在此之前还可以使用其他工具如 tinypng 对图片进行进一步的压缩预处理。jpeg 还是 png,根据需要做选择,在某些时候 jpeg 可以减少图片的体积。 对于9.png 的图片,可拉伸区域尽量切小,另外可以通过使用 9.png 拉伸达到大图效果的时候尽量不要使用整张大图。
策略
有选择性的提供 hdpi,xhdpi,xxhdpi 的图片资源。建议优先提供 xhdpi 的图片,对于 mdpi,ldpi 与 xxxhdpi 根据需要提供有差异的部分即可。尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图片可以通过代码旋转的方式实现。能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组成 animate-list 的 AnimationDrawable,这种方式提供了多张图片很占空间。
4、Android 与服务器交互的方式中的对称加密和非对称加密是什么?
对称加密,就是加密和解密数据都是使用同一个 key,这方面的算法有DES。非对称加密,加密和解密是使用不同的 key。发送数据之前要先和服务端约定生成公钥和私钥,使用公钥加密的数据可以用私钥解密,反之。这方面的算法有 RSA。ssh 和 ssl 都是典型的非对称加密。
5、设备横竖屏切换的时候,接下来会发生什么?
1、不设置 Activity 的 android:configChanges 时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
2、设置 Activity 的 android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
3、设置 Activity 的 android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行 onConfigurationChanged 方法
进阶篇
1、哪些情况下的对象会被垃圾回收机制处理掉?
利用可达性分析算法,虚拟机会将一些对象定义为 GC Roots,从 GC Roots 出发沿着引用链
向下寻找,如果某个对象不能通过 GC Roots 寻找到,虚拟机就认为该对象可以被回收掉。
1.1 哪些对象可以被看做是 GC Roots 呢?
1)虚拟机栈(栈帧中的本地变量表)中引用的对象;
2)方法区中的类静态属性引用的对象,常量引用的对象;
3)本地方法栈中 JNI(Native 方法)引用的对象;
1.2 对象不可达,一定会被垃圾收集器回收么?
即使不可达,对象也不一定会被垃圾收集器回收,
1)先判断对象是否有必要执行 finalize()方法,对象必须重写 finalize()方法且没有被运行过。
2)若有必要执行,会把对象放到一个队列中,JVM 会开一个线程去回收它们,这是对象最后一次可以逃逸清理的机会。
2、讲一下常见编码方式?
编码的意义:计算机中存储的最小单元是一个字节即 8bit,所能表示的字符范围是 255
个,而人类要表示的符号太多,无法用一个字节来完全表示,固需要将符号编码,将各种
语言翻译成计算机能懂的语言。
1)ASCII 码:总共 128 个,用一个字节的低 7 位表示,0〜31 控制字符如换回车删除等;
32~126 是打印字符,可通过键盘输入并显示出来;
2)ISO-8859-1,用来扩展 ASCII 编码,256 个字符,涵盖了大多数西欧语言字符。
3)GB2312:双字节编码,总编码范围是 A1-A7,A1-A9 是符号区,包含 682 个字符,B0-B7 是
汉字区,包含 6763 个汉字;
4)GBK 为了扩展 GB2312,加入了更多的汉字,编码范围是 8140~FEFE,有 23940 个码位,
能表示 21003 个汉字。
5)UTF-16:ISO 试图想创建一个全新的超语言字典,世界上所有语言都可通过这本字典
Unicode 来相互翻译,而 UTF-16 定义了 Unicode 字符在计算机中存取方法,用两个字节来
表示 Unicode 转化格式。不论什么字符都可用两字节表示,即 16bit,固叫 UTF-16。
6)UTF-8:UTF-16 统一采用两字节表示一个字符,但有些字符只用一个字节就可表示,浪
费存储空间,而 UTF-8 采用一种变长技术,每个编码区域有不同的字码长度。 不同类型
的字符可以由 1~6 个字节组成。
3、utf-8 编码中的中文占几个字节;int 型几个字节?
utf-8 是一种变长编码技术,utf-8 编码中的中文占用的字节不确定,可能 2 个、3 个、4
个,int 型占 4 个字节。
4、静态代理和动态代理的区别,什么场景使用?
代理是一种常用的设计模式,目的是:为其他对象提供一个代理以控制对某个对象的访
问,将两个类的关系解耦。代理类和委托类都要实现相同的接口,因为代理真正调用的是
委托类的方法。
区别:
1)静态代理:由程序员创建或是由特定工具生成,在代码编译时就确定了被代理的类是哪
一个是静态代理。静态代理通常只代理一个类;2)动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口
下的多个实现类;
实现步骤:a.实现 InvocationHandler 接口创建自己的调用处理器;b.给 Proxy 类提供
ClassLoader 和代理接口类型数组创建动态代理类;c.利用反射机制得到动态代理类的构造
函数;d.利用动态代理类的构造函数创建动态代理类对象;
使用场景:Retrofit 中直接调用接口的方法;Spring 的 AOP 机制;
5、Java 的异常体系
Java 中 Throwable 是所有异常和错误的超类,两个直接子类是 Error(错误)和 Exception
(异常):
1)Error 是程序无法处理的错误,由 JVM 产生和抛出,如 OOM、ThreadDeath 等。这些异
常发生时,JVM 一般会选择终止程序。
2)Exception 是程序本身可以处理的异常,又分为运行时异常(RuntimeException)(也叫
CheckedEception)和非运行时异常(不检查异常 UncheckedException)。运行时异常有
NullPointerException\IndexOutOfBoundsException 等,这些异常一般是由程序逻辑错误引起
的,应尽可能避免。非运行时异常有 IOException\SQLException\FileNotFoundException 以及
由用户自定义的 Exception 异常等。
高级篇
1. 说下你所知道的设计模式与使用场景
a.建造者模式:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。使用场景比如最常见的 AlertDialog,拿我们开发过程中举例,比如 Camera 开发过程中,可能需要设置一个初始化的相机配置,设置摄像头方向,闪光灯开闭,成像质量等等,这种场景下就可以使用建造者模式装饰者模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。装饰者模式可以在不改变原有类结构的情况下曾强类的功能,比如 Java 中的 BufferedInputStream 包装 FileInputStream,举个开发中的例子,
比如在我们现有网络框架上需要增加新的功能,那么再包装一层即可,装饰者模式解决了继承存在的一些问题,比如多层继承代码的臃肿,使代码逻辑更清晰观察者模式:代理模式:门面模式:单例模式:生产者消费者模式:
2. java 语言的特点与 OOP 思想
这个通过对比来描述,比如面向对象和面向过程的对比,针对这两种思想的对比,还可以举个开发中的例子,比如播放器的实现,面向过程的实现方式就是将播放视频的这个功能分解成多个过程,比如,加载视频地址,获取视频信息,初始化解码器,选择合适的解码器进行解码,读取解码后的帧进行视频格式转换和音频重采样,然后读取帧进行播放,这是一个完整的过程,这个过程中不涉及类的概念,而面向对象最大的特点就是类,封装继承和多态是核心,同样的以播放器为例,一面向对象的方式来实现,将会针对每一个功能封装出一个对象,吧如说 Muxer,获取视频信息,Decoder,解码,格式转换器,视频播放器,音频播放器等,每一个功能对应一个对象,由这个对象来完成对应的功能,并且遵循单一职责原则,一个对象只做它相关的事情
3. 说下 java 中的线程创建方式,线程池的工作原理。
java 中有三种创建线程的方式,或者说四种
1.继承 Thread 类实现多线程
2.实现 Runnable 接口
3.实现 Callable 接口
4.通过线程池
线程池的工作原理:线程池可以减少创建和销毁线程的次数,从而减少系统资
源的消耗,当一个任务提交到线程池时
a. 首先判断核心线程池中的线程是否已经满了,如果没满,则创建一个核心线
程执行任务,否则进入下一步
b. 判断工作队列是否已满,没有满则加入工作队列,否则执行下一步
c. 判断线程数是否达到了最大值,如果不是,则创建非核心线程执行任务,否
则执行饱和策略,默认抛出异常
4. 说下 handler 原理
Handler,Message,looper 和 MessageQueue 构成了安卓的消息机制,handler
创建后可以通过 sendMessage 将消息加入消息队列,然后 looper 不断的将消息
从 MessageQueue 中取出来,回调到 Hander 的 handleMessage 方法,从而实现
线程的通信。
从两种情况来说,第一在 UI 线程创建 Handler,此时我们不需要手动开启
looper,因为在应用启动时,在 ActivityThread 的 main 方法中就创建了一个当前
主线程的 looper,并开启了消息队列,消息队列是一个无限循环,为什么无限
循环不会 ANR?因为可以说,应用的整个生命周期就是运行在这个消息循环中
的,安卓是由事件驱动的,Looper.loop 不断的接收处理事件,每一个点击触摸
或者 Activity 每一个生命周期都是在 Looper.loop 的控制之下的,looper.loop 一
旦结束,应用程序的生命周期也就结束了。我们可以想想什么情况下会发生
ANR,第一,事件没有得到处理,第二,事件正在处理,但是没有及时完成,而对事件进行处理的就是 looper,所以只能说事件的处理如果阻塞会导致
ANR,而不能说 looper 的无限循环会 ANR
另一种情况就是在子线程创建 Handler,此时由于这个线程中没有默认开启的消
息队列,所以我们需要手动调用 looper.prepare(),并通过 looper.loop 开启消息
主线程 Looper 从消息队列读取消息,当读完所有消息时,主线程阻塞。子线程
往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件
读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。因
此 loop 的循环并不会对 CPU 性能有过多的消耗。
5. 内存泄漏的场景和解决办法
1.非静态内部类的静态实例
非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会
长期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类
2.多线程相关的匿名内部类和非静态内部类
匿名内部类同样会持有外部类的引用,如果在线程中执行耗时操作就有可能发
生内存泄漏,导致外部类无法被回收,直到耗时任务结束,解决办法是在页面
退出时结束线程中的任务
3.Handler 内存泄漏
Handler 导致的内存泄漏也可以被归纳为非静态内部类导致的,Handler 内部
message 是被存储在 MessageQueue 中的,有些 message 不能马上被处理,存在
的时间会很长,导致 handler 无法被回收,如果 handler 是非静态的,就会导致
它的外部类无法被回收,解决办法是 1.使用静态 handler,外部类引用使用弱引
用处理 2.在退出页面时移除消息队列中的消息
4.Context 导致内存泄漏
根据场景确定使用 Activity 的 Context 还是 Application 的 Context,因为二者生命
周期不同,对于不必须使用 Activity 的 Context 的场景(Dialog),一律采用
Application 的 Context,单例模式是最常见的发生此泄漏的场景,比如传入一个
Activity 的 Context 被静态类引用,导致无法回收
5.静态 View 导致泄漏
使用静态 View 可以避免每次启动 Activity 都去读取并渲染 View,但是静态 View会持有 Activity 的引用,导致无法回收,解决办法是在 Activity 销毁的时候将静
态 View 设置为 null(View 一旦被加载到界面中将会持有一个 Context 对象的引
用,在这个例子中,这个 context 对象是我们的 Activity,声明一个静态变量引
用这个 View,也就引用了 activity)
6.WebView 导致的内存泄漏
WebView 只要使用一次,内存就不会被释放,所以 WebView 都存在内存泄漏的
问题,通常的解决办法是为 WebView 单开一个进程,使用 AIDL 进行通信,根
据业务需求在合适的时机释放掉
7.资源对象未关闭导致
如 Cursor,File 等,内部往往都使用了缓冲,会造成内存泄漏,一定要确保关闭
它并将引用置为 null
8.集合中的对象未清理
集合用于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合
是静态的
9.Bitmap 导致内存泄漏
bitmap 是比较占内存的,所以一定要在不使用的时候及时进行清理,避免静态
变量持有大的 bitmap 对象
10.监听器未关闭
很多需要 register 和 unregister 的系统服务要在合适的时候进行 unregister,手动
添加的 listener 也需要及时移除
由于文章篇幅过长,有需要完整面试题目的可以直接点击下面链接领取哦。
网友评论