Android应用开发岗 面试汇总-第三方库

2022-04-08  本文已影响0人  陈祥_

背景

最近在准备面试,结合之前的工作经验和近期在网上收集的一些面试资料,准备将Android开发岗位的知识点做一个系统的梳理,整理成一个系列:Android应用开发岗 面试汇总。本系列将分为以下几个大模块:
Java基础篇Java进阶篇常见设计模式
Android基础篇Android进阶篇性能优化
网络相关数据结构与算法
常用开源库、Kotlin、Jetpack

注1:以上文章将陆续更新,直到我找到满意的工作为止,有跳转链接的表示已发表的文章。
注2:该系列属于个人的总结和网上东拼西凑的结果,每个知识点的内容并不一定完整,有不正确的地方欢迎批评指正。
注3:部分摘抄较多的段落或有注明出处。如有侵权,请联系本人进行删除。

在日常开发中,我们能够熟练的使用各种第三方库,且第三方库的源码和对源码的分析,网上已经有很多。这里不做过多分析,本篇内容主要概述第三方库的实现流程和原理,旨在通过概述的方式,让读者复习第三方库,以便在面试过程中有更好的表现。

1 OkHttp

1.1 Okhttp 基本实现原理

OkHttp 主要是通过 5 个拦截器和 3 个双端队列(2 个异步队列,1 个同步队列)工作。内部实现通过一个责任链模式完成,将网络请求的各个阶段封装到各个链条中,实现了各层的解耦。

OkHttp 的底层是通过 Socket 发送 HTTP 请求与接受响应,但是 OkHttp 实现了连接池的概念,即对于同一主机的多个请求,可以公用一个 Socket 连接,而不是每次发送完 HTTP 请求就关闭底层的 Socket,这样就实现了连接池的概念。而 OkHttp 对 Socket 的读写操作使用的 OkIo 库进行了一层封装。

执行流程:

内部的大致请求流程图如下所示:


b173c4ed9ef3c5e7ddc88a2054bd1a13.jpg

1.2 Okhttp 网络缓存如何实现?

OKHttp 默认只支持 get 请求的缓存。

1.3 Okhttp 网络连接怎么实现复用?

HttpEngine 在发起请求之前,会先调用nextConnection()来获取一个Connection对象,如果可以从ConnectionPool中获取一个Connection对象,就不会新建,如果无法获取,就会调用createnextConnection()来新建一个Connection对象,这就是 Okhttp 多路复用的核心,不像之前的网络框架,无论有没有,都会新建Connection对象。


20190226140300974.png

1.4 Dispatcher 的功能是什么?

Dispatcher中文是分发器的意思,和拦截器不同的是分发器不做事件处理,只做事件流向。他负责将每一次Requst进行分发,压栈到自己的线程池,并通过调用者自己不同的方式进行异步和同步处理。 通俗的讲就是主要维护任务队列的作用。

Dispatcher 类,该类中维护了三个双端队列(Deque):

OkHttp 设置了默认的最大并发请求量 maxRequests = 64 和单个 Host 主机支持的最大并发量 maxRequestsPerHost = 5

1.5 addInterceptor 与 addNetworkInterceptor 的区别?

二者通常的叫法为应用拦截器和网络拦截器,从整个责任链路来看,应用拦截器是最先执行的拦截器,也就是用户自己设置request属性后的原始请求,而网络拦截器位于ConnectInterceptor和CallServerInterceptor之间,此时网络链路已经准备好,只等待发送请求数据。

1.首先,应用拦截器在RetryAndFollowUpInterceptor和CacheInterceptor之前,所以一旦发生错误重试或者网络重定向,网络拦截器可能执行多次,因为相当于进行了二次请求,但是应用拦截器永远只会触发一次。另外如果在CacheInterceptor中命中了缓存就不需要走网络请求了,因此会存在短路网络拦截器的情况。
2.其次,如上文提到除了CallServerInterceptor,每个拦截器都应该至少调用一次realChain.proceed方法。实际上在应用拦截器这层可以多次调用proceed方法(本地异常重试)或者不调用proceed方法(中断),但是网络拦截器这层连接已经准备好,可且仅可调用一次proceed方法。
3.最后,从使用场景看,应用拦截器因为只会调用一次,通常用于统计客户端的网络请求发起情况;而网络拦截器一次调用代表了一定会发起一次网络通信,因此通常可用于统计网络链路上传输的数据。

