拆轮子笔记

300 阅读7分钟

一Handler

调用prepare(),内部创建Looper和MessageQueue对象(线程单例)。接着调用loop方法开启循环,队列中有Message,就获取Message的target(也就是handler对象),分发Message到Handler对象的handleMessage方法中。

队列中的消息是通过Looper对象获取MessageQueue添加进去的。所以Handler传入的是哪个线程的Looper对象,handleMessage方法就是在哪个线程执行。

源码场景:ActivityThread类中Main线程和binder线程通讯;Retrofit2中callAdapter切换回掉线程;HandlerThread源码体现。

注意:消息是串行执行。

二Activity

ActivityManagerService在Binder线程向主线发送创建Activity消息,主线程通过反射创建Activity对象,创建ContextImpl对象,调用Activity的attach依赖注入ContextImpl对象,接着创建Window对象和WindowManager对象。接着调用Activity的onCreate和onStart方法(我们在onCreate中调用setContentView方法,内部解析布局成View,把View挂载到DoctorView上)

当页面Resume时候,把DoctorView添加到Window上,内部会调用RequestLayout方法,完成View的测量,布局,绘制。

思考:Activity是什么?它是AMS回调类,是一个ContextImpl的包装类,他是Window的包装类;

注意:Activity的生命周期都是通过AMS控制的。

三Service

ActivityManagerService在Binder线程向主线发送创建Service消息,主线程通过反射创建Service对象,创建ContextImpl对象,注入ContextImpl对象。紧接着调用Service的生命周期方法。

思考:Service是什么?它是一个回调类,是一个ContextImpl的包装类。

注意:Service的生命周期都是通过AMS控制的。默认是在主线程中执行的。

onStartCommand方法参数startId的作用是在stopSelf中传递的,如果服务还有没执行完的线程,stopSelf关闭服务无效。

返回值作用:黏性服务,非黏性服务,含有Intnent的黏性服务(控制意外销毁服务是否自动创建)。

因为Service默认是在主线程的,IntentService通过在OnCreate中创建HandlerThread实现在子线程中执行任务。

四measure

测量的目的是:View树上的每个View类成员变量mMeasuredWidth和mMeasuredHeight赋值。策略的过程用图比较好理解:

需要注意:measureChind中的参数都是父布局的布局参数+子类的LayoutParams结合出来的,不同的容器布局,结合的逻辑不同。

测量的本质是递归,子View确定完大小,父View才能确定大小。

五layout

布局的目的是:View树上的每个View类成员变量mLeft、mRight、mTop和mBottom赋值的过程,注意,他们的坐标原点都是相对于父布局的左上角为坐标原点。通过LayoutParams的参数和控件的宽高,就可以赋值上边四个成员变量。

布局的本质是递归,父布局确定好位置,子View才能确定位置。

六事件分发

 事件分发的本质:MotionEvent对象在View传递的过程。整体传递流程:DoctorView-->Activity-->Window-->ViewGroup---->View。从外向内传递,从内到外判断是否消耗。

Down事件到来,把mFirstTouchTarget至为null,判断是否拦截,如果不拦截,找到点击区域的View,判断是否接收,如果接收,mFirstTouchTarget赋值,等下一个事件到来的时候,直接找到接收的View,直接传递。拦截事件就调用自身的mFirstTouchTarget变成null。

注意调用chind的dispatchTouchEvent方法还是调用super的dispatchTouchEvent方法。

七okhttp3

源码逻辑:

异步:创建线程池,创建runnable扔到线程池,传递Request递归调用拦截器,最终拿到response,在子线程回调扔出去响应(最大并发数64);

同步:传递Request递归调用拦截器,最终拿到response,直接扔出去响应结果

有五个内置拦截器,重试和重定向拦截器,封装请求头拦截器,缓存get拦截器,连接池拦截器,联网拦截器。

解决问题:内部封装了线程池,请求头,响应体封装(不彻底,没有解析成自定义类型)

