Android高级面试题资料(持续更新)

简介: 1. ThreadLocal的理解 可以保证线程的安全。在多个线程共享相同的数据的时候,会为每个线程创建单独的副本,在单独的副本上进行数据的操作,不会对其它线程的数据产生影响,保证了线程安全。 2. HashMap HashSet HashTable的区别? 都是集合,底层都是Hash算法实现的。

2019年阿里云双十一拼团地址:https://www.aliyun.com/1111/2019/home

1. ThreadLocal的理解

可以保证线程的安全。在多个线程共享相同的数据的时候,会为每个线程创建单独的副本,在单独的副本上进行数据的操作,不会对其它线程的数据产生影响,保证了线程安全。

2. HashMap HashSet HashTable的区别?

都是集合,底层都是Hash算法实现的。HashMap是Hashtable的替代品,这两个都是双列集合,而HashSet是单列集合。HashMap线程不安全、效率高、可以存储null键和null值;Hashtable线程安全,效率低,不可以存储null键和null值。

3. 如何让HashMap可以线程安全?

HashMap 在并发执行 put 操作时会引起死循环,导致 CPU 利用率接近100%。因为多线程会导致 HashMap 的 Node 链表形成环形数据结构,一旦形成环形数据结构,Node 的 next 节点永远不为空,就会在获取 Node 时产生死循环。
使用下面三种替换方式:
Hashtable
ConcurrentHashMap
Synchronized Map

4. Android对HashMap做了优化后推出的新的容器类是什么?

SparseArray
它要比 HashMap 节省内存,某些情况下比HashMap性能更好,按照官方问答的解释,主要是因为SparseArray不需要对key和value进行auto-boxing(将原始类型封装为对象类型,比如把int类型封装成Integer类型),结构比HashMap简单(SparseArray内部主要使用两个一维数组来保存数据,一个用来存key,一个用来存value)不需要额外的额外的数据结构(主要是针对HashMap中的HashMapEntry而言的)。

5. Java多线程之间如何通信

等待唤醒机制

6. 线程池的实现机制

向线程池提交任务,会依次启动核心线程,如果提交的任务数超过了核心线程数,会将任务保存到阻塞队列中,如果阻塞队列也满了,且继续提交任务,则会创建新线程执行任务,直到任务数达到最大线程数。此时如果再提交任务的话会抛出异常或者直接丢弃任务。通过Executor.execute()无法得到返回值,通过ExecutorService.submit()可以得到返回值。

7. RxJava中map和flatmap操作符的区别及底层实现

Map返回的是结果集,flatmap返回的是包含结果集的Observable。Map只能一对一,flatmap可以一对多、多对多。
RxJava是通过观察者模式实现的。

8. 对消息机制中Looper的理解

Looper在消息机制中扮演的角色是创造无限循环从Messagequeue中取得消息然后分发。

9. 单例模式有哪些实现方式

饿汉模式(线程安全,调用效率高,但是不能延时加载)
懒汉模式(线程安全,调用效率不高,但是能延时加载)
双重检测锁模式(由于JVM底层模型原因,偶尔会出问题,不建议使用)
静态内部类式(线程安全,调用效率高,可以延时加载)
枚举类(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用)

10. 通过静态内部类实现单例模式有哪些优点

线程安全,调用效率高,可以延时加载

11. synchronized volatile关键字有什么区别?以及还有哪些同样功能的关键字

(1) volatile是变量修饰符,而synchronized则作用于一段代码或者方法。
(2) volatile只是在线程内存和main memory(主内存)间同步某个变量的值;而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。
const、final、lock

12. 界面卡顿的原因有哪些?

UI线程(main)有耗时操作
视图渲染时间过长,导致卡顿

13. 造成OOM/ANR 的原因?

OOM: (1)不恰当地使用static关键字 (2)内部类对Activity的引用 (3)大量Bitmap的使用会导致程序包运行时的内存消耗变大 (4)游标Cursor对象用完应该及时关闭 (5)加载对象过大 (6)相应资源过多,来不及释放。
ANR: (1)在5秒内没有响应输入的事件(IO操作耗时、数据库操作复杂耗时、主线程非主线程产生死锁等待、网络加载/图片操作耗时、硬件操作耗时) (2)BroadcastReceiver在10秒内没有执行完毕(Service binder数量达到上限、Service忙导致超时无响应)

14. Activity与Fragment生命周期有何联系

