知识总结

1.handle的实现原理:
handle.sendMessage 在子线程发送一条消息
messageQueen 是一个消息队列,负责存储消息,有消息过来就存储起来
Looper.loop 是一个死循环,不停的从messagequeen中取出符合条件的消息
如果loop取到了一个消息就会调用handle的dispatchMessage方法 然后把从MessageQueue中取到的message传入进去

2.子线程中能不能直接new一个Handler
子线程可以new 一个handle 不过需要在子线程里先调用Looper.prepare(),new一个Handler后,还需要调用Looper.loop()方法。

  1. 主线程的Looper第一次调用loop方法,什么时候,哪个类
    在主线程中为什么没看到Looper.prepare()?其实系统已经给我们调用了,不过调用的是Looper.prepareMainLooper() 在SystemServer类中

4.Handler导致的内存泄露原因及其解决方案
当使用内部类(包括匿名类)来创建Handler的时候,Handler对象会隐式地持有一个外部类对象(通常是一个Activity)的引用(不然你怎么可能通过Handler来操作Activity中的View?)如果用户在网络请求过程中关闭了Activity,正常情况下,Activity不再被使用,它就有可能在GC检查时被回收掉,但由于这时线程尚未执行完,而该线程持有Handler的引用(不然它怎么发消息给Handler?),这个Handler又持有Activity的引用,就导致该Activity无法被回收(即内存泄露)
如何解决, 使用弱引用(WeakReference)。使用final static静态类 在ondestory中remove掉这个message

5.什么是WeakReference
软引用是在system.gc 不一定回收 软引用回收只会在jvm内存不足时回收 弱引用 system.gc方法执行后就会回收
WeakReference弱引用,与强引用(即我们常说的引用)相对,它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向(实际上多数时候还要求没有软引用,但此处软引用的概念可以忽略),该对象就会在被GC检查到时回收掉。

6.一个线程可以有几个Handler,几个Looper,几个MessageQueue对象
一个线程只能有一个looper 每一个Thread都有一个threadLocalMap变量 里面存着
key是当前线程(默认会是主线程) value为loop

一个线程可以有多个handler 但是他们使用的消息队列都是同一个,一个线程的looper只会创建一次,只有一个looper对象,一个looper下只有一个MessageQueue属性,所以一个线程只有一个MessageQueue。

7.Message对象创建的方式有哪些区别?
Message msg = new Message() 直接创建一个message对象
Message msg2 = Message.obtain(); 从message池中返回一个message对象
Message msg1 = handler1.obtainMessage(); 作用和第二步一样,

8.Handler 有哪些发送消息的方法
handle.sendMessage
handle.post

9.Handler的post与sendMessage的区别和应用场景
sendMessage发送的是Message对象
post发送的是Runble对象 runble对象中可以直接做更新ui的操作

10.handler postDealy后消息队列有什么变化,假设先 postDelay 10s, 再postDelay 1s, 怎么处理这2条消息
使用Handler的postDealy后消息队列可能会进行重新排序。消息队列里消息按执行先后时间进行排序,先执行的在前,后执行的在后。postDealy发送的消息会根据延迟时间与消息队列里存在的消息的执行时间进行比较,然后寻找插入位置插入消息。

11.MessageQueue是什么数据结构

基础数据结构中“先进先出”的一种数据结构

12.Handler怎么做到的一个线程对应一个Looper,如何保证只有一个MessageQueue
ThreadLocal在Handler机制中的作用

ThreadLocal 可以把一个对象保存在指定的线程中,对象保存后,只能在指定线程中获取保存的数据,对于其他线程来说则无法获取到数据。Android系统在 Handler 机制中使用了它来保证每一个 Handler 所在的线程中都有一个独立的 Looper 对象。

13.HandlerThread是什么 & 好处 &原理 & 使用场景

14.子线程能不能更新UI
不一定

15.为什么Android系统不建议子线程访问UI
单线程访问,是不需要加锁的,如果多个线程访问那就需要加锁,耗时会比较多,如果多线程访问不加锁,多个线程共同访问更新操作同一个UI控件时容易发生不可控的错误。

