因为我的缘由在一个公司连续呆了四年之久,然而此次面试也是人生中第一次面试,对于工做四年工做内容我分为三点:1.产品筹划、2.产品设计、3.安卓开发java
通常面试时间短则30分钟,多则1个小时,这么点时间要全面考察一我的难度很大,须要一些技巧,这里我不局限于回答题主的问题,而是分享一下我我的关于如何作好Android技术面试的一些经验:android
- 简历调查
简历到你手上的时候,你要作好充分的调查分析,不只仅是对公司负责,也是对本身与候选人时间的尊重,明显不match的简历,就不要抱着“来试试看”的想法了,候选人也许很不错,但若是跟你的岗位不match, 也不要浪费你们时间,你要想清楚如今须要的人是有潜力能够培养的,仍是亟需帮忙干活的。另外若是简历里附带了博客连接,GitHub地址,相关做品的,能够提早去看看,直接看人家多年积累的文章与代码,比这短短一小时的面试来得靠谱的多。面试
- 准备问题
了解清楚候选人背景后,要根据简历,有针对性的准备问题,能够是他做品或作过项目里的某个技术细节的实现方式,也能够是他声称精通的某些领域的相关问题。总之不要等到面试过程当中现想问题,特别是刚开始面试别人的同窗,每每经验不足稍带紧张致使大脑短路,其实也是很尴尬的,把要问的问题提早写下来,准备充分。算法
在面试以前看到不少朋友在博客,简书、csdn、掘金等发布本身的面试总结,其中有不少知识点都是模糊的,由于项目中不多用到,或者是用到了没有深刻去研究。其实在过去,我经常有这种心理,以为项目中不经常使用或者几乎不用普及的大可没必要去研究。彻底是为了开发而开发。长此以往我发现我所作的项目或多或少会出现一些bug,而这些bug有时候会困扰我几个小时,甚至大半天。后面当我去研究那些我认为项目中不多用,或者几乎用不到的知识点后,才发现本身所犯的错,并且你会很清楚的明白错误起因,以及解决办法。我之因此说这些就想说明要多去研究知识点,不要为了开发而开发。对一些不理解的api要学会看源码。学会读源码。好了,言归正算。下面我就汇总下咱们日常面试中常见的问题(我认为)及本身的答题。数据库
- activity生命周期?
onCreate() : 该方法是在Activity被建立时回调,它是生命周期第一个调用的方法,咱们在建立Activity时通常都须要重写该方法,而后在该方法中作一些初始化的操做,如经过setContentView设置界面布局的资源,初始化所须要的组件信息等。canvas
onStart : 此方法被回调时表示Activity正在启动,此时Activity已处于可见状态,只是尚未在前台显示,所以没法与用户进行交互。能够简单理解为Activity已显示而咱们没法看见摆了。设计模式
onResume() : 当此方法回调时,则说明Activity已在前台可见,可与用户交互了(处于前面所说的Active/Running形态),onResume方法与onStart的相同点是二者都表示Activity可见,只不过onStart回调时Activity仍是后台没法与用户交互,而onResume则已显示在前台,可与用户交互。固然从流程图,咱们也能够看出当Activity中止后(onPause方法和onStop方法被调用),从新回到前台时也会调用onResume方法,所以咱们也能够在onResume方法中初始化一些资源,好比从新初始化在onPause或者onStop方法中释放的资源。api
onPause() : 此方法被回调时则表示Activity正在中止(Paused形态),通常状况下onStop方法会紧接着被回调。但经过流程图咱们还能够看到一种状况是onPause方法执行后直接执行了onResume方法,这属于比较极端的现象了,这多是用户操做使当前Activity退居后台后又迅速地再回到到当前的Activity,此时onResume方法就会被回调。固然,在onPause方法中咱们能够作一些数据存储或者动画中止或者资源回收的操做,可是不能太耗时,由于这可能会影响到新的Activity的显示——onPause方法执行完成后,新Activity的onResume方法才会被执行。数组
onStop() : 通常在onPause方法执行完成直接执行,表示Activity即将中止或者彻底被覆盖(Stopped形态),此时Activity不可见,仅在后台运行。一样地,在onStop方法能够作一些资源释放的操做(不能太耗时)。缓存
onRestart() :表示Activity正在从新启动,当Activity由不可见变为可见状态时,该方法被回调。这种状况通常是用户打开了一个新的Activity时,当前的Activity就会被暂停(onPause和onStop被执行了),接着又回到当前Activity页面时,onRestart方法就会被回调。
onDestroy() :此时Activity正在被销毁,也是生命周期最后一个执行的方法,通常咱们能够在此方法中作一些回收工做和最终的资源释放。
2.两个activity分别是AB,在A中启动B,而后返回A,请问各自的生命周期?
当A中启动B时:A:onPause()进入暂停状态,当启动B时:onCreate()-onStart-onResume后B可见,A再执行onStop()中止 返回A时B的生命周期:先onPause()进入暂停状态,当返回A时:A生命周期:onReStart()-onStart()-onResume()后A从新可见。B再执行onStop()-onDestory()销毁界面
总结:当从一个activity到另外一个acitvity的时候,第一个activity会先进入一个暂停状态,只有当另一个activity可见(调用onResume())的时候第一个acitvity才会中止或者销毁
3.activity启动模式有几种并简单说明下各自的应用场景
1.Fragment的生命周期?
onAttach(): 这个时候 activity已经传进来了,得到activity的传递的值,就能够进行 与activity的通讯里,固然也可使用getActivity(),前提是这个fragment已经和宿主的activity关联,而且没有脱离他只调用一次。
onCreate(): 系统建立fragment的时候回调他,在他里面实例化一些变量 这些个变量主要是:当你 暂停 中止的时候 你想保持的数据,若是咱们要为fragment启动一个后台线程,能够考虑将代码放于此处。参数是:Bundle savedInstance, 用于保存 Fragment 参数,Fragement也能够重写 onSaveInstanceState(BundleoutState)方法, 保存Fragement状态;能够用于 文件保护他也只调用一次。
onCreateView():第一次使用的时候 fragment会在这上面画一个layout出来,为了能够画控件 要返回一个 布局的view,也能够返回null当系统用到fragment的时候 fragment就要返回他的view,越快越好,因此尽可能在这里不要作耗时操做,好比从数据库加载大量数据显示listview,固然线程仍是能够的。给当前的fragment绘制ui布局,可使用线程更新UI说白了就是加载fragment的布局的。这里通常都先判断是否为null
if(text==null){
Bundle args=getArguments();
text=args.getString("text");
}
if (view == null) {
view = inflater.inflate(R.layout.hello, null);
}
复制代码
这样进行各判断免得每次都要加载,减小资源消耗
-onStart():和activity等同启动, Fragement 启动时回调, 此时Fragement可见;
onResume():和activity等同在activity中运行是可见的激活, Fragement 进入前台, 可获取焦点时激活;
onPause():和activity等同 其余的activity得到焦点,这个仍然可见第一次调用的时候,指的是用户离开这个fragment(并非被销毁)一般用于 用户的提交(可能用户离开后不会回来了)
onStop():和activity等同fragment不可见的,可能状况:activity被stopped了 OR fragment被移除但被加入到回退栈中一个stopped的fragment仍然是活着的若是长时间不用也会被移除
onDestroyView():Fragment中的布局被移除时调用。表示fragemnt销毁相关联的UI布局清除全部跟视图相关的资源,之前觉得这里没什么用处其实 大有文章可作,相信你们都用过ViewPager+Fragment,因为ViewPager的缓存机制,每次都会加载3页。 例如:有四个 fragment 当滑动到第四页的时候 第一页执行onDestroyView(),但没有执行onDestroy。 他依然和activity关联。当在滑动到第一页的时候又执行了 onCreateView()。 生命周期能够本身试一下。 那么问题来了。会出现重复加载view的局面,因此这么作(下面是先人的代码)
@Override
public void onDestroyView() {
if(view!=null){
((ViewGroup)view.getParent()).removeView(view);
}
super.onDestroyView();
}
复制代码
onDestroy():销毁fragment对象跟activity相似了。
onDetach(): Fragment和Activity解除关联的时候调用。脱离activity
2.Fragemnt栈管理方式?
3.Fragment中出现内存泄露并致使闪退,请举例说出一种场景?
回答这个问题,咱们首先须要知道什么是内存泄露,什么会致使内存泄露。
内存泄露:在android中再也不使用的对象,Gc垃圾回收机制没法对其回收就会产生内存泄漏注:过多的内存泄漏会致使内存溢出OOM
4.activtiy,fragment数据传递通信方式
1.service的启动方式且各自的生命周期?
提示:若是咱们的service继承的时intentservice,通常这种状况就是咱们要在service中作耗时操做,然而intentservice在 onHandleIntent()中建立一个工做线程。而后在onDestory()中会自动关闭线程
2.BroadCastReceiver有几种分类及注册方式?
分类:(1)有序广播(2)标准广播
注册方式:(1)动态注册:就是代码注册广播(2)静态注册:在注册文件中注册
ContentProvider:
四大组件的内容提供者,主要用于对外提供数据
实现各个应用程序之间的(跨应用)数据共享,好比联系人应用中就使用了ContentProvider,你在本身的应用中能够读取和修改联系人的数据,不过须要得到相应的权限。其实它也只是一个中间人,真正的数据源是文件或者SQLite等
一个应用实现ContentProvider来提供内容给别的应用来操做,经过ContentResolver来操道别的应用数据,固然在本身的应用中也能够
ContentResolver:
内容解析者,用于获取内容提供者提供的数据
ContentResolver.notifyChange(uri)发出消息
ContentObserver:
内容监听器,能够监听数据的改变状态
目的是观察(捕捉)特定Uri引发的数据库的变化,继而作一些相应的处理,它相似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。触发器分为表触发器、行触发器,相应地ContentObsever也分为表ContentObserver、行ContentObserver,固然这是与它所监听的Uri MIME Type有关的
ContentResolver.registerContentObserver()监听消息
什么状况会致使内存泄露?
Fragment中出现内存泄露并闪退说明内存泄露频繁致使oom.
内存优化
这个问题就不是三言两语了,内存优化是个比较广的知识面,他包括内存泄漏,布局优化,资源优化,内存溢出等知识点
我这里就总结内存优化的几点吧:
此处我说下MVP模式,既然是设计模式那么写法确定不是固定的,所以每一个人对其理解都不同,我就在此说下我对mvp模式的理解吧。
MVP是MVC的升级版,其中M-Model数据模型,V-View就是咱们的视图,P-Presenter控制层,他控制M,V逻辑,使M,V彻底解耦。通常来讲咱们在M层定义一个接口和一个实现类,实现类中获取网络数据或者进行一些数据操做,P层中获取到,M,V层引用,把M层返回的结果交给V层,最后在V层作处理(更新ui一些操做)。
自定义view涉及到的知识点比较全面,也是个难点,通常来讲自定义view就是去实现一些咱们系统控件实现不了的效果,其具体流程就是:
其中onMeasue()就是测量,这个通常在组合控件中比较常见,onLayout()就是控制view的显示位置,onDraw()就是绘制view,绘制view涉及到canvas与paint一个画布一个画笔
说说跨进程通信? 跨进程通信即IPC,可是跨进行通信也很常见
对于android开发者来讲对于ANR这个词并不陌生,那么ANR是什么?什么状况会ANR?怎么避免?
所谓gc机制就是垃圾处理器一共须要两部:检测和回收垃圾
1.引用计数法:一个对象引用一次计数+1,引用失效-1
2.根搜索法:以根集对象为起始点进行搜索,若是有对象不可达的话,即为垃圾对象。这里的根集通常包括:java栈中的引用对象、本地方法栈中JNI的引用对象、方法区中运行常量池中的引用对象、方法区中静态属性引用的对象、运行中的线程、由引导类加载器加载的对象、GC控制的对象。总之,JVM在作垃圾回收的时候,会检查堆中的全部对象是否被这些根集中的对象所引用,不可以被引用的对象就会被垃圾回收器回收。
1.标记-清除:将全部须要回收的对象进行标记,而后统一清除
2.复制算法:将内存分两个区域,只使用其中一个区域存储对象,垃圾回收时遍历使用区域的全部对象并将使用对象复制到另一个区域。
3.标记-整理:此算法为前两个算法的结合体,分两个阶段,第一部将全部被使用的对象标记,第二部遍历堆中全部对象将未被标记的对象进行回收并整理存活对象
4.分代收集算法:年轻代:复制算法,老年代:标记-整理
1.对于HaspMap原理我也是才经过一些资料研究了一下,我先根据个人理解说说HashMap原理及结构
(1)HaspMap结构在jdk1.8之前:数组+链表也就是散列表形式,在jdk1.8加了一个红黑树其结构:数组+链表+红黑树 至于其意义接下来请看HashMap原理
(2)HashMap原理:首先咱们经过源码看HashMap他默认的数组大小16,每次扩容为其2倍,且数组大小为2的N次方。
数据由Entry实体类以键值对形式存储,而每一个实体类对应一个HashCode.在java中每一个对象都有一个hashcode()方法,它是一个由32位整数组成的,而在HashMap中咱们只须要9位Int整数型,当咱们put一个键值对的时候,会首先判断key是否位null,(为null默认返回第一个)而后根据K找到获取HashCode值而后对其进行hash运算获取到hash值,接着循环table根据其hash值及key判断是否相同若是相同就替换,若是不相同就添加一个Entry
另外特别注意:有可能hash冲突,也就是说咱们随机参数的int整数型重复了,这时候咱们就经过拉链表的方式进行处理,让每一个Entry以单向链表形式存在,当链表长度达到8就会采用红黑树提升性能,当链表长度小于8就从新由转换成单向链表
总结:HashMap默认数组大小为16,每一个键值对都是以Entry实体类存储的,而后每一个K会对应一个HashCode也就是Hash值,当咱们put一个数据的时候,首先会根据这个K获取到对应的hashcode经过hashcode获取到hash值,而后循环散列表也就是循环table判断k,hash值是否相等,若是相等就表示是更新数据,若是有一个不相等就表示添加数据(包括hash冲突,上面已经说到)。最后咱们也能够经过不一样构造函数设置数组默认大小,负载因子。
写在前面:说到android动画咱们脑海里就会想起自定义,平移,旋转等一些特效。其实android动画也是个知识面比较广的。
1.android动画分几类,说说他们的区别与特色?
总结:帧动画与补间动画归为:视图动画。属性动画为另外一类,此外我推荐另外两个动画,共享动画和过渡动画,有兴趣的朋友能够研究下
首先:字体用sp设置大小能够跟随系统字体大小改变而改变,控件用dp至少对不一样分辨率适配了
接下来咱们了解下sp,dp,dpi这几个单位:
px=dp*(dpi/160)
特别注意:假设如今我有两部手机分辨率不同,可是手机尺寸大小同样,假如第一部手机的像素密度及dpi=320,第二部手机的像素密度及dpi=160,我设置了一个2dp的宽的控件根据px=dp*(dpi/160)公式可得像素密度为160的px=dp及1px1dp,像素密度320的px=2*dp及1dp=2px.咱们能够想象像素密度越大那么说明像素之间很紧密,反之就稀疏。那么当我在160dpi手机上设置宽为2dp的时候此时2dp=2px,在320dpi手机上设置宽的为2dp的时候此时2dp=4px.上面说到像素密度越大像素之间就紧密,那么此时在手机尺寸大小同样的状况下。此时2px与4px宽度相差不大的。你能够把一个像素想象成一个点,像素密度(dpi)越大每一个点的距离就越小,反正就越大。这就是为何设置宽高要用dp了,由于它适配了不一样分辨率。
线程池的做用及原理?
首先咱们看下使用线程池的缘由:在咱们开发中咱们有时候须要频繁建立工做线程,而后这些工做线程及有可能致使线程阻塞,并且频繁建立线程也会下降性能
那么线程池的做用是什么呐?
根据上诉咱们知道:线程池就是用来管理一个个线程的,它不会频繁建立线程,并且对其进行复用这样就不会频繁调用gc,提升了效率及性能。
那么线程池的原理是什么?
要知道线程池原理首先咱们看下其构造函数:
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
复制代码
corePoolSize:核心线程最大数量
maximumPoolSize:最大线程数量
keepAliveTime:非核心线程的保活时间,就是说当非核心线程在空闲状态在保活时间,这样当工做任务时间短,能够避免频繁建立线程。提升效率及性能
unit:上面时间属性的单位
workQueue:任务队列
threadFactory:线程工厂,可用于设置线程名字等等
经过构造函数推出线程池的原理:
1.当有新的任务进入的时候(execute一个线程以后)会首先判断当前核心线程是否达到最大,若是没有就直接建立一个核心线程开执行任务
2.当有新任务进入的时候(execute一个线程以后)若是核心线程达到最大可是任务队列未满,那么次任务就会进入任务队列等待
3.当有新的任务进入的时候(execute一个线程以后)若是核心线程达到最大而且任务队列已经满了,可是线程数量未超过非核心线程最大数量,就建立一个新非核心线程执行任务
4.当有新任务进入的时候(execute一个线程以后)若是线程池中的线程数已经超过非核心线程数,则拒绝执行该任务,采起饱和策略,并抛出RejectedExecutionException异常