1.6 Okhttp 拦截器的作用是什么?

1、应用拦截器
拿到的是原始请求,可以添加一些自定义header、通用参数、参数加密、网关接入等等。

2、网络拦截器
用户自定义拦截器,通常用于监控网络层的数据传输。

1.7 Okhttp 有哪些优势?

1.8 Okhttp 运用了哪些设计模式?

Okhttp 运用了六种设计模式:

链接

2 Glide

2.1 基本使用

通过Glide类进行一个链式调用
Glide.with(getApplicationContext()).load(imageurl).into(imageview);
with()

在使用过程中尽量要传入Applicaiton、Activity 、Fragment等类型的参数,因为glide加载图片的请求会与该参数的生命周期绑定在一起,如果onPaush时候,Glide就会暂停加载,重新onResume之后,又会继续加载。

load()

支持网络图片网址、二进制流、drawable资源、本地图片的传入。

crossFade

这是开启显示淡入淡出的动画

override

如果获取的网络图片过大,我们通过它进行一个大小的裁剪,传入width和height参数进行宽高裁剪。

diskCacheStrategy

磁盘缓存的设置,默认Glide会开启的。
DiskCacheStrategy.NONE 什么都不缓存
DiskCacheStrategy.SOURCE 只缓存全尺寸图
DiskCacheStrategy.RESULT 只缓存最终的加载图
DiskCacheStrategy.ALL 缓存所有版本图(默认行为)

Glide 不仅缓存了全尺寸的图,还会根据 ImageView 大小所生成的图也会缓存起来。比如,请求一个 800x600 的图加载到一个 400x300 的 ImageView 中,Glide默认会将这原图还有加载到 ImageView 中的 400x300 的图也会缓存起来。

error

这里的设置是当加载图片出现错误时,显示的图片。

placeholder

图片加载完成之前显示的占位图。

into()

一般传 ImageView 。

2.2 Glide原理

with是Glide类的一个静态方法,重载方法很多可以接收 Activity,Fragment,Context。

with方法里面,首先会调用RequestManagerRetriever的静态get方法得到RequestManagerRetriver对象。然后再调用该对象的get方法获取RequestManager对象。静态get方法中也有很多重载方法,主要分为传入Application参数和非Application参数,传入Application参数是最简单的情况,Glide只要持保和整个应用生命周期同步。

非Application参数不管是Activity,Fragment,最终都会向当前Activity传入一个隐藏的Fragment,因为Glide需要监控Activity的生命周期,Fragment依赖Activity生命周期并且是同步的,通过这个隐藏的Fragment就监听到Activity生命周期。

load方法,with方法返回的是一个RequestManager对象,所以load方法在RequestManager类中,load方法也有很多重载,支持本地图片,内存图片,网络图片,只看加载url的load方法。首先调用了fromString方法,再调用load方法,传入图片url,fromString方法里调用了loadGeneric方法,这个方法创建并返回了DrawableTypeRequest对象。

DrawableTypeRequest并没有load方法,load在DrawableTypeRequest的父类DrawableTypeRequestBuildle中。大部分操作都在这个类中,比如placeholder占位符,error,discacheStrategy等。

into方法是Glide图片加载流程中逻辑最为复杂的方法。

into方法在DrawableTypeRequestBuilder类中,里面调用了super.into方法,真正的实现在DrawableTypeRequestBuilder的父类GenericRequestBuilder中,这个类包括了网络请求,图片解析,图片解码,bitmap生成,缓存处理,图片压缩等大量逻辑操作,最后的最后才将图片展示出来。

总结:
Glide在加载绑定了Activity的生命周期。

2.3 Glide三级缓存

2.3.1 普通的三级缓存

2.3.2 Glide的三级缓存

2.3.3 缓存读取

Glide 获取一张图片时,首先会从弱引用缓存中获取,没有则从内存缓存 LruCache 中获取,再没有则从磁盘缓存中获取,再没有才通过网络获取。拿到图片后,通过 Handler 发送消息给主线程。将图片展示并缓存起来。