16.Android中为什么主线程不会因为Looper.loop()里的死循环卡死
对于线程既然是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出

在main方法中我们可以看到,最后调用 Looper.loop(),也就是说,其实我们的应用其实就是运行在死循环中的。如果不运行死循环中,那么程序运行完,就结束了。

阻塞,应用死循环,没有输入事件,MsgQ为空,Looper空闲状态,线程进入阻塞,释放CPU执行权,等待唤醒。

卡死(ARN),是指消息里有超时操作,导致应用处理不过来,后续发送的消息。所以,阻塞跟应用卡死(ARN)没有必然的联系。

17.MessageQueue#next 在没有消息的时候会阻塞,如何恢复?
当其他线程调用 MessageQueue#enqueueMessage 时会唤醒 MessageQueue,这个方法会被 Handler#sendMessage、Handler#post 等一系列发送消息的方法调用。

18.Handler消息机制中,一个looper是如何区分多个Handler的
因为在msg入队列时,会将msg.target设置一个handler,处理消息的时候,也会调用msg对象的target去处理消息

19.Looper.quit/quitSafely的区别
quit 执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息还是非延迟消息

quitSafely执行了MessageQueue中的removeAllFutureMessagesLocked方法,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。

20.通过Handler如何实现线程的切换
当在A线程中创建handler的时候,同时创建了Looper与MessageQueue,Looper在A线程中调用loop进入一个无限的for循环从MessageQueue中取消息。当B线程调用handler发送一个message的时候,会通过msg.target.dispatchMessage(msg);将message插入到handler对应的MessageQueue中,Looper发现有message插入到MessageQueue中,便取出message执行相应的逻辑,因为Looper.loop()是在A线程中启动的,所以则回到了A线程,达到了从B线程切换到A线程的目的

21.Handler 如何与 Looper 关联的
实际Looper和Handler就是通过ThreadLoacl把两者联系在了一起
Looper.prepare()生成了一个Looper对象(并且在生成Looper对象的同时生成了一个与之对应的MessageQueue对象,赋值给了Looper的mQueue成员变量)然后把这个Looper对象存入ThreadLocal中去。 在使用Handler的时候,在其构造函数中,先通过Looper的myLooper方法获取到当前线程对象的值。然后赋值给Handler的mLooper的这个成员变量,然后把Looper的mQueue成员变量的值赋值给Handler的mQueue成员变量。

22.Looper 如何与 Thread 关联的
Looper.prepare()

23.mvc的优缺点 适用于什么场景
优点:不需要变更view 和 model 只需要关注c层就可以
缺点:篇幅太长,c层逻辑可能会很复杂
适用于较小,功能较少,业务逻辑较少的项目。

24.mvp的优缺点
优点:便于测试
缺点:维护比较困难 要写很多类

25.mvvm优缺点
优点:双向绑定
缺点:数据绑定使得 Bug 很难被调试 一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存。 数据双向绑定不利于代码重用。客户端开发最常用的重用是View,但是数据双向绑定技术,让你在一个View都绑定了一个model,不同模块的model都不同。那就不能简单重用View了。

26.什么是OOM & 什么是内存泄漏以及原因
oom是内存溢出的意思, 原因两种 一种是分配少了 还有一种应用用的太多 没及时释放

27.Thread是如何造成内存泄露的,如何解决?
没有使用静态类 没有在ondestroy中关闭

28.MVP中如何处理Presenter层以防止内存泄漏的
在ondestory中 将presenter 和 view 解绑

29.glide原理

30.内存优化
少用静态变量
使用handle要及时清理
删除无用资源

31.启动优化
设置闪屏页面
优化application 可以开启子线程 还有一部分可以设置延迟初始化

32.布局加载和绘制优化
以前的话 尽量减少布局文件的层级
ConstraintLayout

33.卡顿优化
那就的具体时间具体来了 比如说sp不要存太复杂的value 比如复杂的json串

34.网络优化
启用keep-alive,okhttp中已默认打开,但需要服务器支持

35.什么是序列化 什么是反序列化
将对象转换为可传输的二进制流的过程。 把字节序列恢复成对象的过程。

