一、前言:
这一篇是安卓高端面试的技术题,这些题目有点技术含量,有些没有具体的答案,需要好点时间去研究一下的。
二、图片面试
1. 图片库对比
2. LRUCache原理
LruCache是个泛型类,主要原理是:把最近使用的对象用强引用存储在LinkedHashMap中,当缓存满时,把最近最少使用的对象从内存中移除,并提供get/put方法完成缓存的获取和添加。LruCache是线程安全的,因为使用了synchronized关键字。
当调用put()方法,将元素加到链表头,如果链表中没有该元素,大小不变,如果没有,需调用trimToSize方法判断是否超过最大缓存量,trimToSize()方法中有一个while(true)死循环,如果缓存大小大于最大的缓存值,会不断删除LinkedHashMap中队尾的元素,即最少访问的,直到缓存大小小于最大缓存值。当调用LruCache的get方法时,LinkedHashMap会调用recordAccess方法将此元素加到链表头部。
3. 图片加载原理
4. Glide源码解析
1)Glide.with(context)创建了一个RequestManager,同时实现加载图片与组件生命周期绑定:在Activity上创建一个透明的ReuqestManagerFragment加入到FragmentManager中,通过添加的Fragment感知Activty\Fragment的生命周期。因为添加到Activity中的Fragment会跟随Activity的生命周期。在RequestManagerFragment中的相应生命周期方法中通过liftcycle传递给在lifecycle中注册的LifecycleListener
2)RequestManager.load(url) 创建了一个RequestBuilder对象 T可以是Drawable对象或是ResourceType等
5. Glide使用什么缓存?
- 内存缓存:LruResourceCache(memory)+弱引用activeResources
Map<Key, WeakReference<EngineResource<?>>> activeResources正在使用的资源,当acquired变量大于0,说明图片正在使用,放到activeResources弱引用缓存中,经过release()后,acquired=0,说明图片不再使用,会把它放进LruResourceCache中
- 磁盘缓存:DiskLruCache,这里分为Source(原始图片)和Result(转换后的图片)
第一次获取图片,肯定网络取,然后存active\disk中,再把图片显示出来,第二次读取相同的图片,并加载到相同大小的imageview中,会先从memory中取,没有再去active中获取。如果activity执行到onStop时,图片被回收,active中的资源会被保存到memory中,active中的资源被回收。当再次加载图片时,会从memory中取,再放入active中,并将memory中对应的资源回收。
之所以需要activeResources,它是一个随时可能被回收的资源,memory的强引用频繁读写可能造成内存激增频繁GC,而造成内存抖动。资源在使用过程中保存在activeResources中,而activeResources是弱引用,随时被系统回收,不会造成内存过多使用和泄漏。
6. Glide内存缓存如何控制大小?
Glide内存缓存最大空间(maxSize)=每个进程可用最大内存0.4(低配手机是 每个进程可用最大内存0.33)
磁盘缓存大小是250MB int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;
三、网络和安全机制
1. 网络框架对比和源码分析
2. 自己去设计网络请求框架,怎么做?
3. 网络请求缓存处理,okhttp如何处理网络缓存的;
(1) 网络缓存优先考虑强制缓存,再考虑对比缓存
–首先判断强制缓存中的数据的是否在有效期内。如果在有效期,则直接使用缓存。如果过了有效期,则进入对比缓存。
–在对比缓存过程中,判断ETag是否有变动,如果服务端返回没有变动,说明资源未改变,使用缓存。如果有变动,判断Last-Modified。
–判断Last-Modified,如果服务端对比资源的上次修改时间没有变化,则使用缓存,否则重新请求服务端的数据,并作缓存工作。
(2)okhttp缓存
开启使用Okhttp的缓存其实很简单,只需要给OkHttpClient对象设置一个Cache对象即可,创建一个Cache时指定缓存保存的目录和缓存最大的大小即可。
//新建一个cache,指定目录为外部目录下的okhttp_cache目录,大小为100M
Cache cache = new Cache(new File(Environment.getExternalStorageDirectory() + “/okhttp_cache/”), 100 * 1024 * 1024);
//将cache设置到OkHttpClient中,这样缓存就开始生效了。
OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
4. 从网络加载一个10M的图片,说下注意事项
5. TCP的3次握手和四次挥手
6. TCP与UDP的区别
7. TCP与UDP的应用
8. HTTP协议
9. HTTP1.0与2.0的区别
10. HTTP报文结构
11. HTTP与HTTPS的区别以及如何实现安全性
12. 如何验证证书的合法性?
13. https中哪里用了对称加密,哪里用了非对称加密,对加密算法(如RSA)等是否有了解?
14. client如何确定自己发送的消息被server收到?
15. 谈谈你对WebSocket的理解
16. WebSocket与socket的区别
17. 谈谈你对安卓签名的理解。
18. 请解释安卓为啥要加签名机制?
19. 视频加密传输
20.App 是如何沙箱化,为什么要这么做?
21.权限管理系统(底层的权限是如何进行 grant 的)?
四、数据库
1. sqlite升级,增加字段的语句
2. 数据库框架对比和源码分析
3. 数据库的优化
4. 数据库数据迁移问题
五、算法
1. 排序算法有哪些?
2. 最快的排序算法是哪个?
3. 手写一个冒泡排序
4. 手写快速排序代码
5. 快速排序的过程、时间复杂度、空间复杂度
6. 手写堆排序
7. 堆排序过程、时间复杂度及空间复杂度
8. 写出你所知道的排序算法及时空复杂度,稳定性
9. 二叉树给出根节点和目标节点,找出从根节点到目标节点的路径
10. 给阿里2万多名员工按年龄排序应该选择哪个算法?
11. GC算法(各种算法的优缺点以及应用场景)
12. 蚁群算法与蒙特卡洛算法
13. 子串包含问题(KMP 算法)写代码实现
14. 一个无序,不重复数组,输出N个元素,使得N个元素的和相加为M,给出时间复杂度、.空间复杂度。手写算法
15. 万亿级别的两个URL文件A和B,如何求出A和B的差集C(提示:Bit映射->hash分组->多文件读写效率->磁盘寻址以及应用层面对寻址的优化)
16. 百度POI中如何试下查找最近的商家功能(提示:坐标镜像+R树)。
17. 两个不重复的数组集合中,求共同的元素。
18. 两个不重复的数组集合中,这两个集合都是海量数据,内存中放不下,怎么求共同的元素?
19. 一个文件中有100万个整数,由空格分开,在程序中判断用户输入的整数是否在此文件中。说出最优的方法
20. 一张Bitmap所占内存以及内存占用的计算
一张图片(bitmap)占用的内存影响因素:图片原始长、宽,手机屏幕密度,图片存放路径下的密度,单位像素占用字节数
bitmapSize=图片长度*(inTargetDensity手机的density / inDensity图片存放目录的density)宽度(手机的inTargetDensity / inDensity目标存放目录的density)*单位像素占用的字节数(图片长宽单位是像素)
1)图片长宽单位是像素:单位像素字节数由其参数BitmapFactory.Options.inPreferredConfig变量决定,它是Bitmap.Config类型,包括以下几种值:ALPHA_8图片只有alpha值,占用一个字节;ARGB_4444 一个像素占用2个字节,A\R\G\B各占4bits;ARGB_8888一个像素占用4个字节,A\R\G\B各占8bits(高质量图片格式,bitmap默认格式);ARGB_565一个像素占用2字节,不支持透明和半透明,R占5bit, Green占6bit, Blue占用5bit. 从Android4.0开始该项无效。
2) inTargetDensity 手机的屏幕密度(跟手机分辨率有关系)
inDensity原始资源密度(mdpi:160; hdpi:240; xhdpi:320; xxhdpi:480; xxxhdpi:640)
当Bitmap对象在不使用时,应该先调用recycle(),再将它设置为null,虽然Bitmap在被回收时可通过BitmapFinalizer来回收内存。但只有系统垃圾回收时才会回收。Android4.0之前,Bitmap内存分配在Native堆中,Android4.0开始,Bitmap的内存分配在dalvik堆中,即Java堆中,调用recycle()并不能立即释放Native内存。
21. 2000万个整数,找出第五十大的数字?
22. 烧一根不均匀的绳,从头烧到尾总共需要1个小时。现在有若干条材质相同的绳子,问如何用烧绳的方法来计时一个小时十五分钟呢?
23.求1000以内的水仙花数以及40亿以内的水仙花数
####24. 5枚硬币,2正3反如何划分为两堆然后通过翻转让两堆中正面向上的硬8币和反面向上的硬币个数相同
25. 时针走一圈,时针分针重合几次
26. N*N的方格纸,里面有多少个正方形
27. x个苹果,一天只能吃一个、两个、或者三个,问多少天可以吃完?
六、插件化、模块化、组件化、热修复、增量更新、Gradle
1. 对热修复和插件化的理解
2. 插件化原理分析
3. 模块化实现(好处,原因)
4. 热修复,插件化
5. 项目组件化的理解
6. 描述清点击 Android Studio 的 build 按钮后发生了什么
七、架构设计和设计模式
1. 谈谈你对Android设计模式的理解
2.MVC MVP MVVM原理和区别
3.你所知道的设计模式有哪些?
4.项目中常用的设计模式
5.手写生产者/消费者模式
6.写出观察者模式的代码
7.适配器模式,装饰者模式,外观模式的异同?
8.用到的一些开源框架,介绍一个看过源码的,内部实现过程。
9.谈谈对RxJava的理解
RxJava是基于响应式编程,基于事件流、实现异步操(类似于Android中的AsyncTask、Handler作用)作的库,基于事件流的链式调用,使得RxJava逻辑简洁、使用简单。RxJava原理是基于一种扩展的观察者模式,有四种角色:被观察者Observable 观察者Observer 订阅subscribe 事件Event。RxJava原理可总结为:被观察者Observable通过订阅(subscribe)按顺序发送事件(Emitter)给观察者(Observer), 观察者按顺序接收事件&作出相应的响应动作。
10. Rxjava发送事件步骤:
1)创建被观察者对象Observable&定义需要发送的事件
Observable.create(new ObservableOnSubscribe<T>(){
@Override
public void subscribe(ObservableEmitter<T> emitter) throws Exception {
//定义发送事件的行为
}
});
Observable.create()方法实际创建了一个ObservableCreate对象,它是Observable的子类,传入一个ObservableOnSubscribe对象,复写了发送事件行为的subscribe()方法。
2)创建观察者对象Observer&定义响应事件的行为
Observer observer = new Observer<T>() {
@Override
public void onSubscribe(Disposable d){//Disposable对象可用于结束事件
//默认最先调用
}
@Override
public void onNext(T t){
}
@Override
public void onError(Throwable d){
}
@Override
public void onComplete(){
}
}
3)通过subscribe()方法使观察者订阅被观察者
Observable.subscribe(Observer observer);//实际调用的是ObservableCreate.subscribeActual()方法,具体实现如下
protected void subscribeActual(Observer<? super T> observer) {
// 1. 创建1个CreateEmitter对象用于发射事件(封装成1个Disposable对象)
CreateEmitter<T> parent = new CreateEmitter<T>(observer);
// 2. 调用观察者(Observer)的onSubscribe()
observer.onSubscribe(parent);
try {
// 3. 调用source对象的(ObservableOnSubscribe对象)subscribe()
source.subscribe(parent);
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
parent.onError(ex);
}
}