前言
成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~。
A awesome android expert interview questions and answers(continuous updating ...)
从几十份顶级面试仓库和300多篇高质量面经中总结出一份全面成体系化的Android高级面试题集。
欢迎来到2020年中高级Android大厂面试秘籍,为你保驾护航金三银四,直通大厂的Android高级篇下。
五、架构设计
MVC MVP MVVM原理和区别?
架构设计的目的
通过设计是模块程序化,从而做到高内聚低耦合,让开发者能更专注于功能实现本身,提供程序开发效率、更容易进行测试、维护和定位问题等等。而且,不同的规模的项目应该选用不同的架构设计。
MVC
MVC是模型(model)-视图(view)-控制器(controller)的缩写,其中M层处理数据,业务逻辑等;V层处理界面的显示结果;C层起到桥梁的作用,来控制V层和M层通信以此来达到分离视图显示和业务逻辑层。在Android中的MVC划分是这样的:
- 视图层(View):一般采用XML文件进行界面的描述,也可以在界面中使用动态布局的方式。
- 控制层(Controller):由Activity承担。
- 模型层(Model):数据库的操作、对网络等的操作,复杂业务计算等等。
MVC缺点
在Android开发中,Activity并不是一个标准的MVC模式中的Controller,它的首要职责是加载应用的布局和初始化用户界面,并接受和处理来自用户的操作请求,进而作出响应。随着界面及其逻辑的复杂度不断提升,Activity类的职责不断增加,以致变得庞大臃肿。
MVP
MVP框架由3部分组成:View负责显示,Presenter负责逻辑处理,Model提供数据。
- View:负责绘制UI元素、与用户进行交互(在Android中体现为Activity)。
- Model:负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)。
- Presenter:作为View与Model交互的中间纽带,处理与用户交互的逻辑。
- View interface:需要View实现的接口,View通过View interface与Presenter进行交互,降低耦合,方便使用MOCK对Presenter进行单元测试。
MVP的Presenter是框架的控制者,承担了大量的逻辑操作,而MVC的Controller更多时候承担一种转发的作用。因此在App中引入MVP的原因,是为了将此前在Activty中包含的大量逻辑操作放到控制层中,避免Activity的臃肿。
MVP与MVC的主要区别:
- 1、(最主要区别)View与Model并不直接交互,而是通过与Presenter交互来与Model间接交互。而在MVC中View可以与Model直接交互。
- 2、Presenter与View的交互是通过接口来进行的,更有利于添加单元测试。
MVP的优点
- 1、模型与视图完全分离,我们可以修改视图而不影响模型。
- 2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部。
- 3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
- 4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)。
UI层一般包括Activity,Fragment,Adapter等直接和UI相关的类,UI层的Activity在启动之后实例化相应的Presenter,App的控制权后移,由UI转移到Presenter,两者之间的通信通过BroadCast、Handler、事件总线机制或者接口完成,只传递事件和结果。
MVP的执行流程:首先V层通知P层用户发起了一个网络请求,P层会决定使用负责网络相关的M层去发起请求网络,最后,P层将完成的结果更新到V层。
MVP的变种:Passive View
View直接依赖Presenter,但是Presenter间接依赖View,它直接依赖的是View实现的接口。相对于View的被动,那Presenter就是主动的一方。对于Presenter的主动,有如下的理解:
- Presenter是整个MVP体系的控制中心,而不是单纯的处理View请求的人。
- View仅仅是用户交互请求的汇报者,对于响应用户交互相关的逻辑和流程,View不参与决策,真正的决策者是Presenter。
- View向Presenter发送用户交互请求应该采用这样的口吻:“我现在将用户交互请求发送给你,你看着办,需要我的时候我会协助你”。
- 对于绑定到View上的数据,不应该是View从Presenter上“拉”回来的,应该是Presenter主动“推”给View的。(这里借鉴了IOC做法)
- View尽可能不维护数据状态,因为其本身仅仅实现单纯的、独立的UI操作;Presenter才是整个体系的协调者,它根据处理用于交互的逻辑给View和Model安排工作。
MVP架构存在的问题与解决办法
- 1、加入模板方法
将逻辑操作从V层转移到P层后,可能有一些Activity还是比较膨胀,此时,可以通过继承BaseActivity的方式加入模板方法。注意,最好不要超过3层继承。
- 2、Model内部分层
模型层(Model)中的整体代码量是最大的,此时可以进行模块的划分和接口隔离。
- 3、使用中介者和代理
在UI层和Presenter之间设置中介者Mediator,将例如数据校验、组装在内的轻量级逻辑操作放在Mediator中;在Presenter和Model之间使用代理Proxy;通过上述两者分担一部分Presenter的逻辑操作,但整体框架的控制权还是在Presenter手中。
MVVM
MVVM可以算是MVP的升级版,其中的VM是ViewModel的缩写,ViewModel可以理解成是View的数据模型和Presenter的合体,ViewModel和View之间的交互通过Data Binding完成,而Data Binding可以实现双向的交互,这就使得视图和控制层之间的耦合程度进一步降低,关注点分离更为彻底,同时减轻了Activity的压力。
MVC->MVP->MVVM演进过程
MVC -> MVP -> MVVM 这几个软件设计模式是一步步演化发展的,MVVM 是从 MVP 的进一步发展与规范,MVP 隔离了MVC中的 M 与 V 的直接联系后,靠 Presenter 来中转,所以使用 MVP 时 P 是直接调用 View 的接口来实现对视图的操作的,这个 View 接口的东西一般来说是 showData、showLoading等等。M 与 V已经隔离了,方便测试了,但代码还不够优雅简洁,所以 MVVM 就弥补了这些缺陷。在 MVVM 中就出现的 Data Binding 这个概念,意思就是 View 接口的 showData 这些实现方法可以不写了,通过 Binding 来实现。
三种模式的相同点
M层和V层的实现是一样的。
三种模式的不同点
三者的差异在于如何粘合View和Model,实现用户的交互操作以及变更通知。
- Controller:接收View的命令,对Model进行操作,一个Controller可以对应多个View。
- Presenter:Presenter与Controller一样,接收View的命令,对Model进行操作;与Controller不同的是Presenter会反作用于View,Model的变更通知首先被Presenter获得,然后Presenter再去更新View。通常一个Presenter只对应于一个View。据Presenter和View对逻辑代码分担的程度不同,这种模式又有两种情况:普通的MVP模式和Passive View模式。
- ViewModel:注意这里的“Model”指的是View的Model,跟MVVM中的一个Model不是一回事。所谓View的Model就是包含View的一些数据属性和操作的这么一个东东,这种模式的关键技术就是数据绑定(data binding),View的变化会直接影响ViewModel,ViewModel的变化或者内容也会直接体现在View上。这种模式实际上是框架替应用开发者做了一些工作,开发者只需要较少的代码就能实现比较复杂的交互。
补充:基于AOP的架构设计
AOP(Aspect-Oriented Programming, 面向切面编程),诞生于上个世纪90年代,是对OOP(Object-Oriented Programming, 面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种从上道下的对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,即定义从左到右的关系时,OOP则显得无能为力。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(Cross-Cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
在Android App中的横切关注点有Http, SharedPreferences, Log, Json, Xml, File, Device, System, 格式转换等。Android App的需求差别很大,不同的需求横切关注点必然是不一样的。一般的App工程中应该有一个Util Package来存放相关的切面操作,在项目多了之后可以将其中使用较多的Util封装为一个Jar包/aar文件/远程依赖的方式供工程调用。
在使用MVP和AOP对App进行纵向和横向的切割之后,能够使得App整体的结构更清晰合理,避免局部的代码臃肿,方便开发、测试以及后续的维护。这样纵,横两次对于App代码的分割已经能使得程序不会过多堆积在一个Java文件里,但靠一次开发过程就写出高质量的代码是很困难的,趁着项目的间歇期,对代码进行重构很有必要。
最后的建议
如果“从零开始”,用什么设计架构的问题属于想得太多做得太少的问题。 从零开始意味着一个项目的主要技术难点是基本功能实现。当每一个功能都需要考虑如何做到的时候,我觉得一般人都没办法考虑如何做好。 因为,所有的优化都是站在最上层进行统筹规划。在这之前,你必须对下层的每一个模块都非常熟悉,进而提炼可复用的代码、规划逻辑流程。
MVC的情况下怎么把Activity的C和V抽离?
MVP 架构中 Presenter 定义为接口有什么好处;
MVP如何管理Presenter的生命周期,何时取消网络请求?
aop思想
Fragment如果在Adapter中使用应该如何解耦?
项目框架里有没有Base类,BaseActivity和BaseFragment这种封装导致的问题,以及解决方法?
设计一个音乐播放界面,你会如何实现,用到那些类,如何设计,如何定义接口,如何与后台交互,如何缓存与下载,如何优化(15分钟时间)
从0设计一款App整体架构,如何去做?
说一款你认为当前比较火的应用并设计(比如:直播APP,P2P金融,小视频等)
实现一个库,完成日志的实时上报和延迟上报两种功能,该从哪些方面考虑?
你最优秀的工程设计项目,是怎么设计和实现的;扩展,如何做成一个平台级产品?
六、其它高频面试题
1、如何保证一个后台服务不被杀死?(相同问题:如何保证service在后台不被kill?)比较省电的方式是什么?
保活方案
1、AIDL方式单进程、双进程方式保活Service。(基于onStartCommand() return START_STICKY)
START_STICKY 在运行onStartCommand后service进程被kill后,那将保留在开始状态,但是不保留那些传入的intent。不久后service就会再次尝试重新创建,因为保留在开始状态,在创建 service后将保证调用onstartCommand。如果没有传递任何开始命令给service,那将获取到null的intent。
除了华为此方案无效以及未更改底层的厂商不起作用外(START_STICKY字段就可以保持Service不被杀)。此方案可以与其他方案混合使用
2、降低oom_adj的值(提升service进程优先级):
Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:
- 1.前台进程 (Foreground process)
- 2.可见进程 (Visible process)
- 3.服务进程 (Service process)
- 4.后台进程 (Background process)
- 5.空进程 (Empty process)
当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些。
-
常驻通知栏(可通过启动另外一个服务关闭Notification,不对oom_adj值有影响)。
-
使用”1像素“的Activity覆盖在getWindow()的view上。
此方案无效果
- 循环播放无声音频(黑科技,7.0下杀不掉)。
成功对华为手机保活。小米8下也成功突破20分钟
- 3、监听锁屏广播:使Activity始终保持前台。
- 4、使用自定义锁屏界面:覆盖了系统锁屏界面。
- 5、通过android:process属性来为Service创建一个进程。
- 6、跳转到系统白名单界面让用户自己添加app进入白名单。
复活方案
1、onDestroy方法里重启service
service + broadcast 方式,就是当service走onDestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service。
2、JobScheduler:原理类似定时器,5.0,5.1,6.0作用很大,7.0时候有一定影响(可以在电源管理中给APP授权)。
只对5.0,5.1、6.0起作用。
3、推送互相唤醒复活:极光、友盟、以及各大厂商的推送。
4、同派系APP广播互相唤醒:比如今日头条系、阿里系。
此外还可以监听系统广播判断Service状态,通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活。
结论:高版本情况下可以使用弹出通知栏、双进程、无声音乐提高后台服务的保活概率。
2、Android动画框架实现原理。
Animation 框架定义了透明度,旋转,缩放和位移几种常见的动画,而且控制的是整个View。实现原理:
每次绘制视图时,View 所在的 ViewGroup 中的 drawChild 函数获取该View 的 Animation 的 Transformation 值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵运算完成动画帧,如果动画没有完成,继续调用 invalidate() 函数,启动下次绘制来驱动动画,动画过程中的帧之间间隙时间是绘制函数所消耗的时间,可能会导致动画消耗比较多的CPU资源,最重要的是,动画改变的只是显示,并不能响应事件。
3、Activity-Window-View三者的差别?
Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图) LayoutInflater像剪刀,Xml配置像窗花图纸。
在Activity中调用attach,创建了一个Window, 创建的window是其子类PhoneWindow,在attach中创建PhoneWindow。 在Activity中调用setContentView(R.layout.xxx), 其中实际上是调用的getWindow().setContentView(), 内部调用了PhoneWindow中的setContentView方法。
创建ParentView:
作为ViewGroup的子类,实际是创建的DecorView(作为FramLayout的子类), 将指定的R.layout.xxx进行填充, 通过布局填充器进行填充【其中的parent指的就是DecorView】, 调用ViewGroup的removeAllView(),先将所有的view移除掉,添加新的view:addView()。
4、低版本SDK如何实现高版本api?
- 1、在使用了高版本API的方法前面加一个 @TargetApi(API号)。
- 2、在代码上用版本判断来控制不同版本使用不同的代码。
5、说说你对Context的理解?
6、Android的生命周期和启动模式
由A启动B Activity,A为栈内复用模式,B为标准模式,然后再次启动A或者杀死B,说说A,B的生命周期变化,为什么?
Activity的启动模式有哪些?栈里是A-B-C,先想直接到A,BC都清理掉,有几种方法可以做到?这几种方法产生的结果是有几个A的实例?
7、ListView和RecyclerView系列
RecyclerView和ListView有什么区别?局部刷新?前者使用时多重type场景下怎么避免滑动卡顿。懒加载怎么实现,怎么优化滑动体验。
ListView、RecyclerView区别?
一、使用方面:
ListView的基础使用:
- 继承重写 BaseAdapter 类
- 自定义 ViewHolder 和 convertView 一起完成复用优化工作
RecyclerView 基础使用关键点同样有两点:
- 继承重写 RecyclerView.Adapter 和 RecyclerView.ViewHolder
- 设置布局管理器,控制布局效果
RecyclerView 相比 ListView 在基础使用上的区别主要有如下几点:
- ViewHolder 的编写规范化了
- RecyclerView 复用 Item 的工作 Google 全帮你搞定,不再需要像 ListView 那样自己调用 setTag
- RecyclerView 需要多出一步 LayoutManager 的设置工作
二、布局方面:
RecyclerView 支持 线性布局、网格布局、瀑布流布局 三种,而且同时还能够控制横向还是纵向滚动。
三、API提供方面:
ListView 提供了 setEmptyView ,addFooterView 、 addHeaderView.
RecyclerView 供了 notifyItemChanged 用于更新单个 Item View 的刷新,我们可以省去自己写局部更新的工作。
四、动画效果:
RecyclerView 在做局部刷新的时候有一个渐变的动画效果。继承 RecyclerView.ItemAnimator 类,并实现相应的方法,再调用 RecyclerView的 setItemAnimator(RecyclerView.ItemAnimator animator) 方法设置完即可实现自定义的动画效果。
五、监听 Item 的事件:
ListView 提供了单击、长按、选中某个 Item 的监听设置。
RecyclerView与ListView缓存机制的不同
想改变listview的高度,怎么做?
listview跟recyclerview上拉加载的时候分别应该如何处理?
如何自己实现RecyclerView的侧滑删除?
RecyclerView的ItemTouchHelper的实现原理
8、如何实现一个推送,消息推送原理?推送到达率的问题?
一:客户端不断的查询服务器,检索新内容,也就是所谓的pull 或者轮询方式。
二:客户端和服务器之间维持一个TCP/IP长连接,服务器向客户端push。
9、动态权限系列。
动态权限适配方案,权限组的概念
Runtime permission,如何把一个预置的app默认给它权限?不要授权。
10、自定义View系列。
Canvas的底层机制,绘制框架,硬件加速是什么原理,canvas lock的缓冲区是怎么回事?
双指缩放拖动大图
TabLayout中如何让当前标签永远位于屏幕中间
TabLayout如何设置指示器的宽度包裹内容?
自定义View如何考虑机型适配?
- 合理使用warp_content,match_parent。
- 尽可能地使用RelativeLayout。
- 针对不同的机型,使用不同的布局文件放在对应的目录下,android会自动匹配。
- 尽量使用点9图片。
- 使用与密度无关的像素单位dp,sp。
- 引入android的百分比布局。
- 切图的时候切大分辨率的图,应用到布局当中,在小分辨率的手机上也会有很好的显示效果。
11、对谷歌新推出的Room架构。
12、没有给权限如何定位,特定机型定位失败,如何解决?
13、Debug跟Release的APK的区别?
14、android文件存储,各版本存储位置的权限控制的演进,外部存储,内部存储
15、有什么提高编译速度的方法?
16、Scroller原理。
Scroller执行流程里面的三个核心方法
mScroller.startScroll();
mScroller.computeScrollOffset();
view.computeScroll();
1、在mScroller.startScroll()中为滑动做了一些初始化准备,比如:起始坐标,滑动的距离和方向以及持续时间(有默认值),动画开始时间等。
2、mScroller.computeScrollOffset()方法主要是根据当前已经消逝的时间来计算当前的坐标点。因为在mScroller.startScroll()中设置了动画时间,那么在computeScrollOffset()方法中依据已经消逝的时间就很容易得到当前时刻应该所处的位置并将其保存在变量mCurrX和mCurrY中。除此之外该方法还可判断动画是否已经结束。
17、Hybrid系列。
webwiew了解?怎么实现和javascript的通信?相互双方的通信。@JavascriptInterface在?版本有bug,除了这个还有其他调用android方法的方案吗?
Android中Java和JavaScript交互
webView.addJavaScriptInterface(new Object(){xxx}, "xxx");
1
答案:可以使用WebView控件执行JavaScript脚本,并且可以在JavaScript中执行Java代码。要想让WebView控件执行JavaScript,需要调用WebSettings.setJavaScriptEnabled方法,代码如下:
WebView webView = (WebView)findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
//设置WebView支持JavaScript
webSettings.setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());
JavaScript调用Java方法需要使用WebView.addJavascriptInterface方法设置JavaScript调用的Java方法,代码如下:
webView.addJavascriptInterface(new Object()
{
//JavaScript调用的方法
public String process(String value)
{
//处理代码
return result;
}
}, "demo"); //demo是Java对象映射到JavaScript中的对象名
可以使用下面的JavaScript代码调用process方法,代码如下:
<script language="javascript">
function search()
{
//调用searchWord方法
result.innerHTML = "<font color='red'>" + window.demo.process('data') + "</font>";
}
18、如果在当前线程内使用Handler postdelayed 两个消息,一个延迟5s,一个延迟10s,然后使当前线程sleep 5秒,以上消息的执行时间会如何变化?
答:照常执行
扩展:sleep时间<=5 对两个消息无影响,5< sleep时间 <=10 对第一个消息有影响,第一个消息会延迟到sleep后执行,sleep时间>10 对两个时间都有影响,都会延迟到sleep后执行。
19、Android中进程内存的分配,能不能自己分配定额内存?
20、下拉状态栏是不是影响activity的生命周期,如果在onStop的时候做了网络请求,onResume的时候怎么恢复
21、Android长连接,怎么处理心跳机制。
长连接:长连接是建立连接之后, 不主动断开. 双方互相发送数据, 发完了也不主动断开连接, 之后有需要发送的数据就继续通过这个连接发送.
心跳包:其实主要是为了防止NAT超时,客户端隔一段时间就主动发一个数据,探测连接是否断开。
服务器处理心跳包:假如客户端心跳间隔是固定的, 那么服务器在连接闲置超过这个时间还没收到心跳时, 可以认为对方掉线, 关闭连接. 如果客户端心跳会动态改变, 应当设置一个最大值, 超过这个最大值才认为对方掉线. 还有一种情况就是服务器通过TCP连接主动给客户端发消息出现写超时, 可以直接认为对方掉线.
22、CrashHandler实现原理?
获取app crash的信息保存在本地然后在下一次打开app的时候发送到服务器。
23、SurfaceView和View的最本质的区别?
SurfaceView是在一个新起的单独线程中可以重新绘制画面,而view必须在UI的主线程中更新画面。
在UI的主线程中更新画面可能会引发问题,比如你更新的时间过长,那么你的主UI线程就会被你正在画的函数阻塞。那么将无法响应按键、触屏等消息。当使用SurfaceView由于是在新的线程中更新画面所以不会阻塞你的UI主线程。但这也带来了另外一个问题,就是事件同步。比如你触屏了一下,你需要在SurfaceView中的thread处理,一般就需要有一个event queue的设计来保存touchevent,这会稍稍复杂一点,因为涉及到线程安全。
24、Android程序运行时权限与文件系统权限
1、Linux 文件系统权限。不同的用户对文件有不同的读写执行权限。在android系统中,system和应用程序是分开的,system里的数据是不可更改的。
2、Android中有3种权限,进程权限UserID,签名,应用申明权限。每次安装时,系统根据包名为应用分配唯一的userID,不同的userID运行在不同的进程里,进程间的内存是独立的,不可以相互访问,除非通过特定的Binder机制。
Android提供了如下的一种机制,可以使两个apk打破前面讲的这种壁垒。
在AndroidManifest.xml中利用sharedUserId属性给不同的package分配相同的userID,通过这样做,两个package可以被当做同一个程序,系统会分配给两个程序相同的UserID。当然,基于安全考虑,两个package需要有相同的签名,否则没有验证也就没有意义了。
25、曲面屏的适配。
26、TextView调用setText方法的内部执行流程。
27、怎么控制另外一个进程的View显示(RemoteView)?
28、如何实现右滑finish activity?
29、如何在整个系统层面实现界面的圆角效果。(即所有的APP打开界面都会是圆角)
30、非UI线程可以更新UI吗?
可以,当访问UI时,ViewRootImpl会调用checkThread方法去检查当前访问UI的线程是哪个,如果不是UI线程则会抛出异常。执行onCreate方法的那个时候ViewRootImpl还没创建,无法去检查当前线程.ViewRootImpl的创建在onResume方法回调之后。
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
非UI线程是可以刷新UI的,前提是它要拥有自己的ViewRoot,即更新UI的线程和创建ViewRoot的线程是同一个,或者在执行checkThread()前更新UI。
31、如何解决git冲突?
32、单元测试有没有做过,说说熟悉的单元测试框架?
首先,Android测试主要分为三个方面:
- 单元测试(Junit4、Mockito、PowerMockito、Robolectric)
- UI测试(Espresso、UI Automator)
- 压力测试(Monkey)
WanAndroid项目和XXX项目中使用用到了单元测试和部分自动化UI测试,其中单元测试使用的是Junit4+Mockito+PowerMockito+Robolectric。下面我分别简单介绍下这些测试框架:
1、Junit4:
使用@Test注解指定一个方法为一个测试方法,除此之外,还有如下常用注解@BeforeClass->@Before->@Test->@After->@AfterClass以及@Ignore。
Junit4的主要测试方法就是断言,即assertEquals()方法。然后,你可以通过实现TestRule接口的方式重写apply()方法去自定义Junit Rule,这样就可以在执行测试方法的前后做一些通用的初始化或释放资源等工作,接着在想要的测试类中使用@Rule注解声明使用JsonChaoRule即可。(注意被@Rule注解的变量必须是final的。最后,我们直接运行对应的单元测试方法或类,如果你想要一键运行项目中所有的单元测试类,直接点击运行Gradle Projects下的app/Tasks/verification/test即可,它会在module下的build/reports/tests/下生成对应的index.html报告。
Junit4它的优点是速度快,支持代码覆盖率如jacoco等代码质量的检测工具。缺点就是无法单独对Android UI,一些类进行操作,与原生Java有一些差异。
2、Mockito:
可以使用mock()方法模拟各种各样的对象,以替代真正的对象做出希望的响应。除此之外,它还有很多验证方法调用的方式如Mockit.when(调用方法).thenReturn(验证的返回值)、verfiy(模拟对象).验证方法等等。
这里有一点要补充下:简单的测试会使整体的代码更简洁,更可读、更可维护。如果你不能把测试写的很简单,那么请在测试时重构你的代码。
最后,对于Mockito来说,它的优点是有各种各样的方式去验证"模仿对象"的互动或验证发生的某些行为。而它的缺点就是不支持mock匿名类、final类、static方法private方法。
3、PowerMockito:
因此,为了解决Mockito的缺陷,PoweMockito出现了,它扩展了Mockito,支持mock匿名类、final类、static方法、private方法。只要使用它提供的api如PowerMockito.mockStatic()去mock含静态方法或字段的类,PowerMockito.suppress(PowerMockito.method(类.class, 方法名)即可。
4、Robolectric
前面3种我们说的都是Java相关的单元测试方法,如果想在Java单元测试里面进行Android单元测试,还得使用Robolectric,它提供了一套能运行在JVM的Android代码。它提供了一系列类似ShadowToast.getLatestToast()、ShadowApplication.getInstance()这种方式来获取Android平台对应的对象。可以看到它的优点就是支持大部分Android平台依赖类的底层引用与模拟。缺点就是在异步测试的情况下有些问题,这是可以结合Mockito来将异步转为同步即可解决。
最后,自动化UI测试项目中我使用的是Expresso,它提供了一系列类似onView().check().perform()的方式来实现点击、滑动、检测页面显示等自动化的UI测试效果,这里在我的WanAndroid项目下的BasePageTest基类里面封装了一系列通用的方法,有兴趣可以去看看。
33、Jenkins持续集成。
34、工作中有没有用过或者写过什么工具?脚本,插件等等;比如:多人协同开发可能对一些相同资源都各自放了一份,有没有方法自动检测这种重复之类的。
35、如何绕过9.0限制?
如何限制?
- 1、阻止java反射和JNI。
- 2、当获取方法或Field时进行检测。
- 3、怎么检测?
区分出是系统调用还是开发者调用:
根据堆栈,回溯Class,查看ClassLoader是否是BootStrapClassLoader。
区分后,再区分是否是hidden api:
Method,Field都有access_flag,有一些备用字段,hidden信息存储其中。
如何绕过?
1、不用反射:
利用一个fakelib,例如写一个android.app.ActivityThread#currentActivityThread空实现,直接调用;
2、伪装系统调用:
jni修改一个class的classloder为BootStrapClassLoader,麻烦。
利用系统方法去反射:
利用原反射,即:getDeclaredMethod这个方法是系统的方法,通过getDeclaredmethod反射去执行hidden api。
3、修改Method,Field中存储hidden信息的字段:
利用jni去修改。
36、对文件描述符怎么理解?
37、如何实现进程安全写文件?
其它面试题
其他扩展面试题
一、Kotlin (⭐⭐)
1、Kotlin 特性,和 Java 相比有什么不同的地方?
- 能直接与Java相互调用,能与Java工程共存
- 大大减少样板代码
- 可以将Kotlin代码编译为无需虚拟机就可运行的原生二进制文件
- 支持协程
- 支持高阶函数
- 语言层面解决空指针问题
- 对字符串格式化的处理($变量名)
- 更像Python的语法
- 对λ表达式支持更好
2、Kotlin为什么能和Java混编?
3、什么是协程?
二、大前端 (⭐⭐)
1、Hybrid通信原理是什么,有做研究吗?
2、JS的交互理解吗?平时工作用的多吗,项目中是怎么与Web交互的?
Android通过WebView调用JS代码:
1、通过WebView的loadUrl():
-
设置与Js交互的权限:
webSettings.setJavaScriptEnabled(true)
-
设置允许JS弹窗:
webSettings.setJavaScriptCanOpenWindowsAutomatically(true)
-
载入JS代码:
mWebView.loadUrl("file:///android_asset/javascript.html")
-
webview只是载体,内容的渲染需要使用webviewChromClient类去实现,通过设置WebChromeClient对象处理JavaScript的对话框。
特别注意:
JS代码调用一定要在 onPageFinished() 回调之后才能调用,否则不会调用。
2、通过WebView的evaluateJavascript():
- 该方法比第一种方法效率更高、使用更简洁,因为该方法的执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。
- Android 4.4 后才可使用。
只需要将第一种方法的loadUrl()换成evaluateJavascript()即可,通过onReceiveValue()回调接收返回值。
建议:两种方法混合使用,即Android 4.4以下使用方法1,Android 4.4以上方法2。
JS通过WebView调用 Android 代码:
1、通过 WebView的addJavascriptInterface()进行对象映射:
-定义一个与JS对象映射关系的Android类:AndroidtoJs:
- 定义JS需要调用的方法,被JS调用的方法必须加入@JavascriptInterface注解。
- 通过addJavascriptInterface()将Java对象映射到JS对象。
优点:使用简单,仅将Android对象和JS对象映射即可。
缺点:addJavascriptInterface 接口引起远程代码执行漏洞,漏洞产生原因是:
当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法,包括系统类(java.lang.Runtime 类),从而进行任意代码执行。
2、通过 WebViewClient 的方法shouldOverrideUrlLoading ()回调拦截 url:
-
Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url。
-
解析该 url 的协议。
-
如果检测到是预先约定好的协议,就调用相应方法。
根据协议的参数,判断是否是所需要的url。 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数)。
优点:不存在方式1的漏洞;
缺点:JS获取Android方法的返回值复杂,如果JS想要得到Android方法的返回值,只能通过 WebView 的 loadUrl ()去执行 JS 方法把返回值传递回去。
3、通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息:
原理:
Android通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调分别拦截JS对话框 (警告框、确认框、输入框),得到他们的消息内容,然后解析即可。
常用的拦截是:拦截 JS的输入框(即prompt()方法),因为只有prompt()可以返回任意类型的值,操作最全面方便、更加灵活;而alert()对话框没有返回值;confirm()对话框只能返回两种状态(确定 / 取消)两个值。
Android:你要的WebView与 JS 交互方式 都在这里了
3、react native有多少了解?讲一下原理。
4、weex了解吗?如何自己实现类似技术?
5、flutter了解吗?内部是如何实现跨平台的?如何实现多Native页面接入?如何实现对现有工程的flutter迁移?
6、Dart语言有研究过吗?
7、快应用了解吗?跟其她方式相比有什么优缺点?
8、说说你用过的混合开发技术有哪些?各有什么优缺点?
三、脚本语言 (⭐⭐)
1、脚本语言会吗?
2、Python会吗?
3、Gradle了解多少?groovy语法会吗?
非技术面试题
一、高频题集 (⭐⭐⭐)
1、你觉得安卓开发最关键的技术在哪里?
技术是没有止境的,所以肯定会不断有演进和难点。
一. 底层和框架如何更好地设计及优化以适应业务的高速增长。说起来很简单,低耦合高扩展,做起来是需要长期经验积累。
二. 我抛几个细节难点:
- 插件化如何使插件的 Manifest 生效
- H5 容器如何更好地优化和兼容
- App 端优化,这是个没止境的话题,网络、图片、动画、内存、电量等等随着优化的加深,你会发现不能局限在客户端,服务端也需要深入。
- SPDY 的优点并入 HTTP 2.0 你们有在测试或用吗?
- Fresco 出来前你是不是觉得图片缓存已经到头了?
- Android App 为什么整体流畅性总是被诟病?……
三. 如果你觉得没有难点或者难点在兼容、UI 之类问题上,那么可能两个原因:
- 公司业务发展过慢,对技术的需求不够迫切
- 个人长时间在业务开发上,这个对于走技术路线的人来说挺麻烦的,不主动去接触学习的话,n 年以后也还是这个样子为了更好的个人成长,这两点都是需要注意和解决的问题。
2、你还要什么了解和要问的吗?
你在公司的一天是如何度过的?
能否给我简单介绍下贵公司业务与战略的未来发展?
贵公司最让你自豪的企业文化是什么?(适合大公司)
团队、公司现在面临的最大挑战是什么?
对于未来加入这个团队,你对我的期望是什么?
您觉得我哪方面知识需要深入学习或者我的不足在那些方面,今后我该注意什么*?
你还可以问下项目团队多少人,主要以什么方向为主,一年内的目标怎样,团队气氛怎样,等内容着手。
3、研究比较深入的领域有哪些?
4、自己最擅长的技术点,最感兴趣的技术领域和技术?
5、项目中用了哪些开源库,如何避免因为引入开源库而导致的安全性和稳定性问题?
6、说下你都看过那些技术书籍,你是如何自学的。你觉得自己的优势与弱点是什么。
7、说下项目中遇到的棘手问题,包括技术,交际和沟通。
8、说下你近几年的规划?
9、对加班怎么看(不要太浮夸,现实一点哦)?
10、介绍你做过的哪些项目。
11、你并非毕业于名牌院校?
12、为什么要离职?
13、当你的开发任务很紧张,你怎么去做代码优化的?
二、次高频题集 (⭐⭐)
1、对业内信息的关注渠道有哪些?
2、最近都读哪些书?
3、给你一个项目,你怎么看待他的市场和技术的关系?
4、你以往的项目中,以你现在的眼光去评价项目的利弊?
5、对于非立项(KPI)项目,怎么推进?
6、都使用过哪些自定义控件?
7、除了简历上的工作经历,您还会去关注哪些领域?
8、评价下自己,评价下自己的技术水平,个人代码量如何?
9、你朋友对你的评价?
10、自己的优点和缺点是什么?并举例说明?
11、你觉得你个性上最大的优点是什么?
12、说说你最大的缺点?
13、最能概括你自己的三个词是什么?
14、说说你的家庭?
15、除了本公司外,还应聘了哪些公司?(类似问题:当前的offer状况)
16、通过哪些渠道了解的招聘信息?
17、你的业余爱好是什么?
18、你做过的哪件事最令自己感到骄傲?
19、谈谈你对跳槽的看法?
20、怎样看待学历和能力?
21、您跟您的主管或直接上司有没有针对以上离职原因的这些问题沟通过?如果没有请说明原因。如果有请说一下过程和结果?
22、您觉得你关注的这些领域跟您目前从事的职业有哪些利弊关系?如果有请说明利弊关系?
23、您在选择工作中更看重的是什么?(可能是成长空间、培训机会、发挥平台、薪酬等答案)
24、您可不可以说说您在薪酬方面的心里预期?
25、有人说挣未来比挣钱更为重要,您怎么理解?
26、假设,某一天,在工作办公室走廊,您和一位同事正在抱怨上级陈某平时做事缺乏公平性,恰巧被陈某听到,您会怎么办?
27、怎么样处理工作和生活的关系?怎么处理在工作中遇到困难?请举例说明
28、在您的现实生活中,您最不喜欢和什么样的人共事?为什么?举例说明。
29、在您认识的人中,有没有人不喜欢您?为什么不喜欢您?请举例说明。
30、当老板/上司/同事/客户误会你,你会怎么办?
31、当你发现其他部门的工作疏漏已经影响到您的工作绩效时,您怎么办?
32、您希望在什么样的领导下工作?
33、我们工作与生活历程并不是一帆风顺的,谈谈您的工作或生活中出现的挫折或低潮期,您如何克服?
34、假如您的上司是一个非常严厉、领导手腕强硬,时常给您巨大压力的人,您觉得这种领导方式对您有何利、弊?
35、您的领导给您布置了一项您以前从未触及过的任务,您打算如何去完成它?(如果有类似的经历说说完成的经历。)
36、谈谈您以往职业生涯中最有压力的一、两件事,并说说是如何克服的。
37、谈谈您以往职业生涯中令您有成就感的一、两件事,并说说它给您的启示。
38、请您举一个例子,说明在完成一项重要任务时,您是怎样和他人进行有效合作的。
39、当你要牺牲自己的某些方面与他人共事时,你会怎么办?
40、有时团队成员不能有效共事,当遇到这种问题时你是怎么处理的?你又是如何改善这类情况的?
41、我们有时不得不与自己不喜欢的人在一个团队工作,如果遇到这样的情况你会怎么办?
42、您对委任的任务完成不了时如何处理?
43、说说您对下属布置的任务在时间方面是如何要求的?
44、说说您在完成上司布置的任务时,在时间方面是如何要求自己的?
45、您以往在领导岗位中,一个月内分别有哪些主要的工作任务?
46、当您发现您的部属目前士气较低沉,您一般从哪些方面去调动?
47、说说您在以往领导岗位中出现管理失控的事例及事后的原因分析。您的部属在一个专业的问题上跟您发生争议,您如何对待这种事件?
48、你对某某某互联网发生事情的看法?(直播答题等等)
49、怎么看待前端和后端?
结语
其实,在面试中,很多题目并没有真正的答案,你能回答到什么样的深度,还是得靠自己真正的去使用和研究。知识的深度和广度都很重要,所以都需要花相应的时间去学习。这样,就算被问到自己不熟悉的领域也可以同面试官唠嗑两句,然后可以在适当的时机引向自己有深入研究的领域,让面试官觉得你是这个领域的专家。
公钟号同名,欢迎关注,关注后回复 Framework,我将分享给你一份我这两年持续总结、细化、沉淀出来的 Framework 体系化精品面试题,里面很多的核心题答案在面试的压力下,经过了反复的校正与升华,含金量极高~