36.为什么需要使用序列化和反序列化
方便数据的传输

37.Serializable 和 Parcelable 的区别
在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
Serializable会引起大量的system.gc
Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。

38.HashMap原理
1,hashmap是我们几乎每天用到的集合类,它以键值对的形式存在。
2,在jdk1.7中:底层是数组加链表,1.8中采用的是数组加链表加红黑树,红黑树的引入是为了提高查询效率
3,1.7中hash碰撞采用头插法,头插法会形成循环链表(?),1.8尾插法
4,hash算法1.8进行了简化,up主记不清了,
5,最好传入一个二的次幂的初始化容量, put时,会初始化数组,容量为大于等于初始化容量的最近的二次幂,比如初始化容量为6,他初始化就是8。
6,key的hash值 与 上容量-1,计算出所在位置
7,扩容的问题:加载因子0.75,达到 容量 *0.75后就会扩容,两倍扩容
8,树化,数组容量达到64,链表长度大于等于8,后才会进行树化,链表长度小于6就会解除树化

39.Java中创建线程的方式,Callable,Runnable,Future,FutureTask

40.四种线程池
Executors.newCachedThreadPool() 缓存线程池 创建十个 做个任务休息两秒 那么只有一个线程在工作
Executors.newFixedThreadPool() 定长线程池 可以定制最大线程数
Executors.newSingleThreadExecutor() 单一线程池
定时定周期线程池
val service = Executors.newSingleThreadScheduledExecutor()
service.schedule() //定时间
service.scheduleAtFixedRate() //定周期

41.synchronized和Lock的使用、区别,原理
lock是一个接口,而synchronized是java的一个关键字,synchronized是内置的语言实现;
synchronized在发生异常时候会自动释放占有的锁,因此不会出现死锁;而lock发生异常时候,不会主动释放占有的锁,必须手动unlock来释放锁,可能引起死锁的发生。(所以最好将同步代码块用try catch包起来,finally中写入unlock,避免死锁的发生。)

42.为什么会有线程安全?如何保证线程安全
线程安全是指在多线程环境下,程序可以始终执行正确的行为,符合预期的逻辑。()

43.EventBus 原理解析
EventBus 是一款在 Android 开发中使用的发布/订阅事件总线框架,基于观察者模式,将事件的接收者和发送者分开

44.Glide原理

45.ArrayList LinedList HashMap 区别
ArrayList 有序集合 底层为数组 按下标查找快增删慢 按元素查找、增删都慢
LinkedList 有序集合 底层为链表 按下标查找慢 增删快 按元素查找慢 增删比arrayList快
HashMap 无序哈希表 按下标查找一般比LinkedList快 增删快跟主体大小有关按元素查找快 增删快跟主体大小有关,越大越慢

46.OkHttp 中addInterceptor和addNetworkInterceptor的区别
addInterceptor 有无网络都会被调用到。拦截器只会被调用一次,调用chain.proceed()得到的是重定向之后最终的应信息,不会通过chain.connection() 获得中间过程的响应信息。允许短路,并且允许不去调用chain.proceed()请求服务器数据,可通过缓存来返回数据。
addNetworkInterceptor 无网络时不会被调用。可以显示更多的信息,比如OkHttp为了减少数据的传输时间以及传输流量而自动添加的请求头Accept-Encoding: gzip,从而希望服务器能返回经过压缩过的响应数据。chain.connection()返回不为空的Connection对象,可以查询到客户端所连接的服务器的IP地址以及TLS配置信息

47.简单介绍一下Glide缓存
Glide 缓存机制主要分为2种:内存缓存和磁盘缓存
使用内存缓存的原因是:防止应用重复将图片读入到内存,造成内存资源浪费。
使用磁盘缓存的原因是:防止应用重复从网络或其它地方下载和读取数据。
Glide 默认是使用内存缓存

48.具体说说Glide的三级缓存原理
先从glide的缓存文件中取 如果没有 去内存中取。如果还没有就从硬盘取。 还没有的话 进行网络请求,请求完毕,将图片缓存到硬盘中,在缓存到内存中 在缓存到activityResources中 完成加载。

你可能感兴趣的:(知识总结)