2019年Android岗位BAT等大厂面试题知识点小结
2019年了搜集了很多面试题,希望能对大家有所帮助
1.View的绘制流程;自定义View如何考虑机型适配;自定义View的事件分发机制;View和ViewGroup分别有哪些事件分发相关的回调方法;自定义View如何提供获取View属性的接口。
View的绘制流程:
流程一:mesarue()过程;
流程二:layout布局过程;
流程三:draw()绘图过程。
详情:http://blog.csdn.net/qinjuning/article/details/7110211/
自定义View如何考虑机型适配:
这篇博文解决了Android适配的绝大多数问题:
http://blog.csdn.net/zhaokaiqiang1992/article/details/45419023
自定义View的事件分发机制:
对于 dispatchTouchEvent,onTouchEvent,return true是终结事件传递。return false 是回溯到父View的onTouchEvent方法。
ViewGroup 想把自己分发给自己的onTouchEvent,需要拦截器onInterceptTouchEvent方法return true 把事件拦截下来。
ViewGroup 的拦截器onInterceptTouchEvent 默认是不拦截的,所以return super.onInterceptTouchEvent()=return false;
View 没有拦截器,为了让View可以把事件分发给自己的onTouchEvent,View的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent。
对于ACTION_MOVE、ACTION_UP总结:ACTION_DOWN事件在哪个控件消费了(return true), 那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传,如果ACTION_DOWN事件是在dispatchTouchEvent消费,那么事件到此为止停止传递,如果ACTION_DOWN事件是在onTouchEvent消费的,那么会把ACTION_MOVE或ACTION_UP事件传给该控件的onTouchEvent处理并结束传递。
详情:http://www.jianshu.com/p/e99b5e8bd67b
以及下面这个的第五部分:
http://imtianx.cn/2016/12/17/Android%20View%20%E7%9A%84%E4%BA%8B%E4%BB%B6%E4%BD%93%E7%B3%BB/
View和ViewGroup分别有哪些事件分发相关的回调方法:
View中跟事件分发机制有关的就是两个方法:dispatchTouchEvent和onTouchEvent。整个View的事件转发流程是:
View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent
在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。
对于ViewGroup中的分发事件:
如果ViewGroup找到了能够处理该事件的View,则直接交给子View处理,自己的onTouchEvent不会被触发;
可以通过复写onInterceptTouchEvent(ev)方法,拦截子View的事件(即return true),把事件交给自己处理,则会执行自己对应的onTouchEvent方法
子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup对其MOVE或者UP事件进行拦截;
详情:http://blog.csdn.net/lmj623565791/article/details/38960443
http://blog.csdn.net/lmj623565791/article/details/39102591
自定义View如何提供获取View属性的接口:
关于View所要具备的一般功能,View类中都有了基本的实现,比如确定位置,它有layout方法,当然,这个只适用于ViewGroup,实现自己的ViewGroup时,才需要修改该方法。确定大小,它有onMeasure方法,如果你不满意默认的确认大小的方法,也可以自己定义。改变默认的绘制,就覆写onDraw方法。
详情:http://www.jianshu.com/p/d507e3514b65
2.Art和Dalvik对比;虚拟机原理,如何自己设计一个虚拟机(内存管理,类加载,双亲委派);JVM内存模型及类加载机制;内存对象的循环引用及避免
Art和Dalvik对比:
Dalvik是Google公司自己设计用于Android平台的虚拟机。
ART即Android Runtime
Dalvik和ART的几点区别:
dalvik是执行的时候编译+运行,安装比较快,开启应用比较慢,应用占用空间小
ART是安装的时候就编译好了,执行的时候直接就可以运行的,安装慢,开启应用快,占用空间大
用个比喻来说就是,骑自行车
dalvik 是已经折叠起来的自行车,每次骑都要先组装自行车才能骑
ART 是已经组装好的自行车,每次骑直接上车就能走人
详情:http://www.jb51.net/article/88708.htm
虚拟机原理,如何自己设计一个虚拟机(内存管理,类加载,双亲委派):
在加载阶段,java虚拟机需要完成以下3件事:
a.通过一个类的全限定名来获取定义此类的二进制字节流。
b.将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构。
c.在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口。
类加载:
从类被加载到虚拟机内存中开始,到卸载出内存为止,类的生命周期包括加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段
双亲委派:
系统提供的类加载器主要有下面3个:
.启动类加载器(Bootstarp ClassLoader)
.扩展类加载器(Extension ClassLoader)
.应用程序类加载器(Application ClassLoader)
注意:上述三个JDK提供的类加载器虽然是父子类加载器关系,但是没有使用继承,而是使用了组合关系。
详情:http://blog.csdn.net/ochangwen/article/details/51473120
http://blog.csdn.net/ochangwen/article/details/51472311
JVM内存模型及类加载机制:
JVM要想执行class文件,首先会将文件载入虚拟机的方法区内,根据类文件的格式相应的存放数据。在需要产生对象时,从方法区中获取对应的类信息,在堆中建立对象。
详情:http://blog.csdn.net/dragon\_cat\_han/article/details/52150955
内存对象的循环引用及避免:
如果有两个或者以上的对象,它们彼此引用,就会造成循环引用。
避免:引用计数GC处理,引用遍历GC处理。
详情:http://www.wtoutiao.com/p/11elsk6.html
3.内存回收机制与GC算法(各种算法的优缺点以及应用场景);GC原理时机以及GC对象;内存泄露场景及解决方法;OOM的避免及解决方法
内存回收机制与GC算法(各种算法的优缺点以及应用场景):
GC是通过对象是否存活来决定是否进行回收,最常用的判定算法是引用计数算法以及Java中使用的判定算法为根搜索算法(GC Roots Tracing)
常用的垃圾收集算法
a、标记——清除算法(Mark——Sweep)
首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
缺点:效率问题,标记和清除过程的效率不高;
b、复制算法(Copying)
为了解决标记清除算法的效率问题,复制算法将可用的内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块上,然后再把已使用过的内存空间一次清理掉。
优点:实现简单,运行高效
缺点:对内存空间的利用不高,可用内存变成一半,这代价过高
c、标记——整理算法(Mark——Compact)
与标记清除算法的标记阶段相同,但标记后会将所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。这种算法一般用于老年代的内存回收上,因为老年代中对象的存活时间都比较长,可能存在100%存活的极端情况,因此不能选择Copying算法来进行回收。
d、分代收集算法(Generational Collection)
这种算法只是根据对象的存活周期的不同将内存划分为几块,一般都划分为新生代和老年代,这样可以根据各个年代的特点采用最适当的收集算法。新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,因此选取复制算法,只需要付出少量存活对象的复制成本就可以完成收集;老年代中因为对象存活率高,没有额外的空间对它进行分配担保,就必须使用“标记——清除”或是“标记——整理”算法来进行回收。在之前的三种算法中已经有所描述。
详情:http://www.open-open.com/lib/view/open1380593930103.html
GC原理时机以及GC对象:
JVM 分别对新生代和旧生代采用不同的垃圾回收机制
Java中那些不可达的对象就会变成垃圾。那么什么叫做不可达?其实就是没有办法再引用到该对象了。主要有以下情况使对象变为垃圾:
1.对非线程的对象来说,所有的活动线程都不能访问该对象,那么该对象就会变为垃圾。
2.对线程对象来说,满足上面的条件,且线程未启动或者已停止。
GC将负责回收所有“不可达”对象的内存空间。
详情:https://segmentfault.com/a/1190000002579346
内存泄露场景及解决方法:
因为静态变量造成的内存泄漏;
Handler 的错误使用;
非静态内部类的静态实例的错误使用;
不正确使用线程,造成内存泄漏;
资源没有及时关闭;
详情例子解决方案参见:http://blog.csdn.net/adrian24/article/details/53248255
或者http://www.jianshu.com/p/51072faadf51
OOM的避免及解决方法:
高效加载大图片:
在展示高分辨率图片的时候,最好先将图片进行压缩。压缩后的图片大小应该和用来展示它的控件大小相近,在一个很小的ImageView上显示一张超大的图片不会带来任何视觉上的好处,但却会占用我们相当多宝贵的内存,而且在性能上还可能会带来负面影响。下面我们就来看一看,如何对一张大图片进行适当的压缩,让它能够以最佳大小显示的同时,还能防止OOM的出现。
使用图片缓存技术:
内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。其中最核心的类是LruCache (此类在android-support-v4的包中提供) 。这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。
详情:http://blog.csdn.net/guolin\_blog/article/details/9316683
4.四大组件及生命周期;ContentProvider的权限管理(读写分离,权限控制-精确到表级,URL控制);Activity的四种启动模式对比;Activity状态保存于恢复
都是一些基础知识,详情归类下:
Activity:详细知识:http://blog.csdn.net/amazing7/article/details/51244219
Service:详细知识:http://blog.csdn.net/amazing7/article/details/51305911
ContentProvider:详细知识:http://blog.csdn.net/amazing7/article/details/51324022
BroadcastReceiver:详细知识:http://blog.csdn.net/amazing7/article/details/51352139
ContentProvider的权限管理:http://blog.csdn.net/robertcpp/article/details/51337891
Activity的四种启动模式对比:http://blog.csdn.net/knlnzhao/article/details/8005277
Activity状态保存于恢复:http://blog.csdn.net/js331455217/article/details/40930157
5.Fragment生命周期;Fragment状态保存
生命周期及相关:http://blog.csdn.net/amazing7/article/details/51282082
状态保存:http://www.jianshu.com/p/75dc2f51cd63
6.startActivityForResult是哪个类的方法,在什么情况下使用,如果在Adapter中使用应该如何解耦
startActivityForResult是Activity类里的方法,在原Activity里通过Intent跳转到其他类再跳回到原Activity里时候,回传数据所用
详情:http://blog.csdn.net/sunchaoenter/article/details/6612039
在Adapter以及其他非Activity类使用的时候,可以将由原Activity类传入的Context强转为Activity类,再在原Activity里重写onActivityResult方法接受到返回值。
7.AsyncTask原理及不足;IntentService原理
AsyncTask是一个轻量级的异步类,继承时候有三个泛型参数:
1. Params,在执行AsyncTask时需要传入的参数,可用于在后台任务中使用;
2. Progress,后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位;
3. Result,当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
需要重写四个方法(至少是2个,2和4):
1. onPreExecute(),这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
2. doInBackground(Params...),这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
3. onProgressUpdate(Progress...),当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4. onPostExecute(Result),当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
AnsycTask执行任务时,内部会创建一个进程作用域的线程池来管理要运行的任务,也就就是说当你调用了AsyncTask.execute()后,AsyncTask会把任务交给线程池,由线程池来管理创建Thread和运行Therad,本质上是对Thread+Handler的良好封装,减少了开发者处理问题的复杂度,提高了开发效率。
AsyncTask缺陷:
最大的缺点:在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来。
其他的参考下面的文章。
详情:http://blog.csdn.net/liuhe688/article/details/6532519
http://blog.csdn.net/boyupeng/article/details/49001215
IntentService原理
IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。
详情:http://blog.csdn.net/qq\_18402085/article/details/50753005
http://blog.csdn.net/ryantang03/article/details/8146154/
8.AstncTask+HttpClient与AsyncHttpClient有什么区别
AsyncHttpClient来自android-async-http库是在Apache的HttpClient库的基础上开发构建而成的,这里的异步,是指它所有的网络请求都是在app的UI线程之外的独立工作线程中执行。而开发者通过利用Android的消息处理机制,把我们所需要编写的回调函数放在这个回调函数的创建线程中执行(一般就是UI线程),所以使用起来非常方便除了能应用在开发普通App上,还可以用来开发Service或后台线程,async-http-client库可以自已分辨是被用在哪一种应用下,不需要额外的设置。
详情:https://my.oschina.net/u/725054/blog/494494
http://blog.csdn.net/wangpeng047/article/details/19624529
9.如何保证一个后台服务不被杀死;比较省电的方式是什么
服务不被杀死分3种来讨论
1.系统根据资源分配情况杀死服务
2.用户通过 settings -> Apps -> Running -> Stop 方式杀死服务
3.用户通过 settings -> Apps -> Downloaded -> Force Stop 方式杀死服务
第一种情况:
用户不干预,完全靠系统来控制,办法有很多。比如 onStartCommand() 方法的返回值设为 START_STICKY ,服务就会在资源紧张的时候被杀掉,然后在资源足够的时候再恢复。当然也可设置为前台服务,使其有高的优先级,在资源紧张的时候也不会被杀掉。
第二种情况:
用户干预,主动杀掉运行中的服务。这个过程杀死服务会通过服务的生命周期,也就是会调用 onDestory() 方法,这时候一个方案就是在 onDestory() 中发送广播开启自己。这样杀死服务后会立即启动。
当然,从理论上来讲这个方案是可行的,实验一下也可以。但有些情况下,发送的广播在消息队列中排的靠后,就有可能服务还没接收到广播就销毁了(这是我对实验结果的猜想,具体执行步骤暂时还不了解)。所以为了能让这个机制完美运行,可以开启两个服务,相互监听,相互启动。服务A监听B的广播来启动B,服务B监听A的广播来启动A。经过实验,这个方案可行,并且用360杀掉后几秒后服务也还是能自启的。到这里再说一句,如果不是某些功能需要的服务,不建议这么做,会降低用户体验。
第三种情况:
强制关闭就没有办法。这个好像是从包的level去关的,并不走完整的生命周期。所以在服务里加代码是无法被调用的。处理这个情况的唯一方法是屏蔽掉 force stop和 uninstall 按钮,让其不可用。方法自己去找吧。当然有些手机自带的清理功能就是从这个地方清理的,比如华为的清理。所以第三种情况我也没有什么更好的办法了。
详情:http://www.tuicool.com/articles/iu22QnF
10.如何通过广播拦截和abort一条短信;广播是否可以请求网络;广播引起anr的时间限制
目前找到方法都是适用于Android4.4默认短信应用以前的方法:
第一步:新建一个类继承BroadcastReceiver,并重写onReceive()方法.
第二步:订阅短信的广播Intent,订阅方法有两种:
1:使用代码进行订阅
2:在AndroidManifest.xml文件中的
在Android中,每次广播消息到来时都会创建BroadcastReceiver实例并执行onReceive() 方法,
onReceive() 方法执行完后,BroadcastReceiver 的实例就会被销毁。拦截短信的方法就写在onReceive() 方法里即可。
详情:http://blog.csdn.net/jason0539/article/details/11720419
广播可以监听网络状态,网络变化等,好像不能请求网络。
在 Android中,程序的响应(Responsive)被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视,当BroadcastReceiver在10秒内没有执行完毕,Android会认为该程序无响应,所以在 BroadcastReceiver里不能做一些比较耗时的操作,否则会弹出ANR(Application No Response)的对话框。如果需要完成一项比较耗时的工作,应该通过发送Intent给Service,由Service来完成,而不是使用子线程的方法来解决,因为BroadcastReceiver的生命周期很短(在onReceive()执行后BroadcastReceiver的实例就会被销毁),子线程可能还没有结束BroadcastReceiver就先结束了。如果BroadcastReceiver结束了,它的宿主进程还在运行,那么 子线程还会继续执行。但宿主进程此时很容易在系统需要内在时被优先杀死。因为它属于空进程(没有任何活动组件的进程)。
详情:http://blog.csdn.net/wufen1103/article/details/7839107
11.进程间通信,AIDL
aidl是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口
详情:http://blog.csdn.net/stonecao/article/details/6425019
12.Handler机制及底层实现
详情:http://www.jianshu.com/p/9e4d1fab0f36
http://blog.csdn.net/itachi85/article/details/8035333
13.Binder机制及底层实现
详情:http://blog.csdn.net/weijinqian0/article/details/52233529
14.ApplicationContext和ActivityContext的区别
Context,中文直译为“上下文”,它是一个抽象类,描述的是一个应用程序环境的信息,通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作。
应用程序创建Context实例的情况一共有三种:
1、创建Application 对象时
2、创建Service对象时
3、创建Activity对象时
其中对于ApplicationContext,每个应用程序在第一次启动时,都会首先创建Application对象。如果对应用程序启动一个Activity(startActivity)流程比较清楚的话,创建Application的时机在创建handleBindApplication()方法中,该函数位于 ActivityThread.java类中;
对于ActivityContext,通过startActivity()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时,就会回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去创建一个Activity实例,并且回调onCreate(),onStart()方法等, 函数都位于 ActivityThread.java类。