Android客户端阿里面试总结

面试题:https://zhuanlan.zhihu.com/p/93831276

Android面试必备26题(阿里腾讯总结)含答案

Android 面试问题记录

做了5年Android,靠着这份面试题跟答案,我从12K变成了30K

Android面试一天一题(Day 37:一套高级工程师的面试题)

2019Android多线程面试题总结

(一)背景

1、本人本科一本双非垫底的那种,硕士211。本硕软件工程,小厂工作三年,积累经验,面试阿里饿了么,成功获得offer。

(二)面试经过

阿里一面

1.点击图标,应用打开,点击home键,重新进入,Activity生命周期回调

2.service生命周期

(1)通过 startService 启动 Service:

onCreate() >> onStartCommand() >> onDestory()
1.如果 Service 还没有运行,则调用 onCreate()然后调用onStartCommand();

2.如果Service已经运行,则只调用onStartCommand(),所以一个Service的 onStartCommand()方法可能会重复调用多次。

3.调用 stopService 的时候直接 onDestroy(),

4.生命周期和调用者不同,这时 Service 跟启动的 Activity 没有关联,启动后若调用者未调用 stopService 而直接退出,Service 仍会运行。

(2)通过 bindService 绑定 Service:

onCreate >> onBind >> onUnbind >> onDestory。
1.onBind 将返回给客户端一个 IBind 接口实例,IBind 允许客户端回调服务的方法,比如得到 Service 运行的状态或其他操作。

2.生命周期与调用者绑定,那么这个 Service 就跟启动他的进程有关了调用者一旦退出,Service 就会调用 unBind >> onDestory

3.所以调用 bindService 的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory
 

3.fragment什么时候用

1. 当你有一个activity,想让这个activity根据事件响应可以对应不同的界面时,就可以创建几个fragment,将fragment绑定到该activity

2.Fragment的设计初衷便是为了将布局与代码逻辑一起封装,想想下面这个场景:
一款APP包含界面A和界面B,界面B为界面A的详情。你需要同时适配手机和平板,手机版的操作逻辑为A跳转到B,而平板的布局为AB同一界面,A在左边,B在右边。
这时候最好的方法将A、B都封装为Fragment,手机中直接使用Fragment,平板中设置两个Fragment的布局即可。

4.软引用和弱引用,什么时候用弱引用?

1、软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中

2、弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。

3、弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。所以被软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。

4、应用场景:如果一个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么应该用 Weak Reference 来记住此对象。或者想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候就应该用弱引用,这个引用不会在对象的垃圾回收判断中产生任何附加的影响。
原文链接:https://blog.csdn.net/qq_39192827/article/details/85611873

5.LruCache内部通过什么数据结构实现?最大存贮容量?

三级缓存 DiskLruCache

LruCache的缓存策略

LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序排序的。当调用put()方法时,就会在结合中添加元素,并调用trimToSize()判断缓存是否已满,如果满了就用LinkedHashMap的迭代器删除队尾元素,即最近最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法获得对应集合元素,同时会更新该元素到队头
①设置LruCache缓存的大小,一般为当前进程可用容量的1/8。 

Android DiskLruCache完全解析,硬盘缓存的最佳方案

6.activity如何传递信息?

Activity之间传递数据的三种方式详解

1.通过Bundle传递简单数据 
2.通过Serializable方式传递对象 
3.通过Parcelable方式传递对象

7.bundle能传什么类型的数据?

1、基本类型的数据,如int、String、Float等等;

2、自定义的必须实现Serializable(Java中的)和Parceable(Android中的)序列号接口,使用bundle.putSerializable(Key,Value),bundle.putParceable(Key,Value)传递对象
 

8.parcelable和serialazible的区别?   Android中的IPC机制

那么Serializable和Parcelable的区别:

①、Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量的IO操作。

②、Parcelable是Android中的序列化方式,因此更加适合Android平台上,它缺点就是使用起来稍微麻烦点(Android studio会自动填充),但它的效率很好,这是Android推荐的序列化方式,因此我们首选Parcelable。

9.binder底层实现?复制几次?  1次

binder一次跨进程通讯,只需要一次拷贝(原因后面会解析),而一般的像socket通讯则需要两次拷贝;

用户和内核空间分配了一段虚拟地址,这段虚拟地址将用于binder内存的访问。binder的物理内存页由binder驱动负责分配,然后这些物理页的访问,将分为进程的用户空间和进程内核空间。由于进程的用户空间和内核空间的虚拟地址范围是不一样的,所以这里分配的一段虚拟地址将包括进程的用户空间地址和内核空间地址
内核刚开始只是分配了一个物理页,并且分别将这个物理页映射到进程的内核虚拟地址空间V1(修改内核空间的页表映射)和进程的用户虚拟地址空间V2(修改用户空间的页表映射)。在用户空间访问V1和在内核空间访问V2,其实都是访问的是同一个物理内存块,从而实现进程的内核和用户空间共享同一块物理内存的目的。这样binder驱动在内核空间,将一段数据拷贝到这个物理页,则该进程的用户空间则不需要copy_to_user()即可以同步看到内核空间的修改,并能够访问这段物理内存.