在创建的过程中,是Activity带领着Fragment,在销毁的过程中,是Fragment带领着Activity。
这里写图片描述

15. Glide三级缓存

内存缓存,磁盘缓存、网络缓存(由于网络缓存严格来说不算是缓存的一种,故也称为二级缓存)。缓存的资源分为两种:原图(SOURCE)、处理图(RESULT)(默认)。
内存缓存:默认开启的,可以通过调用skipMemoryCache(true)来设置跳过内存缓存,缓存最大空间:每个进程可用的最大内存*0.4。(低配手机0.33)
磁盘缓存:分为四种:ALL(缓存原图)、NONE(什么都不缓存)、SOURCE(只缓存原图)、RESULT(之后处理图),通过diskCacheStrategy(DiskCacheStrategy.ALL)来设置,缓存大小250M。

16. MVC、MVP、MVVM的原理

(1) MVC,Model View Controller,是软件架构中最常见的一种框架,简单来说就是通过controller的控制去操作model层的数据,并且返回给view层展示。当用户发出事件的时候,view层会发送指令到controller层,接着controller去通知model层更新数据,model层更新完数据以后直接显示在view层上,这就是MVC的工作原理。
这里写图片描述
(2) MVP是MVC的演化。MVP的model层相对于MVC是一样的,而activity和fragment不再是controller层,而是纯粹的view层,所有关于用户事件的转发全部交由presenter层处理。presenter层充当了桥梁的作用,用于操作view层发出的事件传递到presenter层中,presenter层去操作model层,并且将数据返回给view层。
这里写图片描述
(3) MVVM和MVP的区别貌似不大,只不过是presenter层换成了viewmodel层,还有一点就是view层和viewmodel层是相互绑定的关系,这意味着当你更新viewmodel层的数据的时候,view层会相应的变动ui。
这里写图片描述

17. 数据库的操作类型有哪些,如何导入外部数据库?

(1) 增删改查
(2) 将外部数据库放在项目的res/raw目录下。因为安卓系统下数据库要放在data/data/packagename/databases的目录下,然后要做的就是将外部数据库导入到该目录下,操作方法是通过FileInputStream读取外部数据库,再用FileOutputStrean把读取到的东西写入到该目录下。

18. 是否使用过 IntentService,作用是什么, AIDL 解决了什么问题?

(1) IntentService继承自Service。由于Service运行在主线程,无法进行耗时操作。所以你需要在Service中开启一个子线程,并且在子线程中运行。为了简化这一操作,Android中提供了IntentService来进行这一处理。通过查看IntentService的源码可以看到,在onCreate中,我们开启了一个HandlerThread线程,之后获取HandlerThread线程中的Looper,并通过这个Looper创建了一个Handler。然后在onStart方法中通过这个Handler将intent与startId作为Message的参数进行发送到消息队列中,然后交由Handler中的handleMessage中进行处理。由于在onStart方法是在主线程内运行的,而Handler是通过工作者线程HandlerThread中的Looper创建的。所以也就是在主线程中发送消息,在工作者接收到消息后便可以进行一些耗时的操作。
(2) 进程间通信

19. 是否使用过本地广播,和全局广播有什么差别?

本地广播的数据在本应用范围内传播,不用担心隐私数据泄露的问题。不用担心别的应用伪造广播,造成安全隐患。相比在系统内发送全局广播,它更高效。

20. Activity、 Window、 View 三者的差别, fragment 的特点?

(1) Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图) LayoutInflater像剪刀,Xml配置像窗花图纸。
(2) a. Fragment可以作为Activity界面的一部分组成出现;

  1. 可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可以在多个Activity中使用;
  2. 在Activity运行过程中,可以添加、移除或者替换Fragment;
  3. Fragment可以响应自己的输入事件,并且有自己的生命周期,它们的生命周期会受宿主Activity的生命周期影响。

21. Handler、 Thread 和 HandlerThread 的差别

从Android中Thread(java.lang.Thread -> java.lang.Object)描述可以看出,Android的Thread没有对Java的Thread做任何封装,但是Android提供了一个继承自Thread的类HandlerThread(android.os.HandlerThread -> java.lang.Thread),这个类对Java的Thread做了很多便利Android系统的封装。
android.os.Handler可以通过Looper对象实例化,并运行于另外的线程中,Android提供了让Handler运行于其它线程的线程实现,也是就HandlerThread。HandlerThread对象start后可以获得其Looper对象,并且使用这个Looper对象实例Handler。

