不知道你们多长时间没有参加过面试了,最近这段时间的面试,真的是一个比一个严格!
我昨天参加了一线大厂的技术面,当场给我吓die了,没想到这么难!
如果你不信,你也来die die:
你说这面试官狠不狠!太狠了!
对于我们程序员而言,面试就是将自己所学的技术与面试官表达出来。以前上学准备找工作的时候,发现最先找到工作的不是技术最好的而是善于沟通活跃度高的人,而我听的最多的抱怨是 “为什么我明明知道这个东西,面试的时候却不知从何说起”。
本文会从我的自身经历出发谈一谈面试前如何做好充分准备,怎么流畅的表达自己的技术,以及进入项目时如何从一开始的手足无措、盲目下手到后来的淡定从容。
下面我将从以下几个方面来分享一下面试前的准备。
1、给自己定位
首先要给自己定位,自己现在是一个什么阶段,是初级岗,中高级,高级,还是专家级。一般情况下刚参加工作一年左右时初级,两年左右努力提升自己的话是中级,三到五年高级甚至专家级。前提是不停的钻研。
2、知识的梳理
因为把自己定位为高级开发工程师,俗话说:面试修航母,工作螺丝钉。面试考虑各个方面,对于安卓来说,主要有以下几个方面来准备:Java基础,Android基础,Java并发,Java虚拟机,Android源码(包括framework和开源的项目),数据结构和算法。当然基础知识梳理完毕之后,还要去阅读一些面试经,看看最新各个厂商问的问题。其实以上这些东西准备完,基础好的话,一个月左右,基础不好的话至少要3个月吧。
俗话说不打无准备之仗,只有准备好了才能在面试过程中取得好的结果。
3、简历的准备
简历是一个敲门砖,HR每天会有很多简历收到。简历如何让人眼前一亮,并且能一眼抓住关键信息:个人基本信息(姓名,电话,邮箱,毕业院校等),工作经历,技能点,一些亮点(比如六级证书,获奖等)。简历要简洁,层次分明。写完简历要在招聘网站上更新一下自己的简历。
4、面试机会的各个渠道
这次面试一共通过了3种渠道,一是朋友内推,二是猎头帮忙,三是通过boss或智联投递。
5、技能储备
Java 基础知识方面需要掌握的有:面向对象的理解、基本类型与引用类型、构造方法、常用类(内部类、匿名类、抽象类)、三大特性(封装、继承、多态)、重写与重载、接口与接口的实现等等。这些问题面试官会在掌握的层面上去问你,主要是考察你的基础知识是否扎实,毕竟安卓是用 Java 编写的。
Android 方面需要掌握的有:四大组件的简单使用、activity 的生命周期、fragment 的绑定、activity 和 fragment 之间的传值、 recyclerview 实现列表九宫格瀑布流式布局的实现、viewHolder 的复用问题、数据存储的几种方式的特点、常用框架 Glide、Retrofit、eventBus、butterknife 的使用。
Java 基础知识需要掌握的有:对于两到三年的安卓程序员来说,Java 不仅仅是停留在一些基础知识的使用上了,而是在用的同时要有自己的理解。比如说封装,面试官不会问你什么是封装,而是会问你封装过公司的哪些代码/功能,你是如何封装的。这个时候考察的就是你是会写代码还是只会模仿代码,如果没有自己在项目中封装过代码的话可以去阅读下网上一些优秀的框架的源码学习一下别人是怎么封装的。
当然不仅仅是封装还有很多知识点都要按照这个要求去掌握,比如:Java 泛型、反射、集合框架、接口与抽象类、设计模式等等。掌握这些除了看视频学习还可以阅读一些优秀的源码。不懂的地方再查一查博客,理解透了后一定要在自己项目上运用,这样学习才能印象深刻面试官问到也能有列可举。
Android 方面需要掌握的有:APP 启动原理,想要详细了解的可以看我的另一篇 chat (APP 启动原理及启动优化详解 )、图片压缩与性能优化、自定义 view 、事件分发流程、屏幕适配、组件化和插件化、Glide 的缓存与复用、OkHttp 的责任链与连接池、序列化与反序列化、分析一个你最熟练框架的源码等等。可以看到,对于两到三年的程序员来说不仅掌握的知识点更多,而且还需要对原理有一定的了解。
大厂面试经历
本人刚出来找工作的时候面试了很多家,那时候是移动开发的爆发时期,其中有腾讯等中大型公司也有一些刚创业的小公司,当然大厂最终面试失败了。记得去腾讯面试的时候,去面试的人特别的多,需要在大厅等待,一批一批的去面试,先是群面,了解基本情况过后,就是单独面谈等待面试,总共三轮面试,面试官会通知面试结果出来的时间。
面试官提到的问题有:为什么从上一家离职、如何看待我们公司、自己以后的职业规划是什么、技术方面问了启动优化怎么优化?跨进程传递大内存数据如何做?主线程等待所有线程执行完毕,再执行某个特定任务怎么实现?原理和源码看过没?你写的 rxpay 和 rxlogin 具体怎么实现的?
大小厂考察点的区别
流程比较多,一般会有三面以上,首先会有一套面试题等着你,当然不是所有公司都有面试题,只不过大厂有面试题的概率会高些,其次技术主管面试,然后技术经理面试,人事会跟你介绍公司的发展业务公司福利工作时间等等。大厂的开发一般是分模块开发,每个人单独负责某一个模块,所以要求你要有自己的优势点,比如你会写自定义组件,视频模块,对 NDK 深有研究等等。
流程少,有可能一面过了就叫你来上班了。小公司面试一定要问清楚工资什么时候发,月初发说明公司资金充足,月中旬发是正常的,如果月末才发的话公司资金紧张很可能出现财务危机。还有五险一金有没有,有的话试用期有没有,目前还有一部分公司没有五险一金的,而且大部分公司试用期不交五险一金。关于五险一金的重要性大家可以去百度搜一搜,限购令出来后这个更加重要了。
关于技术方面的区别就是,小公司一般都是一个人开发,要求你知道整个项目的开发流程,但是对于技术深度要求不高(仅仅对于初级程序员来说,高级架构师就另说了)。面试之前多准备些项目去演示,有些人说懂技术的都不看作品的,但是对于小公司就不同了,有可能面试你的是产品经理、后台人员等等,对安卓了解的也不是很深,这时候有几个好的作品演示一定会给你的面试加不少分。
以下五个问题本人面试的时候都被问到过,也作为面试官考察过别人,算是比较有代表性的题目。
1)通过Intent方式传递参数跳转
2)通过广播方式
3)通过接口回调方式
4)借助类的静态变量或全局变量
5)借助SharedPreference或是外部存储,如数据库或本地文件
请介绍下 Android 的数据存储方式
使用 SharedPreferences 存储数据;文件存储数据;SQLite 数据库存储数据;使用 ContentProvider 存储数据;网络存储数据。
Preference,File, DataBase 这三种方式分别对应的目录是 /data/data/Package Name/Shared_Pref, /data/data/Package Name/files, /data/data/Package Name/database 。
首先说明 SharedPreferences 存储方式,它是 Android 提供的用来存储一些简单配置信息的一种机制,例如:登录用户的用户名与密码。其采用了 Map 数据结构来存储数据,以键值的方式存储,可以简单的读取与写入,具体实例如下:
void ReadSharedPreferences(){String strName,strPassword;SharedPreferences user = getSharedPreferences(“user_info”,0);strName = user.getString(“NAME”,””);strPassword = user getString(“PASSWORD”,””);}void WriteSharedPreferences(String strName,String strPassword){SharedPreferences user = getSharedPreferences(“user_info”,0);uer.edit();user.putString(“NAME”, strName);user.putString(“PASSWORD” ,strPassword);user.commit();}
数据读取与写入的方法都非常简单,只是在写入的时候有些区别:先调用 edit() 使其处于编辑状态,然后才能修改数据,最后使用 commit() 提交修改的数据。实际上 SharedPreferences 是采用了 XML 格式将数据存储到设备中,在 DDMS 中的 File Explorer 中的 /data/data//shares_prefs 下。使用 SharedPreferences 是有些限制的:只能在同一个包内使用,不能在不同的包之间使用。
文件存储方式是一种较常用的方法,在 Android 中读取/写入文件的方法,与 Java 中实现 I/O 的程序是完全一样的,提供了 openFileInput() 和openFileOutput() 方法来读取设备上的文件。具体实例如下:
String fn = “moandroid.log”;FileInputStream fis = openFileInput(fn);FileOutputStream fos = openFileOutput(fn,Context.MODE_PRIVATE);
网络存储方式,需要与 Android 网络数据包打交道,关于 Android 网络数据包的详细说明,请阅读 Android SDK 引用了 Java SDK 的哪些package?。
activity的启动模式有哪些?是什么含义?
在 Android 里,有 4 种 activity 的启动模式,分别为:
“standard” (默认)
“singleTop”
“singleTask”
“singleInstance”
它们主要有如下不同:
“standard” 和 ”singleTop” 的 activity 的目标 task,和收到的 Intent 的发送者在同一个 task 内,除非 intent 包括参数 FLAG_ACTIVITY_NEW_TASK。
如果提供了 FLAG_ACTIVITY_NEW_TASK 参数,会启动到别的 task 里。
“singleTask” 和 ”singleInstance” 总是把 activity 作为一个 task 的根元素,他们不会被启动到一个其他 task 里。
“standard” 和 ”singleTop” 可以被实例化多次,并且存在于不同的 task 中,且一个 task 可以包括一个 activity 的多个实例。
“singleTask” 和 ”singleInstance” 则限制只生成一个实例,并且是 task 的根元素。 singleTop 要求如果创建 intent 的时候栈顶已经有要创建的 Activity的实例,则将 intent 发送给该实例,而不发送给新的实例。
“singleInstance” 独占一个 task,其它 activity 不能存在那个 task 里;如果它启动了一个新的 activity,不管新的 activity 的 launch mode 如何,新的activity 都将会到别的 task 里运行(如同加了 FLAG_ACTIVITY_NEW_TASK参数)。
而另外三种模式,则可以和其它 activity 共存。
“standard” 对于没一个启动 Intent 都会生成一个 activity 的新实例。
“singleTop” 的 activity 如果在 task 的栈顶的话,则不生成新的该 activity 的实例,直接使用栈顶的实例,否则,生成该 activity 的实例。
比如现在 task 栈元素为 A-B-C-D(D在栈顶),这时候给 D 发一个启动 intent,如果 D 是 “standard” 的,则生成 D 的一个新实例,栈变为 A-B-C-D-D。
如果 D 是 singleTop 的话,则不会生产 D 的新实例,栈状态仍为 A-B-C-D。
如果这时候给 B 发 Intent 的话,不管 B 的 launchmode 是 ”standard” 还是 “singleTop” ,都会生成 B 的新实例,栈状态变为 A-B-C-D-B。
“singleInstance” 是其所在栈的唯一 activity,它会每次都被重用。
“singleTask” 如果在栈顶,则接受 intent,否则,该 intent 会被丢弃,但是该 task 仍会回到前台。
当已经存在的 activity 实例处理新的 intent 时候,会调用 onNewIntent() 方法 如果收到 intent 生成一个 activity 实例,那么用户可以通过 back 键回到上一个状态;如果是已经存在的一个 activity 来处理这个 intent 的话,用户不能通过按 back 键返回到这之前的状态。
ContentProvider实现各个应用程序间数据共享,用来提供内容给别的应用操作。如联系人应用中就使用了ContentProvider,可以在自己应用中读取和修改联系人信息,不过需要获取相应的权限。它也只是一个中间件,真正的数据源是文件或SQLite等。 ContentResolver内容解析者,用于获取内容提供者提供的数据,通过ContentResolver.notifyChange(uri)发出消息 ContentObserver内容监听者,可以监听数据的改变状态,观察特定Uri引起的数据库变化,继而做一些相应的处理,类似于数据库中的触发器,当ContentObserver所观察的Uri发生变化时,便会触发它。
注册广播有几种方式,这些方式有何优缺点?请谈谈 Android 引入广播机制的用意。
首先写一个类要继承 BroadcastReceiver
第一种:在清单文件中声明,添加
第二种使用代码进行注册如
两种注册类型的区别是:
搭建项目的主流框架集
Dagger2+RxJava+Retrofit+MVP 是本人目前用的框架集,目前使用的很广泛, 四个相结合,组成项目的优美整体架构。 需要导入的包
> dependencies { // 网络请求 compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:adapter-rxJava:2.0.1' compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4' compile 'com.squareup.retrofit2:converter-scalars:2.0.0-beta4' compile 'com.squareup.okhttp3:okhttp:3.2.0' // 注解 compile 'com.google.dagger:dagger:2.0.2' apt 'com.google.dagger:dagger-compiler:2.0.2' provided 'org.glassfish:Javax.annotation:10.0-b28' compile 'com.jakewharton:butterknife:7.0.1' // Rx compile 'io.reactivex:rxandroid:1.1.0' compile 'io.reactivex:rxJava:1.1.5' }
下面是 MVP 的架构图
如上图所示
View 与 Model 并不直接交互,而是使用 Presenter 作为 View 与 Model 之间的桥梁。
其中 Presenter 中同时持有 view 层以及 Model 层的 Interface 的引用,View 层持有 Presenter 层 Interface 的引用。当 View 层某个界面需要展示某些数据的时候,首先会调用 Presenter 层的某个接口,然后 Presenter 层会调用 Model 层请求数据。
当 Model 层数据加载成功之后会调用 Presenter 层的回调方法通知 Presenter 层数据加载完毕。
最后 Presenter 层再调用 View 层的接口将加载后的数据展示给用户。这就是 MVP 模式的整个核心过程,如果你是面试初级安卓开发,面试官应该只会让你阐述下整个调用过程,只要你能流畅的说完整个过程应该差不多了。
Dagger2 的流程图
什么是 Dagger2
Dagger2 是一个依赖注入框架,butterknife 也是一个依赖注入框架。不过 butterknife,最多叫奶油刀,Dagger2 被叫做利器啊,他的主要作用,就是对象的管理,其目的是为了降低程序耦合。
Dagger2 的优点
和 ButterKnife 库定义了view,事件处理以及资源的引用一样,Dagger2 提供全局对象引用的简易访问方式。声明了单例的实例都可以使用 @inject 进行访问。比如下面的 MyTwitterApiClient 和SharedPreferences 的实例:
> public class MainActivity extends Activity { @Inject MyTwitterApiClient mTwitterApiClient; @Inject SharedPreferences sharedPreferences; public void onCreate(Bundle savedInstance) { // assign singleton instances to fields InjectorClass.inject(this); }
Dagger2 会通过依赖关系并且生成易懂易分析的代码。以前通过手写的大量模板代码中的对象引用将会由它给你创建并传递到相应对象中。因此你可以更多的关注模块中构建的内容而不是模块中的对象实例的创建顺序。
因为依赖关系已经为我们独立出来,所以我们可以轻松的抽取出不同的模块进行测试。依赖的注入和配置独立于组件之外。因为对象是在一个独立、不耦合的地方初始化,所以当注入抽象方法的时候,我们只需要修改对象的实现方法,而不用大改代码库。依赖可以注入到一个组件中:我们可以注入这些依赖的模拟实现,这样使得测试更加简单。
我们不仅可以轻松的管理全局实例对象,也可以使用 Dagger2 中的 scope 定义不同的作用域。(比如根据 user session、activity 的生命周期)
Retrofit 是 Square 开发的一个 Android 和 Java 的 REST 客户端库。这个库非常简单并且具有很多特性,相比其他的网络库,更容易让初学者快速掌握。
创建 Retrofit 实例时需要通过 Retrofit.Builder,并调用 baseUrl 方法设置 URL。
接口定义
以获取时间列表为例
> public interface TimeService {> @GET("getTimes?") Call< ResponseBody > getTimes (@Query("month") String month);}
注意,这里是 interface 不是 class,所以我们是无法直接调用该方法,我们需要用 Retrofit 创建一个 TimeService 的代理对象。
TimeService timeService= createRetrofit().create(TimeService .class);
接口调用
> Call mService= timeService.getTimes ("1"); mService.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { try { Log(response.body().string()); } catch (IOException e) { e.printStackTrace(); } } @Override public void onFailure(Call call, Throwable t) { t.printStackTrace(); } });
如何快速理解项目进行二次开发
当我们进入一个新公司工作,有可能接手的不是一个新项目而是维护别人开发的项目,面对庞大的项目不知从何下手。在这里我要告诉大家的是,拿到项目的时候不要盲目的进行开发而是阅读项目源码。阅读源码首先浏览项目结构,
通过这张结构图可以了解项目用的是 MVP 架构,有两个 lib 一个和 service 有关 一个和图片处理有关。然后再阅读 build.gradle 文件,里面有项目中用到的第三方库的引用地址,知道项目中用到了哪些技术,如果有不熟悉的第三方库就可以根据这个地址去查找资源熟悉调用方式项目中用到的模块,日后逐渐研究库的源码以及实现原理。等这些了解的差不多了,再看一看需求文档和设计图,对着需求走一遍流程,主要记录 activity 之间的跳转,可以画一张类之间跳转的结构图,这样整个跳转的逻辑会更清晰。
还有两个值得阅读的是:项目中的工具类和封装的组件。相信不少人遇到过,在网上找了很久的一个处理数据的方法,过了很多天发现项目的工具类中有直接就可以拿来用。封装的组件也和工具类似可以直接拿来用的,在后面的开发也提倡大家将项目组件化。
腾讯地图
阿里巴巴
美团
爱奇艺
对于基础知识考察的比较多,注重原理,要求面试者在学习技术的时候加深理解。不同的公司在业务侧重点方面有所不同,但总体需要掌握的技能有:高级 UI、性能优化、移动架构等方面。
感谢大家能耐着性子,看完我啰哩啰嗦的文章。
我愿与各位坚守在Android开发岗位的同胞们互相交流学习,共同进步!
在这里我也分享一份自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
如果你有需要的话,可以点赞+转发,关注我,然后私信我【学习】我发给你