需求已改活已加,加班通宵看朝霞。 终是上线已延期,bug还是改不完。
面试造火箭,工作拧螺丝,虽然我只想拧螺丝,可是我需要用造火箭的技术去寻找拧螺丝的工作,如何能在面试过程中让自己处于不败的地步呢,刷题是一个比较好的捷径,今天就汇总了一些比较经典的面试题进行了汇总,分享给大家。
大厂面试真题
嘀d出行:
- handler原理,主线程发送message给子线程
- recyclerview列表的优化
- 自定义view,onmeasure的如何测量,测量模式起什么作用?
- 大图片如何处理?
- sp支持多进程吗?多线程呢?
- 数据库读写在同一个线程吗?
- 一个文本文件中每行有一个手机号或电话号,给定一个手机号,判断该文件中是否存在。给出时间复杂度较低的方案。
最y:
- 对着项目一通问,包括项目结构、自己参与的部分等等
- OKhttp原理,链式调用、链接复用等
- 对exoplayer、ijkplayer的了解,如果在视频未播放时就调用暂停,会有什么问题?如何解决视频的边下边播?如何解决列表中的视频滑动到屏幕中间就自动开始播放?
- 列表滑动卡顿该如何定位问题?
- 对以空间换时间的理解?
- HashMap、LinkedHashMap原理
作y帮:
- 主线程给子线程发送消息,handler、threadlocal、threadlocalmap、thread之间是怎么联系的
- glide的结构设计、btimap的复用和系统的复用比较,有什么优点?与生命周期的绑定
- 自定义view测量、布局、绘制,有没有做过复杂的自定义view,举个例子
- 一个scrollview里有个button,button有点击事件,列表可滑动,怎么去做事件分发的
- 项目架构设计、mvp的实现、model里的请求分页怎么实现?
- 视频播放器、exoplay的优缺点,ijkplay的优缺点
小m:
- handler、massage、massager、Loop之间的关系和区别
- view的绘制和事件分发,Android的绘制机制?布局的绘制流程?
- Activity在oncreate中finish,生命周期是怎样的?
- Glide、okhhtp、retrofit等三方库的原理,简单阐述
- 视频播放器封装、弹幕的实现,自己项目的结构和路由框架、新闻列表的实现、mpv的实现等
- android内存泄露有哪些?怎么处理?handler为什么会发生内存泄露?它的gcRoot是什么?强软弱虚引用哪些可以解决内存泄露?为什么?
- 一个view上每秒显示一个数字,每隔一秒改变一次,想出解决办法,越多越好
- 代码健壮性和质量怎么控制?appbug怎么统计的?怎么处理的?
- 一个string值传入方法,值改变吗?换成stringbuild呢?
- Java垃圾回收、分代算法的原理,如何判定对象死亡?gcRoot有哪些?Java内存模型,哪些区可以作为gcRoot?内存怎么释放?线程的工作内存放在哪?强软弱虚四种引用的区别?
- 给定数组-1,0,1,0,-1,-4,0找出其中3个数相加为0的全部组合,给出解决方案
- 判断单链表相交,找出节点,手写代码
- 反转单链表,手写代码
- 给定两个链表,存储着两个16进制数,链表的一个节点存储着16进制数的其中一个数,从高位到低位,求相加的值,返回一个链表,链表中保存相加的结果。(先反转链表,然后逐位相加,记录进位值,再与高位相加)手写代码
百d:
- 抽奖转盘,分四份,中奖概率为5%,UI和逻辑怎么实现
- 数据库查询,至少参与了三项考试,且分数均超过80的人,写出sql语句
- 线程同步:线程1循环输出1到10,线程2循环输出1到10,启动线程1、2,要求做到线程2输出5之后,线程1才开始输出(用wait、notify实现)
- jvm内存模型,垃圾回收机制
- 手写单例
- kotlin类的扩展,【】方括号该扩展什么?
- 怎么在项目中进行架构设计的?MVP模式的优点,如何实现?MVVM了解吗?
- 网络请求大量图片并展示在页面上,需要注意什么?网络请求资源复用、图片缓存等
- 内存泄露有哪些?怎么处理?
- 数据查出来为什么用cursor游标,而不直接返回个list集合?
- 缓存了10000条数据、怎么查出来并显示?项目中数据库大概是什么量级的?数据量及占内存量?
- content provide是干什么的?Google为什么设计它?
- 一个网格页面、显示9张图片,弱网情况下,滑到下一页,怎么去调度线程加载下一页面的图片?
- 了解的设计模式,代理模式流程、观察者模式流程、涉及几个类
- 了解哪些google推出的比较新的库、livedata?databinding?jetpack?
- kotlin相对于Java有什么优势?函数式编程的优势?函数式和面向对象比较
- 商城里有图片、文档、视频,字段有(id,name,type,pic,author,price),选择购买后在我的订单页可以查看,可选择下载至手机本地,给出客户端实现方案、写关键代码,给出数据库实现,写关键sql语句
- 文件中每行有一个手机号对应用户信息,给定手机号查找出对应信息,如果手机号排序了,怎么查?用了二分法查,写了简单的二分法实现
- 任意二叉树,求出其中最远的两个节点间的距离
- 对未来职业生涯的规划?怎么去做有深度的工程师?开发中遇到的困难?怎么解决的?自己项目里做的亮点!面对一份新工作怎么去适应?自己公司开发项目的流程是怎样的?你参与了哪些流程?项目中你有没有主动提出过对产品优化的意见?
Android面试原题解析
1.Bundle被用来传递数据,为什么不用HashMap代替?
从Bundle源码分析,说明我们的Bundle它实现的数据传递是通过我们的ArrayMap实现的,所以说实际上我们的这个Bundle为什么不用HashMap来替代。实际上就是说为什么我们的Android传递数据的时候要用ArrayMap,而不用HashMap。
Bundle的优势:
1). ArrayMap适合于小数据量操作,如果在数据量比较大的情况下,它的性能将退化。HashMap内部则是数组 链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。而使用Bundle的场景大多数为小数据量。所以使用ArrayMap实现更合适。
2).Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,Bundle使用Parcelable进行序列化,而HashMap则是使用Serializable进行序列化。Parcelable它的性能是要优于Serializable的。
2. 什么是内存泄漏,Java是如何处理它的?
内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
产生的原因:一个长生命周期的对象持有一个短生命周期对象的引用。
通俗讲:就是该回收的对象,因为引用问题没有被回收,所以最终的结果,如果内存泄漏太多,那么我们内存会不断的变大,最后会产生一个OOM的过程。
Java是如何处理它的,Java本身并不会把我们处理的内存泄漏。
3. LRUCache原理
LRUCache是个泛型类,主要原理是:把最近使用的对象用强引用存储在LinkedHashMap中,当缓存满时,把最近很少使用的对象从内存中移除,并提供get/put方法完成缓存的获取和添加。LRUCache线程是安全的,因为使用了synchronized关键字。
当调用put方法时,将元素添加到链表头,如果链表头没有该元素,大小不变,如果没有,需调用trimToSize方法判断是否超过最大缓存量,trimToSize()方法中有一个while(true)死循环,如果缓存大小大于最大缓存值,会不断删除LinkedHashMap中队尾的元素,即最少访问的,直到缓存大小小于最大缓存值。当调用LRUCache的get方法时,LinkedHashMap会调用recordAccess方法将此元素添加到链表头部。
4. Synchronized使用和底层原理
Synchronized是Java中的关键字,是一种同步锁,它修饰的对象有一下几种:
1). 修饰一个方法:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 2). 修饰一个静态的方法:其作用的范围是整个静态的方法,作用的对象是这个类的所有对象; 3). 修饰一个代码块:被修饰的代码块称为同步块,起作用是范围的大括号{}起来的代码,作用的对象是调用这个代码块的对象; 4). 修饰一个类:其作用范围是Synchronized后面括号括起来的部分,作用主对象是这个类的所有对象。
Synchronized作用主要有三个:
1). 确保线程互斥的访问同步代码; 2). 保证共享变量的修改能够及时可见; 3). 有效解决重排序问题。
5. Java 关键字Synchronized 和 volatile 的区别?
一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰后,就具备了两层含义:
1).保证不同线程对该变量进行操作时的可见性,即一个线程修改了变量的值,新值对其他线程来说是立即可见的;
2). 禁止进行指令重排序。
volatile本职是告诉jvm当前变量在寄存器(内存)中的值是不确定的,需要从主存中读取。Synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞。
1). volatile仅能使用在变量上;Synchronized 可以使用在变量、方法、类上;
2). volatile仅能实现变量的修改可见性,不能保证原子性;Synchronized 则可以保证变量的修改可见性和原子性;
3). volatile不会造成线程阻塞;Synchronized 可能会造成线程阻塞;
4). volatile标记的变量不会被编辑器优化,Synchronized 标记的变量可以被编辑器优化。
6. 数组和链表的区别
数组:是将元素在内存中连续存储的;
它的优点:因为数据是连续存储的,内存地址连续,所以在查找数据的时候效率比较高;
它的缺点:在存储之前,我们需要申请一块连续的内存空间,并且在编译的时候就必须确定好它的空间大小。在运行的时候空间大小是无法随着你的需要进行增加或者减少而改变的,当数据量比较大的时候,有可能会出现越界的情况,数据比较小的时候,又有可能会浪费掉内存空间。在改变整个数据个数时,增加、插入、删除数据效率比较低。
链表:是动态申请内存空间,不需要像数组需要提前申请好内存的大小,链表只需要在用的时候申请就可以,根据需要来动态申请或者删除内存空间,对于数据增加和删除以及插入比数组灵活。还有就是链表中数据在内存中可以在任意的为止,通过应用来关联数据(就是存在元素的指针来联系)。
7. java中的线程创建方式,线程池的工作原理。
Java 中有三种创建线程的方式,或者4种
1).继承Thread类实现多线程; 2).实现Runnable接口; 3).实现Callable接口; 4).通过线程池。
线程池的工作原理:
线程池可以减少创建和销毁线程的次数,从而减少系统资源的消耗,当一个任务提交 到线程池时
a. 首先判断核心线程池中的线程是否已经满了,如果没满,则创建一个核心线程执行任务,否则进入下一步
b. 判断工作队列是否已满,没有满则加入工作队列,否则执行下一步
c. 判断线程数是否达到了最大值,如果不是,则创建非核心线程执行任务,否则执行饱和策略,默认抛出异常。
8.内存泄漏的场景和解决办法。
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也需要及时移除。
9.冷启动的流程是什么,如何优化。
当点击app的启动图标时,安卓系统会从Zygote进程中fork创建出一个新的进程分配给该应用,之后会依次创建和初始化Application类、创建MainActivity类、加载主题样式Theme中的windowBackground等属性设置给MainActivity以及配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后最后才进行contentView的measure/layout/draw显示在界面上
冷启动的生命周期简要流程:Application构造方法 –> attachBaseContext()–>onCreate –>Activity构造方法 –> onCreate() –> 配置主体中的背景等操作 –>onStart() –> onResume() –> 测量、布局、绘制显示。
冷启动的优化主要是视觉上的优化,解决白屏问题,提高用户体验,所以通过上面冷启动的过程。能做的优化如下:
a) 减少onCreate()方法工作量; b) 不要让Application参与业务的操作; c) 不要再Application进行耗时操作; d) 不要以静态变量的方式在Application保存数据; e) 减少布局的复杂度和层级; f) 减少主线程耗时。
10. Android中的线程有那些,原理与各自特点。
AsyncTask、HandlerThread和IntentService。
AsyncTask原理:内部是Handler和两个线程池实现的,Handler用于将线程切换到主线程,两个线程池一个用于任务的排队,一个用于执行任务,当AsyncTask执行execute方法时会封装出一个FutureTask对象,将这个对象加入队列中,如果此时没有正在执行的任务,就执行它,执行完成之后继续执行队列中下一个任务,执行完成通过Handler将事件发送到主线程。AsyncTask必须在主线程初始化,因为内部的Handler是一个静态对象,在AsyncTask类加载的时候他就已经被初始化了。在Android3.0开始,execute方法串行执行任务的,一个一个来,3.0之前是并行执行的。如果要在3.0上执行并行任务,可以调用executeOnExecutor方法。
HandlerThread原理:继承自Thread,start开启线程后,会在其run方法中会通过Looper创建消息队列并开启消息循环,这个消息队列运行在子线程中,所以可以将HandlerThread中的Looper实例传递给一个Handler,从而保证这个Handler的handleMessage方法运行在子线程中,Android中使用HandlerThread的一个场景就是IntentService。
IntentService原理:继承自Service,它的内部封装了HandlerThread和Handler,可以执行耗时任务,同时因为它是一个服务,优先级比普通线程高很多,所以更适合执行一些高优先级的后台任务,HandlerThread底层通过Looper消息队列实现的,所以它是顺序的执行每一个任务。可以通过Intent的方式开启IntentService,IntentService通过handler将每一个intent加入HandlerThread子线程中的消息队列,通过looper按顺序一个个的取出并执行,执行完成后自动结束自己,不需要开发者手动关闭。
11. ANR的原因
1).耗时的网络访问; 2).大量的数据读写; 3).数据库操作; 4).硬件操作例如camera; 5).调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候; 6).service binder的数量达到上限; 7).system server中发生WatchDog ANR; 8).service忙导致超时无响应; 9).其他线程持有锁,导致主线程等待超时; 10). 其他线程终止或崩溃导致主线程一直等待。
12. 说下你对Collection这个类的理解。
Collection是集合框架的顶层接口,是存储对象的容器,Colloction定义了接口的公用方法如add remove clear等等,它的子接口有两个,List和Set,List的特点有元素有序,元素可以重复,元素都有索引(角标),典型的有Vector:内部是数组数据结构,是同步的(线程安全的)。增删查询都很慢。ArrayList:内部是数组数据结构,是不同步的(线程不安全的)。替代了Vector。查询速度快,增删比较慢。LinkedList:内部是链表数据结构,是不同步的(线程不安全的)。增删元素速度快。
而Set的是特点元素无序,元素不可以重复HashSet:内部数据结构是哈希表,是不同步的。Set集合中元素都必须是唯一的,HashSet作为其子类也需保证元素的唯一性。判断元素唯一性的方式:通过存储对象(元素)的hashCode和equals方法来完成对象唯一性的。如果对象的hashCode值不同,那么不用调用equals方法就会将对象直接存储到集合中;如果对象的hashCode值相同,那么需调用equals方法判断返回值是否为true,若为false, 则视为不同元素,就会直接存储;若为true, 则视为相同元素,不会存储。如果要使用HashSet集合存储元素,该元素的类必须覆盖hashCode方法和equals方法。一般情况下,如果定义的类会产生很多对象,通常都需要覆盖equals,hashCode方法。建立对象判断是否相同的依据。
TreeSet:保证元素唯一性的同时可以对内部元素进行排序,是不同步的。判断元素唯一性的方式:根据比较方法的返回结果是否为0,如果为0视为相同元素,不存;如果非0视为不同元素,则存。TreeSet对元素的排序有两种方式:方式一:使元素(对象)对应的类实现Comparable接口,覆盖compareTo方法。这样元素自身具有比较功能。方式二:使TreeSet集合自身具有比较功能,定义一个比较器Comparator,将该类对象作为参数传递给TreeSet集合的构造函数。
总结:
以上真题案例,并不是靠死记硬背,也不是应付技术面试,而是认识自己的过程,对自身技术的认知,方便加深记忆,不断归纳总结,建立技术知识体系,让自己的能力提升一个新的台阶。学无止境,贵在坚持。
面试前的准备
接下来分享的系统学习资源以详解各大互联网公司的 Android 常见面试题为主线,从面试的角度带你介绍必备知识点,以及该知识点在项目中的实际应用。
帮你在现在的基础上,重新梳理和建立 Android 开发的知识体系。无论是你短期内想提升 Android 内功实力,突破自己工作中的能力瓶颈,还是准备参加 Android 面试,都会在这份资料中有所一些收获。
从架构基础开始,分了8个模块来逐步从基础进阶到架构师的环节:
多余的话就不讲了,接下来将分享面试的一个复习路线,如果你也在准备面试但是不知道怎么高效复习,可以参考一下我的复习路线,有任何问题也欢迎一起互相交流,加油吧!
首先是超级详细得不能再详细的Android开发学习思维导图,因为图片实在是太大了,所以我就只把二级目录的内容放出来。
接下来就需要梳理知识,提升储备了!(Android移动架构师七大专题学习资源)
- 架构师筑基必备技能:深入Java泛型 注解深入浅出 并发编程 数据传输与序列化 Java虚拟机原理 反射与类加载 动态代理 高效IO
- Android高级UI与FrameWork源码:高级UI晋升 Framework内核解析 Android组件内核 数据持久化
- 360°全方面性能调优:设计思想与代码质量优化 程序性能优化 开发效率优化
- 解读开源框架设计思想:热修复设计 插件化框架解读 组件化框架设计 图片加载框架 网络访问框架设计 RXJava响应式编程框架设计 IOC架构设计 Android架构组件Jetpack
- NDK模块开发:NDK基础知识体系 底层图片处理 音视频开发
- 微信小程序:小程序介绍 UI开发 API操作 微信对接
- Hybrid 开发与Flutter:Html5项目实战 Flutter进阶
知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结。
然后再是通过源码来系统性地学习
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
刷大厂面试题备战,增加大厂通过率
以上这些内容均免费分享给大家,需要完整版的朋友,点这里可以看到全部内容。或者点击 【这里】 查看获取方式。