https://blog.csdn.net/AndroidStudyDay/article/details/93749470

Binder传输数据大小限制:Binder内存大小是不到1M的,准确说是 110241024) - (4096 *2) 

Version:0.9 StartHTML:0000000105 EndHTML:0000004737 StartFragment:0000000141 EndFragment:0000004697

Android Binder 有哪些特点
基于 C/S 模式 , 每次 IPC 只需要一次数据拷
支持同步与异步两种 用方式
线 程池自 管理 : 按需
接口基于 AIDL(Android Interface Definition Language) 来描述
支持多种 型基本数据的序列化与反序列化
可以通 过进 程的 UID/PID 识别验证调 用者的身份

10.Android handler和looper源码?

Android 源码分析 —— HandlerLooper 和 MessageQueue - 知乎

Android Handler机制(三)---Looper源码解析 - 超超boy - 博客园

11.loop()方法阻塞,为什么不影响事件分发?

Android中为什么主线程不会因为Looper.loop()方法造成阻塞

主要原因有2个

  1. epoll模型 
    当没有消息的时候会epoll.wait,等待句柄写的时候再唤醒,这个时候其实是阻塞的。

  2. 所有的ui操作都通过handler来发消息操作。 
    比如屏幕刷新16ms一个消息,你的各种点击事件,所以就会有句柄写操作,唤醒上文的wait操作,所以不会被卡死了。

12.Android 事件分发?

Android事件分发机制详解:史上最全面、最易懂 - 简书

13.看过哪些Android的书?

阿里二面

Http协议总结(面试)
1.七层模型,每一层是干什么的

OSI七层模型与TCP的三次握手与四次挥手
2.http和https区别

http1.0 http1.0 http2.0的特点和改进

  • https://blog.csdn.net/sysuzhyupeng/article/details/56508111
  • 多路复用:允许同时通过单一的HTTP/2连接发起多重的请求-响应消息
  • 二进制分帧 :关键之一就是在应用层(HTTP/2)和传输层(TCP or UDP)之间增加一个二进制分帧层
  • 头部压缩:HTTP2.0encoder来减少需要传输的header大小,
  • 服务器推送:服务器除了对最初请求的响应外,服务器还可以额外的向客户端推送资源,而无需客户端明确的请求。

3.如何改进http,提高响应速度,减少请求时间
4.http缓存 https://blog.csdn.net/jutal_ljt/article/details/80021545


5.工作期间遇到的困难
6.操作系统中什么是堆栈   

操作系统,堆栈(stack),堆(heap),详解 - qq_42034205的博客 - CSDN

1.堆:什么是堆?又该怎么理解呢?

  1. 堆(heap)是一种数据结构,堆控制一段自己的存储空间,叫做堆空间。
  2. 堆是在程序运行时申请的动态内存,而不是在程序编译时,申请某个大小的内存空间。
  3. 堆是应用程序在运行的时候请求操作系统分配给自己内存,一般是申请/给予的过程。

2. 栈(堆栈):什么是堆栈?又该怎么理解呢?

  1. 栈(stack)又名堆栈是操作系统在建立某个进程时或者线程,为这个线程建立的存储区域,在编译的时候可以指定需要的栈的大小
  2. 栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。
  3. 栈好比一个桶,后放进去的先拿出来,它下面本来有的东西要等它出来之后才能出来(先进后出)。


7.为什么内存要在堆栈中分配

内存分配方式有三种:

  1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  2. 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  3. 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

8.查找算法有哪些  

查找算法总结 - 深夜十二点三十三 - 博客园

二分查找、插值查找和斐波那契查找
9.什么是时间复杂度    

算法(一)时间复杂度

时间复杂度:评估执行程序所需的时间。可以估算出程序对处理器的使用程度。
空间复杂度:评估执行程序所需的存储空间。可以估算出程序对计算机内存的使用程度。


10.二分查找的时间复杂度是什么

O(logn)

阿里三面

1.自我介绍
2.详细说我第一个项目

3.如何把ip地址存储在一个int 变量里面?

一个int占多少个字节?

Java语言中一个字符占几个字节?


4.详细介绍第二个项目

hr面

1. 你对阿里面试官的印象如何?你从面试官上学到了哪些东西

2.你每天的生活安排是什么样子的?

3.你为什么选择来阿里?

4.阿里的开源框架用了哪些呢?说说weex?

5.你以后的技术规划是什么样的?

6.你最有成就的项目是哪个?

7.为什么选择android开发?