22. 低版本 SDK 实现高版本 api

自己实现或使用注解@TargetApi annotation

23. launch mode 应用场景

(1) standard:标准的启动模式。

这里写图片描述
(2) singleTop:单一顶部模式

如果Activity已经被开启,并且处于任务栈的栈顶,就不会创建新的Activity,而是复用这个已经开启的Activity。
为了防止出现一些奇怪的用户体验,推荐使用单一顶部模式,整个任务栈可以有多个实例存在.
应用场景:短信发送界面.
这里写图片描述
(3)singletask:单一任务栈

在整个任务栈里面只允许有一个当前Activity的实例存在
如果要开启的Activity在任务栈中已经存在,直接复用这个已经存在的Activity,并且把这个Activity上面的所有的其他Activity给清空
应用场景:如果一个Activity非常消耗内存和cpu资源,建议把这个Activity做成singletask的模式。浏览器的browserActivity
这里写图片描述
(4)singleinstance:单一实例.

整个手机操作系统只有一个实例存在,并且是运行在自己单独的任务栈里面.
应用场景:通话界面的Activity
这里写图片描述

24. touch 事件传递流程

事件处理包括三种情况,分别为:传递—-dispatchTouchEvent()函数、拦截——onInterceptTouchEvent()函数、消费—-onTouchEvent()函数和OnTouchListener。
Android事件传递流程:
(1) 事件都是从Activity.dispatchTouchEvent()开始传递
(2) 事件由父View传递给子View,ViewGroup可以通过onInterceptTouchEvent()方法对事件拦截,停止其向子view传递
(3) 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。
(4) 如果View没有对ACTION_DOWN进行消费,之后的其他事件不会传递过来,也就是说ACTION_DOWN必须返回true,之后的事件才会传递进来
(5) OnTouchListener优先于onTouchEvent()对事件进行消费

View不处理事件流程图
View不处理事件流程图

View处理事件流程图
View处理事件流程图

事件拦截
事件拦截

25.Android性能优化

一、代码优化

1.使用AndroidLint分析结果进行相应优化
2.不使用枚举及IOC框架,反射性能低
3.常量加static
4.静态方法
5.减少不必要的对象、成员变量
6.尽量使用线程池
7.适当使用软引用和弱引用
8.尽量使用静态内部类,避免潜在的内存泄露
9.图片缓存,采用内存缓存LRUCache和硬盘缓存DiskLRUCache
10.Bitmap优化,采用适当分辨率大小并及时回收

二、布局优化

避免OverDraw过渡绘制
优化布局层级
避免嵌套过多无用布局
当我们在画布局的时候,如果能实现相同的功能,优先考虑相对布局,然后在考虑别的布局,不要用绝对布局。
使用标签把复杂的界面需要抽取出来
使用标签,因为它在优化UI结构时起到很重要的作用。目的是通过删减多余或者额外的层级,从而优化整个Android Layout的结构。核心功能就是减少冗余的层次从而达到优化UI的目的!
ViewStub 是一个隐藏的,不占用内存空间的视图对象,它可以在运行时延迟加载布局资源文件。

三、ListView和GridView优化

1.采用ViewHolder复用convertView
2.避免在getView中执行耗时操作
3.列表在滑动状态时不加载图片
4.开启硬件加速

26.Android内存泄漏

内存泄漏简单地说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,且没有任何一个指针指向它,那么这块内存就泄露了。可能的原因有:
1.注册没取消造成内存泄露,如:广播
2.静态变量持有Activity的引用
3.单例模式持有Activity的引用
4.查询数据库后没有关闭游标cursor
5.构造Adapter时,没有使用 convertView 重用
6.Bitmap对象不在使用时调用recycle()释放内存
7.对象被生命周期长的对象引用,如activity被静态集合引用导致activity不能释放
8.使用Handler造成的内存泄露

原文地址:http://cloud.yundashi168.com/archives/964