八Rxjava2

每调用一次操作符方法内部都创建一个新的Observable(多态)对象,创建这个Observable对象持有上游的Observable对象(this)。

订阅:每一步都会生成对应的Observer,对存储上游的Observable进行订阅。订阅的执行顺序是由下到上的。(在下游的订阅中调用上游订阅)

执行:先执行每一步传入的函数操作,然后将操作后的数据交给下游的Observer继续处理。 数据的传递和处理顺序是由上到下的。(在上游的OnNext中调用下游的OnNext方法)

线程切换: source.subscribe(parent);放入runnable类中,指定不同的线程,事件就会在不同的线程中生产。 worker.schedule(observer);仅仅指定了observer的工作线程

九Retrofit2

Retrofit2本质是对okhttp3的进一步封装,根据注解生成Request类,根据返回值类型生成不同的CallAdapter和responseConverter。

当返回值是call(T)的时候,adapter通过DefaultCallAdapterFactory类生成adapter

当返回至是Observable的时候,adapter通过RxJava2CallAdapterFactory类生成adapter。

调用adapter的adapt方法(多态体现),

内部调用okhttpCall同步或者异步请求方法,okhttpCall拿到响应通过responseConverter解析。

把解析完成的结果返回给adapter类中最后封装成不同平台的返回值返回出去。

注意:由okhttp3源码可知,调用同步没有线程池的复用优化。再由源码可知,当返回值是call的时候,异步调用通过handler切换到主线程,并且有线程池优化。当同步调用,没有切换线程这一说,也没有线程优化。当返回值是Observable的时候,isAsync为true的时候,有线程池优化,但是默认事件生成在子线程中。当isAsync为false的时候,没有线程池优化,事件默认在主线程生成。isAsync默认值是false。

十广播

广播注册基本流程通过contextImpl把广播接受者注入到AMS中,广播的发送基本流程也是通过contextImpl把广播发送到AMS中,各种条件判断,最后把Intent传递到注册的app中去,类似事件总线

注意:广播不是上下文对象

十一 HashMap

HashMap底层是一个数组。按照正常储存思路,创建一个Entry对象,里边定义属性 K key和V value。数组储存创建的对象,每次get都需要遍历数组,比较key获取value返回,但是有个问题,随着数据量的增大,每次访问数据效率就会慢慢变慢。如何在数据量增大的情况下,提高操作效率成为设计的重点。

官方思路:底层还用一个数组,同时也创建一个Entry对象,里边定义四个属性分别是  K key和V value,int hash , Enety next 。数组储存创建的对象。对象在数组的位置是通过计算获取的(key的hash & 数组的长度-1),这个时候就会面临一个问题,不同key通过计算的位置可能一样,这就是所谓的Hash冲突。上边定义的next就发挥了作用,当有冲突的时候,先去比较key的值是否相同,如果相同就覆盖,如果不相同,那么就给这个对象的next赋值。——数组+链表结构

这个时候你再去获取值,首先通过hash去定位在数组的位置的值,然后通过循环数组当前位置的链表,比较key,即可快速的获取到value。JDK1.8时候又做了优化,链表长度>8就改成红黑树。

数据这样维护确实提高了查找和操作的效率,但是却无法保证数据插入的顺序性,如何保证效率的同时维护顺序呢?LinkedHashMap继承HashMap, 在创建的Entry对象中添加两个属性 Entry  before 和 Entry  after。这 before 变量在每次添加元素的时候将会链接上一次添加的元素,而上一次添加的元素的 after 变量将指向该次添加的元素,互相持有引用来形成数据链。以此来保证效率的同时记录顺序。

LruCache 内部维护这一个 LinkedHashMap,通过比较是否操作设置的容量来删除,最近最少访问的缓存数据。

LruCache DiskLruCache

内容提供者

十三RecyclerView

十四dialog

过度重绘怎么解决,适配怎么解决