8.为什么选择nodejs开发作为第二条技术路线?

9.你有什么要问我的吗?(问了面试官入职培训)

*hr面10天过后,收到了饿了么offer。*

(三)面试总结

3.1、简历

网上有很多对程序员简历的一些指导,这里就不重述,大家可以搜下网上其他大神的总结,结合自身情况修改下。我有几点建议:

1.尽量不要花哨,程序员和设计师或者产品运营还不一样,我们的简历成功与否决定权还是在技术面试官那,而他们看重的是你的项目经验内容和技术等描述。

2.技能描述这块尽量只写你懂得而且理解深刻的,可以适当加入一些新技术或流行框架,不过这块需要理解,没来得及看源码的可以看看大神们对它的总结,网上一大堆。

3.项目经验这块尽量加入关键词,比如使用了什么技术、用到哪些设计模式、优化数据对比、扩展总结之类的。而非一味地介绍这个项目内容(那是产品经理的描述),比如性能优化这块,分为UI性能优化、内存优化、数据库优化、网络优化、耗电优化等等。可以从1.如何发现问题,2.怎么解决问题,3.解决效果对比,这几个方面去描述。举个简单例子——UI优化,可以从 UI出现什么问题(卡顿不流畅),怎么查找问题(手机开发者权限>GPU过度绘制 发现层级问题,TraceView CPU使用情况分析),怎么解决问题(降低层级、自定义View绘图出现问题等),解决问题后性能再次对比。

3.2、技能储备

3.2.1Java

3.2.1.0、 SparseArray

当新建一个key为整型的HashMap时,会出现如下的提示信息,推荐使用SparseArray来替代HashMap:

接下来就来介绍下SparseArray:

a.数据结构:又称稀疏数组,内部通过两个数组分别存储key和value,并用压缩的方式来存储数据

b.优点:可替代key为int、value为Object的HashMap,相比于HashMap

· 能更节省存储空间

· 由于key指定为int,能节省int和Integer的装箱拆箱操作带来的性能消耗

· 扩容时只需要数组拷贝工作,而不需重建哈希表

c.适用场景:数据量不大(千以内)、空间比时间重要、需要使用Map且key为整型;不适合存储大容量数据,此时性能将退化至少50%

d.使用

添加:public void put(int key, E value)

删除:

  • · public void delete(int key)
  • · public void remove(int key)实际上内部会调用delete方法

查找:

  • · public E get(int key)
  • · public E get(int key, E valueIfKeyNotFound)可设置假设key不存在时默认返回的value
  • · public int keyAt(int index)获取相应的key
  • · public E valueAt(int index)获取相应的value

e.get/put过程:元素会按照key从小到大进行存储,先使用二分法查询key对应在数组中的下标index,然后通过该index进行增删查。源码分析见SparseArray解析。

3.2.1.1、HashMap和Hashtable区别?

HashMap和HashTable

这个一定要去看源码!看源码!看源码!实在看不下去的可以上网看别人的分析。简单总结有几点:

1.HashMap支持null Key和null Value;Hashtable不允许。这是因为HashMap对null进行了特殊处理,将null的hashCode值定为了0,从而将其存放在哈希表的第0个bucket。

2.HashMap是非线程安全,HashMap实现线程安全方法为Map map = Collections.synchronziedMap(new HashMap());Hashtable是线程安全

3.HashMap默认长度是16,扩容是原先的2倍;Hashtable默认长度是11,扩容是原先的2n+1

4.HashMap继承AbstractMap;Hashtable继承了Dictionary

扩展,HashMap 对比 ConcurrentHashMap ,HashMap 对比 SparseArray,LinkedArray对比ArrayList,ArrayList对比Vector

3.2.1.2、Java垃圾回收机制

https://blog.csdn.net/yulong0809/article/details/77421615

需要理解JVM,内存划分——方法区、内存堆、虚拟机栈(线程私有)、本地方法栈(线程私有)、程序计数器(线程私有), 理解回收算法——标记清除算法、可达性分析算法、标记-整理算法、复制算法、分代算法,优缺点都理解下。

3.2.1.3、类加载机制

这个可以结合 热修复 深入理解下。

Java虚拟机四:类加载机制

Android动态加载技术基础2之类加载

 

3.2.1.4、线程和线程池,并发,锁等一系列问题

这个可以扩展下 如何自己实现一个线程池?

ThreadPoolExecutor配置细节(自定义线程池)

Android的线程和线程池(包含HandlerThread、IntentService)

线程和线程池

在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销。所以此时线程池就派上了用场。一个线程池中会缓存一定数量的线程,通过线程池就可以避免因为频繁创建和销毁线程所带来的系统开销。
线程池主要有以下三个优点

  • 重用线程池中的线程,避免因为线程的创建和销毁所带来的的性能开销。
  • 有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
  • 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。

 