相关文章
|
3月前
|
Java Android开发
Android面试题经典之Glide取消加载以及线程池优化
Glide通过生命周期管理在`onStop`时暂停请求,`onDestroy`时取消请求,减少资源浪费。在`EngineJob`和`DecodeJob`中使用`cancel`方法标记任务并中断数据获取。当网络请求被取消时,`HttpUrlFetcher`的`cancel`方法设置标志,之后的数据获取会返回`null`,中断加载流程。Glide还使用定制的线程池,如AnimationExecutor、diskCacheExecutor、sourceExecutor和newUnlimitedSourceExecutor,其中某些禁止网络访问,并根据CPU核心数动态调整线程数。
93 2
|
3月前
|
Android开发
Android面试题之Activity的启动模式和flag
Android Activity的四种启动模式:standard(默认,每次启动创建新实例),singleTop(栈顶复用,不走onCreate,调用onNewIntent),singleTask(栈内唯一,清除上方Activity)和singleInstance(单独栈内唯一)。启动模式在AndroidManifest.xml中配置,Intent Flags如FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_SINGLE_TOP可实现类似功能。了解这些对于处理Activity栈管理至关重要。
43 0
|
3月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
**Kotlin中的`by lazy`和`lateinit`都是延迟初始化技术。`by lazy`用于只读属性,线程安全,首次访问时初始化;`lateinit`用于可变属性,需手动初始化,非线程安全。`by lazy`支持线程安全模式选择,而`lateinit`适用于构造函数后初始化。选择依赖于属性特性和使用场景。**
107 5
Android经典面试题之Kotlin延迟初始化的by lazy和lateinit有什么区别?
|
3月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin中常见作用域函数
**Kotlin作用域函数概览**: `let`, `run`, `with`, `apply`, `also`. `let`安全调用并返回结果; `run`在上下文中执行代码并返回结果; `with`执行代码块,返回结果; `apply`配置对象后返回自身; `also`附加操作后返回自身
42 8
|
3月前
|
Android开发 开发者
Android经典面试题之SurfaceView和TextureView有什么区别?
分享了`SurfaceView`和`TextureView`在Android中的角色。`SurfaceView`适于视频/游戏,独立窗口低延迟,但变换受限;`TextureView`支持复杂变换,视图层级中渲染,适合动画/视频特效,但性能略低。两者在性能、变换、使用和层级上有差异,开发者需按需选择。
48 1
|
3月前
|
SQL Java Unix
Android经典面试题之Java中获取时间戳的方式有哪些?有什么区别?
在Java中获取时间戳有多种方式,包括`System.currentTimeMillis()`(毫秒级,适用于日志和计时)、`System.nanoTime()`(纳秒级,高精度计时)、`Instant.now().toEpochMilli()`(毫秒级,ISO-8601标准)和`Instant.now().getEpochSecond()`(秒级)。`Timestamp.valueOf(LocalDateTime.now()).getTime()`适用于数据库操作。选择方法取决于精度、用途和时间起点的需求。
47 3
|
3月前
|
SQL 安全 Java
Android经典面试题之Kotlin中object关键字实现的是什么类型的单例模式?原理是什么?怎么实现双重检验锁单例模式?
Kotlin 单例模式概览 在 Kotlin 中,`object` 关键字轻松实现单例,提供线程安全的“饿汉式”单例。例如: 要延迟初始化,可使用 `companion object` 和 `lazy` 委托: 对于参数化的线程安全单例,结合 `@Volatile` 和 `synchronized`
43 6
|
3月前
|
XML Android开发 数据格式
Android面试题之DialogFragment中隐藏导航栏
在Android中展示全屏`DialogFragment`并隐藏状态栏和导航栏,可通过设置系统UI标志实现。 记得在布局文件中添加内容,并使用`show()`方法显示`DialogFragment`。
50 2
|
3月前
|
Android开发 Kotlin
Android面试题之kotlin中怎么限制一个函数参数的取值范围和取值类型等
在Kotlin中,限制函数参数可通过类型系统、泛型、条件检查、数据类、密封类和注解实现。例如,使用枚举限制参数为特定值,泛型约束确保参数为Number子类,条件检查如`require`确保参数在特定范围内,数据类封装可添加验证,密封类限制为一组预定义值,注解结合第三方库如Bean Validation进行校验。
52 6
|
3月前
|
Android开发
Android面试题之自定义View注意事项
在Android开发中,自定义View主要分为四类:直接继承View重写onDraw,继承ViewGroup创建布局,扩展特定View如TextView,以及继承特定ViewGroup。实现时需注意:支持wrap_content通过onMeasure处理,支持padding需在onDraw或onMeasure/onLayout中处理。避免在View中使用Handler,使用post系列方法代替。记得在onDetachedFromWindow时停止线程和动画以防止内存泄漏。处理滑动嵌套时解决滑动冲突,并避免在onDraw中大量创建临时对象。
41 4
下一篇
无影云桌面