2.3.4

DiskLruCache 缓存原图 -> 弱引用缓存 -> LruCache -> DiskLruCache 缓存编码后的图片
注:弱引用缓存和 LruCache 之间存在缓存的转换关系,图片从正在使用状态转为不使用状态,Glide 将图片从弱引用缓存移除然后缓存到 LruCache 中,假如 LruCache 中的某张图片现在需要使用,则图片从 LruCache 中移除缓存到弱引用缓存中,弱引用缓存中保存的是正在使用的图片。

链接
问:为什么Glide磁盘缓存效率高?
答:磁盘缓存对图片文件进行了加密和压缩处理

意义:
1、三级缓存策略,最实在的意义就是减少不必要的流量消耗,增加加载速度。
2、从开发角度来说,Bitmap 的创建非常消耗时间和内存,可能导致频繁GC。而使用缓存策略,会更加高效地加载 Bitmap,减少卡顿,从而减少读取时间。

2.4 Glide优点

3 EventBus

EventBus是一个基于观察者模式的事件订阅/发布框架,利用 EventBus 可以在不同模块之间,实现低耦合的消息通信。

3.1 使用

使用 EventBus 注册消息的时候,可以通过 @Subscribe 注解来完成注册事件, @Subscribe 中可以通过参数 threadMode 来指定使用那个线程来接收消息。

@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventTest(event:TestEvent){
  // 处理事件
}

3.2 参数

threadMode 是一个 enum,有多种模式可供选择:

3.3 线程切换源码

EventBus 的线程切换,主要涉及的方法就是 EventBus 的 postToSubscription() 方法。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
  switch (subscription.subscriberMethod.threadMode) {
    case POSTING:
      invokeSubscriber(subscription, event);
      break;
    case MAIN:
      if (isMainThread) {
        invokeSubscriber(subscription, event);
      } else {
        mainThreadPoster.enqueue(subscription, event);
      }
      break;
    case MAIN_ORDERED:
      if (mainThreadPoster != null) {
        mainThreadPoster.enqueue(subscription, event);
      } else {
        // temporary: technically not correct as poster not decoupled from subscriber
        invokeSubscriber(subscription, event);
      }
      break;
    case BACKGROUND:
      if (isMainThread) {
        backgroundPoster.enqueue(subscription, event);
      } else {
        invokeSubscriber(subscription, event);
      }
      break;
    case ASYNC:
      asyncPoster.enqueue(subscription, event);
      break;
    default:
      throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
  }
}

从上面的代码可以看出,切换线程是通过MainThreadPoster、BackgroundPoster。BackgroundPoster通过线程池去执行事件。

3.4 总结

链接

4 LeakCanary

利用弱引用特性,检测Activity 的内存泄漏。核心步骤如下:

一句话总结:LeakCanary.install(application)在Application中通过registerActivityLifecycleCallbacks进行注册监听。当Activity onDestroy时,通过一个弱引用指向该Activity,把弱引用放入引用队列中。当发生GC后,判断队列中的是否存在该弱引用,如果存在,则说明发生了泄露,则去分析泄露原因,最后通知应用发生了泄露。

链接

5 ButterKnife

ButterKnife又名黄油刀,是一款知名的Andorid框架,通过注解绑定,省去初始化控件等重复工作,简化代码,极大提高工作效率。使用如下:

@BindView(R.id.tv)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }
@OnClick(R.id.tv)
public void click(View view) {
        Toast.makeText(this, tv.getText().toString(), Toast.LENGTH_SHORT).show();
    }

ButterKnife为什么执行效率为什么比其他注入框架高?
答:由于是在编译期生成的代码,并不是通过反射实现,所以性能优势是非常高的。

6 Retrofit

Retrofit 通过 Java 接口以及注解来描述网络请求,并用动态代理的方式生成网络请求的 request,然后通过 client 调用相应的网络框架(默认 okhttp)去发起网络请求,并将返回的 response 通过 converterFactorty 转换成相应的数据 model,最后通过 calladapter 转换成其他数据方式(如 rxjava Observable)

6.1 Retrofit 流程

6.2 用到的设计模式和设计原则

设计模式

设计原则

链接

上一篇 下一篇

猜你喜欢

热点阅读