3.2.1.5、HandlerThread、IntentService理解

HandlerThread:

HandlerThread继承了Thread,它是一种可以使用HandlerThread,它的实现也很简单,就是在run方法中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了。普通的Thread主要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。HandlerThread是一个很有用的类,它在Android中的一个具体使用场景是IntentService。由于HandlerThreadrun方法是一个无限循环,因此当明确不需要再使用HandlerThread时,可以通过它的quit或者quitSafely方法来终止线程的执行。

  • IntentService源码分析
  • Android面试题-Service是否在main thread中执行, service里面是否能执行耗时的操作?

IntentService:

是一个特殊的Service,它继承了Service并且它是一个抽象类,因此必须创建它的子类才能使用IntentServiceIntentService可用于执行后台耗时的任务,当任务执行后它会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务,因为它优先级高不容易被系统杀死。在实现上,IntentService封装了HandlerThreadHandler。IntentServiceonHandlerIntent方法是一个抽象方法,它需要我们在子类中实现,它的作用是从Intent参数中区分具体的任务并执行这些任务。

 

3.2.1.7、int、Integer有什么区别

主要考值传递和引用传递问题

int和Integer的区别

int和Integer的基本区别:

  1. int是基本数据类型,Integer是int包装类。
  2. Integer变量必须实例化后才能使用,int可以直接使用
  3. Integer的默认值是null,int默认值是0
  4. Integer变量实际上是对象的引用,指向new的Integer对象,int是直接存储数据

3.2.1.8、手写生产者/消费者 模式

【Java】生产者消费者模式实现

手写一个生产者/消费者模式(三种方式实现)

生产者/消费者实现原理:Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

3.2.1.9、RecyclerView

看完这篇,面试RecyclerView的时候再也不怕了

android面试题(30)-RecycleView和ListView

面试常考点之RecyclerView回收和复用机制最全分析

Android自定义控件进阶篇,自定义LayoutManager

3.2.2、Android

3.2.2.1、android启动模式

需要了解下Activity栈和taskAffinity

  1. 1.Standard:系统默认,启动一个就多一个Activity实例
  2. 2.SingleTop:栈顶复用,如果处于栈顶,则生命周期不走onCreate()和onStart()去重新创建一个新的activity实例,会调用onNewIntent()复用位于栈顶的activity实例对象。如果不位于栈顶仍旧会重新创建activity的实例对象。适合推送消息详情页,比如新闻推送详情Activity;
  3. 3.SingleTask:设置了singleTask启动模式的activity在启动时,如果位于activity栈中,就会复用该activity,这样的话,在该实例之上的所有activity都依次进行出栈操作,即执行对应的onDestroy()方法,直到当前要启动的activity位于栈顶。一般应用在网页的图集,一键退出当前的应用程序,app首页。
  4. 4.SingleInstance:这个是SingleTask加强本,如果使用singleInstance启动模式的activity在启动的时候会复用已经存在的activity实例。不管这个activity的实例是位于哪一个应用当中,都会共享已经启动的activity的实例对象。使用了singlestance的启动模式的activity会单独的开启一个共享栈,这个栈中只存在当前的activity实例对象,适用新开Activity和app能独立开的,如系统闹钟。SingleTask和SingleInstance好像会影响到onActivityResult的回调,具体问题大家搜下,我就不详说。
  5. Intent也需要进一步了解,Action、Data、Category各自的用法和作用,还有常用的Intent.FLAG_ACTIVITY_SINGLE_TOP,Intent.FLAG_ACTIVITY_NEW_TASK,Intent.FLAG_ACTIVITY_CLEAR_TOP,等等,具体看下源码吧。

Activity的启动过程

插件化原理解析——Activity生命周期管理

 

3.2.2.2、View的绘制流程

ViewRoot

->performTraversal()->performMeasure()->performLayout()->perfromDraw()->View/ViewGroup measure()->View/ViewGroup onMeasure()->View/ViewGroup layout()->View/ViewGroup onLayout()->View/ViewGroup draw()->View/ViewGroup onDraw()

看下invalidate方法,有带4个参数的,和不带参数有什么区别;requestLayout触发measure和layout,如何实现局部重新测量,避免全局重新测量问题。

3.2.2.3、事件分发机制

->dispatchTouchEvent()->onInterceptTouchEvent()->onTouchEvent()requestDisallowInterceptTouchEvent(boolean)

还有onTouchEvent()、onTouchListener、onClickListener的先后顺序

3.2.2.4、消息分发机制

Handler机制

这个考得非常常见。一定要看源码,代码不多。带着几个问题去看:

1.为什么一个线程只有一个Looper、只有一个MessageQueue? 2.如何获取当前线程的Looper?是怎么实现的?(理解ThreadLocal) 3.是不是任何线程都可以实例化Handler?有没有什么约束条件? 4.Looper.loop是一个死循环,拿不到需要处理的Message就会阻塞,那在UI线程中为什么不会导致ANR? 5.Handler.sendMessageDelayed()怎么实现延迟的?结合Looper.loop()循环中,Message=messageQueue.next()和MessageQueue.enqueueMessage()分析。

 

3.2.2.5、AsyncTask源码分析

优劣性分析,这个网上一大堆,不重述。这两个问题我面试过程有3家公司问到。

Android Asynctask与Handler的比较,优缺点区别

首先从Android3.0开始,系统要求网络访问必须在子线程中进行,否则网络访问将会失败并抛出NetworkOnMainThreadException这个异常,这样做是为了避免主线程由于耗时操作所阻塞从而出现ANR现象。AsyncTask封装了线程池和Handler。AsyncTask有两个线程池:SerialExecutor和THREAD_POOL_EXECUTOR。前者是用于任务的排队,默认是串行的线程池:后者用于真正的执行任务。AsyncTask还有一个Handler,叫InternalHandler,用于将执行环境从线程池切换到主线程。AsyncTask内部就是通过InternalHandler来发送任务执行的进度以及执行结束等消息。

AsyncTask排队执行过程:系统先把参数Params封装为FutureTask对象,它相当于Runnable,接着FutureTask交给SerialExcutor的execute方法,它先把FutureTask插入到任务队列tasks中,如果这个时候没有正在活动的AsyncTask任务,那么就会执行下一个AsyncTask任务,同时当一个AsyncTask任务执行完毕之后,AsyncTask会继续执行其他任务直到所有任务都被执行为止。

关于线程池,AsyncTask对应的线程池ThreadPoolExecutor都是进程范围内共享的,都是static的,所以是AsyncTask控制着进程范围内所有的子类实例。由于这个限制的存在,当使用默认线程池时,如果线程数超过线程池的最大容量,线程池就会爆掉(3.0默认串行执行,不会出现这个问题)。针对这种情况。可以尝试自定义线程池,配合AsyncTask使用。

使用的优点:  简单快捷;过程可控

使用的缺点:  在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.

 

3.2.2.7、Binder机制,进程通信

Android用到的进程通信底层基本都是Binder,AIDL、Messager、广播、ContentProvider。不是很深入理解的,至少ADIL怎么用,Messager怎么用,可以写写看,另外序列化(Parcelable和Serilizable)需要做对比,这方面可以看看任玉刚大神的android艺术开发探索一书。

3.2.2.8、动态权限适配问题、自定义权限、换肤实现原理

国产 Android 权限申请最佳适配方案 —— permissions4m

Rxpermissions

这方面看下鸿洋大神的博文吧

换肤实现原理 :

LayoutInflater.setFactory还非常适合一个场景,就是换肤,换肤需要解决的核心问题有两个:

  1. 外部资源的加载;可以通过构造AssetManager,反射调用其addAssetPath就可以完成。
  2. 定位到需要换肤的View;可以利用在onCreateView中,根据view的属性来定位,例如你可以让需要换肤的view添加一个自定义的属性skin_enabled=true(最开始有打印属性),并且利用一些手段拿到构造到的view,就能在View构造阶段定位的需要换肤的View。

 

3.2.2.9、SharedPreference原理,能否跨进程?如何实现?

3.3、性能优化问题

3.3.1、UI优化

a.合理选择RelativeLayout、LinearLayout、FrameLayout,RelativeLayout会让子View调用2次onMeasure,而且布局相对复杂时,onMeasure相对比较复杂,效率比较低,LinearLayout在weight>0时也会让子View调用2次onMeasure。LinearLayout weight测量分配原则。

b.使用标签

c.减少布局层级,可以通过手机开发者选项>GPU过渡绘制查看,一般层级控制在4层以内,超过5层时需要考虑是否重新排版布局。

d.自定义View时,重写onDraw()方法,不要在该方法中新建对象,否则容易触发GC,导致性能下降

e.使用ListView时需要复用contentView,并使用Holder减少findViewById加载View。

f.去除不必要背景,getWindow().setBackgroundDrawable(null)

g.使用TextView的leftDrawabel/rightDrawable代替ImageView+TextView布局

3.3.2、内存优化

主要为了避免OOM和频繁触发到GC导致性能下降

a.Bitmap.recycle(),Cursor.close,inputStream.close()

b.大量加载Bitmap时,根据View大小加载Bitmap,合理选择inSampleSize,RGB_565编码方式;使用LruCache缓存

c.使用 静态内部类+WeakReference 代替内部类,如Handler、线程、AsyncTask

d.使用线程池管理线程,避免线程的新建

e.使用单例持有Context,需要记得释放,或者使用全局上下文

f.静态集合对象注意释放

g.属性动画造成内存泄露

h.使用webView,在Activity.onDestory需要移除和销毁,webView.removeAllViews()和webView.destory()

Webview内存泄露优化

备:使用LeakCanary检测内存泄露

3.2.3、响应速度优化

Activity如果5秒之内无法响应屏幕触碰事件和键盘输入事件,就会出现ANR,而BroadcastReceiver如果10秒之内还未执行操作也会出现ANR,Serve20秒会出现ANR 为了避免ANR,可以开启子线程执行耗时操作,但是子线程不能更新UI,因此需要Handler消息机制、AsyncTask、IntentService进行线程通信。

备:出现ANR时,adb pull data/anr/tarces.txt 结合log分析

ANR问题:

1、ANR排错一般有三种类型

  1. KeyDispatchTimeout(5 seconds) --主要是类型按键或触摸事件在特定时间内无响应
  2. BroadcastTimeout(10 seconds) --BroadcastReceiver在特定时间内无法处理完成
  3. ServiceTimeout(20 secends) --小概率事件 Service在特定的时间内无法处理完成

2、哪些操作会导致ANR 在主线程执行以下操作:

  1. 高耗时的操作,如图像变换
  2. 磁盘读写,数据库读写操作
  3. 大量的创建新对象

3、如何避免

  1. UI线程尽量只做跟UI相关的工作
  2. 耗时的操作(比如数据库操作,I/O,连接网络或者别的有可能阻塞UI线程的操作)把它放在单独的线程处理
  3. 尽量用Handler来处理UIThread和别的Thread之间的交互

4、解决的逻辑

  1. 使用AsyncTask
    1. 在doInBackground()方法中执行耗时操作
    2. 在onPostExecuted()更新UI
  2. 使用Handler实现异步任务
    1. 在子线程中处理耗时操作
    2. 处理完成之后,通过handler.sendMessage()传递处理结果
    3. 在handler的handleMessage()方法中更新UI
    4. 或者使用handler.post()方法将消息放到Looper中

5、如何排查

  1. 首先分析log
  2. 从trace.txt文件查看调用stack,adb pull data/anr/traces.txt ./mytraces.txt
  3. 看代码
  4. 仔细查看ANR的成因(iowait?block?memoryleak?)

6、监测ANR的Watchdog

最近出来一个叫LeakCanary

#FC(Force Close) ##什么时候会出现

  1. Error
  2. OOM,内存溢出
  3. StackOverFlowError
  4. Runtime,比如说空指针异常

##解决的办法

  1. 注意内存的使用和管理
  2. 使用Thread.UncaughtExceptionHandler接口

3.2.4、其他性能优化

a.常量使用static final修饰 b.使用SparseArray代替HashMap c.使用线程池管理线程 d.ArrayList遍历使用常规for循环,LinkedList使用foreach e.不要过度使用枚举,枚举占用内存空间比整型大 f.字符串的拼接优先考虑StringBuilder和StringBuffer g.数据库存储是采用批量插入+事务

android面试(16)-数据库存储框架greendao

深入理解Java中的String(大坑)_String,java

3.2.4.2:String全面解析

不是特别需要请不要使用new关键字创建字符串

从前文我们知道使用new关键字创建String的时候,即便串池中存在相同String,仍然会再次在堆内存中创建对象,会浪费内存,另一方面对象的创建相较于从串池中取效率也更低下。

String StringBuffer StringBuilder的区别:

关于三者的区别,在面试题中经常的出现,String对象不可变,因此在进行任何内容上的修改时都会创建新的字符串对象,一旦修改操作太多就会造成大量的资源浪费。

StringBuffer和StringBuilder在进行字符串拼接的时候不会创建新的对象,而是在原对象上修改,不同之处在于StringBuffer线程安全,StringBuilder线程不安全。所以在进行字符串拼接的时候推荐使用StringBuffer或者StringBuilder。

 

3.3、设计模式

1.单例模式:好几种写法,要求会手写,分析优劣。一般双重校验锁中用到volatile,需要分析volatile的原理

2.观察者模式:要求会手写,有些面试官会问你在项目中用到了吗?实在没有到的可以讲一讲EventBus,它用到的就是观察者模式

3.适配器模式:要求会手写,有些公司会问和装饰器模式、代理模式有什么区别?

1).适配器模式:

2).代理模式和装饰模式有点像,都是持有了被代理或者被装饰对象的引用。它们两个最大的不同就是装饰模式对引用的对象增加了功能,而代理模式只是对引用对象进行了控制却没有对引用对象本身增加功能。

4.建造者模式+工厂模式:要求会手写

5.策略模式:这个问得比较少,不过有些做电商的会问。

6.MVC、MVP、MVVM:比较异同,选择一种你拿手的着重讲就行

3.4、数据结构

1.HashMap、LinkedHashMap、ConcurrentHashMap,在用法和原理上有什么差异,很多公司会考HashMap原理,通过它做一些扩展,比如中国13亿人口年龄的排序问题,年龄对应桶的个数,年龄相同和hash相同问题类似。

2.ArrayList和LinkedList对比,这个相对简单一点

ArrayListlinkedList区别

共性:ArrayList与LinkedList都是List接口的实现类,因此都实现了List的所有未实现的方法,只是实现的方式有所不同。

区别:List接口的实现方式不同

1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 

2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。 

3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

ArrayList实现了List接口,以数组的方式来实现的,因此对于快速的随机取得对象的需求,使用ArrayList实现执行效率上会比较好。

LinkedList是采用链表的方式来实现List接口的,因此在进行insert和remove动作时效率要比ArrayList高。适合用来实现Stack(堆栈)与Queue(队列)

 

3.平衡二叉树、二叉查找树、红黑树,这几个我也被考到。

4.Set原理,这个和HashMap考得有点类似,考hash算法相关,被问到过常用hash算法。HashSet内部用到了HashMap

 

3.5、算法

· 排序算法有哪些?

· 最快的排序算法是哪个?

· 手写一个冒泡排序

· 手写快速排序代码

· 快速排序的过程、时间复杂度、空间复杂度

· 手写堆排序

· 堆排序过程、时间复杂度及空间复杂度

· 写出你所知道的排序算法及时空复杂度,稳定性

· 二叉树给出根节点和目标节点,找出从根节点到目标节点的路径

· 给阿里2万多名员工按年龄排序应该选择哪个算法?

· GC算法(各种算法的优缺点以及应用场景)

· 蚁群算法与蒙特卡洛算法

· 子串包含问题(KMP 算法)写代码实现

· 一个无序,不重复数组,输出N个元素,使得N个元素的和相加为M,给出时间复杂度、空间复杂度。手写算法

· 万亿级别的两个URL文件A和B,如何求出A和B的差集C(提示:Bit映射->hash分组->多文件读写效率->磁盘寻址以及应用层面对寻址的优化)

· 百度POI中如何试下查找最近的商家功能(提示:坐标镜像+R树)。

· 两个不重复的数组集合中,求共同的元素。

· 两个不重复的数组集合中,这两个集合都是海量数据,内存中放不下,怎么求共同的元素?

· 一个文件中有100万个整数,由空格分开,在程序中判断用户输入的整数是否在此文件中。说出最优的方法

· 一张Bitmap所占内存以及内存占用的计算

· 2000万个整数,找出第五十大的数字?

· 烧一根不均匀的绳,从头烧到尾总共需要1个小时。现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?

· 求1000以内的水仙花数以及40亿以内的水仙花数

· 5枚硬币,2正3反如何划分为两堆然后通过翻转让两堆中正面向上的硬8币和反面向上的硬币个数相同

· 时针走一圈,时针分针重合几次

· N*N的方格纸,里面有多少个正方形

· x个苹果,一天只能吃一个、两个、或者三个,问多少天可以吃完?

3.6、源码理解

项目中多多少少会用到开源框架,很多公司都喜欢问原理和是否看过源码,比如网络框架Okhttp,这是最常用的,现在Retrofit+RxJava也很流行。

3.6.1、网络框架库 Okhttp

okhttp源码一定要去看下,里面几个关键的类要记住,还有连接池,拦截器都需要理解。被问到如何给某些特定域名的url增加header,如果是自己封装的代码,可以在封装Request中可以解决,也可以增加拦截器,通过拦截器去做。

推荐一篇讲解Okhttp不错的文章

3.6.2、消息通知 EventBus

 EventBus 源码和设计之禅

1.EventBus原理:建议看下源码,不多。内部实现:观察者模式+注解+反射

2.EventBus可否跨进程问题?代替EventBus的方法(RxBus)

3.6.3、图片加载库(Fresco、Glide、Picasso)

1.项目中选择了哪个图片加载库?为什么选择它?其他库不好吗?这几个库的区别

2.项目中选择图片库它的原理,如Glide(LruCache结合弱引用),那么面试官会问LruCache原理,进而问LinkedHashMap原理,这样一层一层地问,所以建议看到不懂的追进去看。如Fresco是用来MVC设计模式,5.0以下是用了共享内存,那共享内存怎么用?Fresco怎么实现圆角?Fresco怎么配置缓存?

Android图片加载框架最全解析(三),深入探究Glide的缓存机制

Android图片加载框架最全解析(五),Glide强大的图片变换功能

3.6.4、消息推送Push

1.项目中消息推送是自己做的还是用了第三方?如极光。还有没有用过其他的?这几家有什么优势区别,基于什么原因选择它的?

2.消息推送原理是什么?如何实现心跳连接?

3.6.5、TCP/IP、Http/Https

网络这一块如果简历中写道熟悉TCP/IP协议,Http/Https协议,那么肯定会被问道,我就验证了。一般我会回答网络层关系、TCP和UDP的区别,TCP三次握手(一定要讲清楚,SYN、ACK等标记位怎样的还有报文结构都需要熟悉下),四次挥手。为什么要三次握手?DDoS攻击。为什么握手三次,挥手要四次?Http报文结构,一次网络请求的过程是怎样的?Http和Https有什么不同?SSL/TLS是怎么进行加密握手的?证书怎么校验?对称性加密算法和非对称加密算法有哪些?挑一个熟悉的加密算法简单介绍下?DNS解析是怎样的?

网络通信必备基础之Http协议&TCP/IP协议(二) https://zhuanlan.zhihu.com/p/92383936

网络通信必备基础之SSL握手&DNS解析(三) https://zhuanlan.zhihu.com/p/92384525

3.6.7、热更新、热修复、插件化(这一块要求高点,一般高级工程师是需要理解的)

了解classLoader

3.7、新技术

RxJava、RxBus、RxAndroid,这个在面试想去的公司时,可以反编译下他们的包,看下是不是用到,如果用到了,面试过程难免会问道,如果没有,也可以忽略,但学习心强的同学可以看下,比较是比较火的框架。

Retrofit,熟练okhttp的同学建议看下,听说结合RxJava很爽。

Kotlin

 

Android面试指导2

看面试题可以是为了面试,也可以是对自己学到的东西的一种查漏补缺,更加深刻的去了解一些核心知识点

1、Java 相关

  1. 容器(HashMap、HashSet、LinkedList、ArrayList、数组等)
需要了解其实现原理,还要灵活运用,如:自己实现 LinkedList、两个栈实现一个队列,数组实现栈,队列实现栈等。
  1. 内存模型 JVM内存模型
  2. 垃圾回收算法(JVM)
  3. 类加载过程(需要多看看,重在理解,对于热修复和插件化比较重要)
  4. 反射
  5. 多线程和线程池
  6. HTTP、HTTPS、TCP/IP、Socket通信、三次握手四次挥手过程
  7. 设计模式(六大基本原则、项目中常用的设计模式、手写单例等)
  8. 断点续传

2、Android 基础

  1. 自定义 View(参考链接:自定义View,有这一篇就够了 - 简书、Android 自定义 View)
  2. 事件拦截分发(Android事件分发机制,大表哥带你慢慢深入 - 简书 )
  3. 解决过的一些性能问题,在项目中的实际运用。
  4. 性能优化工具 (TraceView、Systrace、调试 GPU 过度绘制 & GPU 呈现模式分析、Hierarchy Viewer、MAT、Memory Monitor & Heap Viewer & Allocation Tracker 等)
  5. 性能优化
    (1)网络:API 优化、流量优化、弱网优化
    (2)内存:OOM 处理、内存泄漏、内存检测、分析、Bitmap 优化
    (3)绘制
    (4)电量:WeakLock 机制、JobScheduler 机制
    (5)APK 瘦身:  Lint
    (6)内存抖动
    (7)内存泄漏
    (8)卡顿
    (9)性能优化:布局优化、过度渲染处理、ANR 处理、监控、埋点、Crash 上传。
  6. IntentService 原理及应用
  7. 缓存自己如何实现(LRUCache 原理)
  8. 图形图像相关:OpenGL ES 管线流程、EGL 的认识、Shader 相关
  9. SurfaceView、TextureView、GLSurfaceView 区别及使用场景
  10. 动画、差值器、估值器(Android中的View动画和属性动画 - 简书、Android 动画 介绍与使用)
  11. MVC、MVP、MVVM
  12. Handler、ThreadLocal、AsyncTask
  13. Gradle(Groovy 语法、Gradle 插件开发基础)
  14. 热修复、插件化

3、Android Framework

  1. AMS 、PMS
  2. Activity 启动流程
  3. Binder 机制(IPC、AIDL 的使用)
  4. 为什么使用 Parcelable,好处是什么?
  5. Android 图像显示相关流程,Vsync 信号等

4、三方源码

  1. Glide :加载、缓存、LRU 算法
  2. EventBus
  3. LeakCanary Leakcanary原理浅析 
  4. ARouter Arouter核心思路和源码详解  Android跳转-ARouter详细使用教程
  5. 插件化(不同插件化机制原理与流派,优缺点。局限性)
  6. 热修复
  7. RXJava  一张图看懂Rxjava的原理
  8. Retrofit

5、算法与数据结构

  1. 单链表:反转、插入、删除
  2. 双链表:插入、删除
  3. 手写常见排序、归并排序、堆排序
  4. 二叉树前序、中序、后序遍历
  5. 最大 K 问题
  6. 广度、深度优先搜索算法

 

 

你可能感兴趣的:(面试)