Retrofit
对网络请求接口的封装,定义网络请求方法的接口,及添加方法的注解和参数。内部经过动态代理拦截须要处理的接口,并把注解和方法参数解析成须要的http api请求,给OkHttp库进行实际的网络请求。
A、Retrofit的设计模式
1、构建者模式
一、将复杂对象的构建与表示相分离
不关心成员对象的建立,直接调用Builder()内部类经过链式调用内部不一样方法,知足成员的初始化操做。
Retrofit的构建者模式:
建立Retrofit 对象使用到了build构建者模式,利用构建者模式的Build()内部类进行Retrofit成员变量的初始化,最后经过build()完成建立。
优势:
简便易操做,若不适用构建者模式就须要建立Retrofit构造方法来初始化操做,不一样的场景须要构造不一样的Retrofit对象。
二、针对不一样的网络场景须要配置不一样的环境参数,这时候就须要用到了addCallAdapterFactory 网络适配器,解析的效果是不一样的。
ps:ServiceMethod 等对象
2、工厂模式
addCallAdapterFactory(xx.create) :添加返回数据所要支持的格式。
Factory abstract内部类,三大函数方法get() 、getRawType()、getParameterUpperBound()
abstract class Factory {
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
//让不一样类型的(可自定义)工厂继承CallAdapter,经过实现不一样的get()方法,返回不一样类型的Calladatper使用
public abstract @Nullable CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
Retrofit retrofit);
在get()方法中有三个实现类,
RxJavaCallAdapter、
DefaultCallAdapter、
ExecutorCallAdapter
A、RxJavaCallAdapter:
CallAdapter<Observable<?>> callAdapter = getCallAdapter(returnType, scheduler);
在RxJavaCallAdapter里面返回的类型,传递的类型是范型的Observable
private CallAdapter<Observable<?>> getCallAdapter(Type returnType, Scheduler scheduler) {
B、DefaultCallAdapter:返回的是
CallAdapter<Object, Call<?>>(){}通配符
ps:如何添加一个新的CallAdapter呢?
答:extends继承 这个Factory类,并覆盖get方法:就能添加新的CallAdapter使用
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit){}
静态工厂模式:简单工厂模式
实例:Platform类
含有一个静态域:private static final Platform PLATFORM = findPlatform();
//经过findPlatform()返回给客户端调用,findPlatform就是一个静态工厂方法
private static Platform findPlatform(){
try{
//根据Class.forName的返回值来判断所须要不一样的平台,默认的是android平台
Class.forName(“android.os.Build”);
}
...
}
静态工厂与动态工厂的区别
3、外观模式
外部与子系统的通讯方式是必须经过一个统一的外观对象(门面)进行,为这个子系统的接口提供一个一致的界面
特色:高内聚 低耦合 对外提供一个统一的接口
在客户端 和子系统间 定义一个高级接口,桥接二者
客户端只须要跟中间类交互就能够,中间类把具体操做和子系统进行内部交互,再把结果交还给客户端
在Retrofit中的使用:
Retrofit的初始化操做就使用了外观模式,Retrofit自身就至关于外观类,内部封装所须要的7大成员变量的初始化操做
4、策略模式
根据环境和条件的不一样选择不一样的方法策略,完成须要的任务。
Context:上下文角色;
维护一个策略角色的引用;定义一个接口ContextInterface()。来让Strategy来访问Context的数据
使用Strategy的接口来调用某个定义的具体的Strategy策略算法
Strategy:策略角色;
不一样的具体策略须要实现的方法在该方法中定义
ConcreteStrategy xx :具体的策略
Retrofit 中的使用:
addCallAdapterFactory(xx.create) 设置平台适配模式
interface CallAdapter<T> 接口就是提到的抽象的Strategy
具体的策略实现:CallAdapter的实现类完成
在addCallAdapterFactory中()里会生成一个具体的adapter工厂,实际上就是一种具体的策略
Context:在Retrofit当中配置的类就是对应的Context
与工厂模式的区别:
5、适配器模式
将一个接口转换成客户端但愿的另一个接口,使与这个接口本不兼容的类能够在一块儿协同工做
Android当中会将封装的OkHttpCall和主线程executor两个实例对象适配成一个新的实例对象,这样这个call就具有了UI线程切换的功能
@Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
final Type responseType = Utils.getCallResponseType(returnType);
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return call;
}
};
}
因为CallAdapter支持多种平台,因此不一样的平台须要适配不一样的Calladapter
经过适配器模式:
将平台特性和该OkHttpCall适配成可使用的Call的实例
6、观察者模式-一对多
一个对象和多个对象之间的依赖关系,当一个被观察者对象发生变化的时候会自动通知其余观察他的对象做出相应的改变,这些观察者们之间能够没有相互联系,增长代码的可扩展性。
Retrofit中:
一、建立Retrofit实例
二、定义一个含有注解的接口方法
三、经过调用Retrofit的Create方法完成网络请求接口实例的建立
四、经过动态代理将定义的接口以及接口里的注解、方法和参数转换成Retrofit中OkHttpCall对象call再进行网络请求
Create()方法就是使用了动态代理的所在之处:
public <T> T create(final Class<T> service) {
...
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
//在invoke方法里实现java的反射机制的实现
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
...
}
全部的网络请求相关的操做确定支持异步请求,而异步请求内部实现经过维护一个内部的队列进行
Callcall = interface.getCall();//调用接口中的方法完成OkHttpCall的建立
这个Call接口就是被观察者,里面有相应的实现方法。
call.enqueue(new retrofit2.Callback(){...});
这里的Callback就是观察者,根据call做出反应的变化
B、Retrofit的题
(1)Retrofit中如何完成线程切换(子线程<->主线程)?
解析:
在构建Retrofit对象的时候使用Builder()内部类构建并经过.build()完成Retrofit的建立,在该方法中:(1)最早判断baseUrl是否存在,不存在抛异常;(2)其次会判断是否callFactor对象为空,若为空Retrofit本身则建立一个新的OkHttpClient;(3)第三步会建立一个Executor线程池,这个callbackExecutor(内部经过handle机制完成)就是Retrofit进行主线程到子线程切换的callback;(4)第四步会建立一个AdapterFactory用于产生callAdapter适配器对象,请求封装类Call的适配器,负责将call请求转换成另外一个能够被当前平台识别的对象。该对象会被添加到platform(在.Builder()内部建立的该对象)的默认适配器defaultCallAdapterFactory工厂;(5)第五步添加一个ConverterFactory数据转换器工厂,将http请求返回的数据解析成java所能用的对象;最后将前面5个对象返回给Retrofit对象构造函数中完成Retrofit对象的建立。
其中第4步中的platform用于适配平台,自己是一个静态platform对象,经过findPlatForm()初始化实例。默认状况下调用defaultCallbackExecutor()函数中返回了一个Android.MainThreadExecutor(),来进行主线程切换操做。在该方法中有一个主线程的handler(传入Looper.getMainLooper()一个主线程Looper)
在call.enqueue()中Callback回调函数会调用ExecutorCallbackCall.this.callbackExecutor.execute(…)。
callbackExecutor在默认状况下就是使用的MainThreadExecutor,因此最后会回调到主线程中。
答:
static class mainThreadExecutor implements Executor{
//构建持有主线程Looper的Handler
private final Handler handler = new Handler(Looper.getMainLooper());
MainThreadExecutor(){}
public void execute(Runnable r){
this.handler.post(r);
}
}
Retrofit 运用android基本的异步机制;经过构建主线程的Looper,经过Looper.getMainLooper()建立handler,因为handler构造函数中传入的是主线程的Looper,因此能实现经过handler.post(runnable)将消息被主线程Looper获取,将整个的运行在主线程中
(2)Retrofit和RxJava结合 完成基本网络请求
a、不使用RxJava的Retrofit网络请求-生成call对象
一、建立一个用于描述网络请求的接口(含有注解及方法参数)
ps:每个接口当中的方法的参数也必须用注解进行标注 *用于@GET方法的@Query(),用于查询参数,不然出错*
二、利用构建者模式 建立Retrofit对象
三、调用Retrofit对象的Create()方法建立自定义网络请求接口实例
四、经过接口对象调用接口方法建立Call实例,用于真实的请求实例
五、经过call对象创建同步/异步请求
b、使用RxJava的网络请求-生成observable对象
一、在Retrofit初始化的时候添加RxJavaCallAdapterFactory适配器工厂,Retrofit和RxJava进行关联-区别与普通Retrofit对象的构建
二、定义新的接口,方法返回值是Observable 被观察者 -与普通Retrofit对象流程的差别化之一
三、经过新的接口方法 获取Observable 对象实例
四、调用被观察者.subscribeOn()函数:来指定io线程处理数据
五、调用.observeOn(AndroidSchedulers.mainThread()) :在主线程中显示数据
ps:RxJava线程操做符切换线程,由于不能在主线中耗时操做,不在工做线程显示Ui,因此工做结束后要通知主线程更新Ui
六、在.subscribe(onCompleted() / onError() )中处理返回结果
(3)Hook与动态代理
Hook:
经过某种手段对一件事物进行改头换面,从而劫持Hook的目标来达到控制目标的行为的目的。
在不改变原代码的状况下替换原有的对象、方法、函数从而改变原有行为的目的
DroidPlugin 历史:
Hook不少android系统底层代码,不安全!如今已被废弃
Hook的两种实现机制:
<1>、反射
把须要替换的目标类替换成需求类,注意:该类须要有和目标类有同样的特征。
hook理念: 实现目标类含有的接口,或是继承目标类的实现类,拦截其内部方法并修改
一、经过反射机制获取目标类的私有成员变量
Field carEngineFiled = 目标类PrivatePlane.class.getDeclaredField(“目标类的引用");
二、经过setAccessible 设为true 表示能够访问私有方法和它的成员变量
carEngineFiled.setAccessible(true);
三、改变原有成员变量 privatePlane—》ReflectPrivatePlane
PrivatePlaneEngine privatePlaneEngine = (PrivatePlaneEngine)carEngineFiled.get(privatePlaneEngine);
carEngineFiled.set(privatePlane,new ReflectPrivatePlane新修改类(privatePlaneEngine) );
<2>、动态代理——JDK形式
一、 建立ProxyHandler 代理类并实现 InvocationHandler 接口
Public class PrivatePlaneProxyHandler implements InvocationHandler {
private Object object;
public PrivatePlaneProxyHandler(Object object){
this.object = object;
}
//在invoke()方法中添加须要的逻辑,修改返回值
@Override
public Object invoke(Object proxy,Method method,Object[] args){
if(“xxx” .equals(method.getName())){
return …;
}
return method.invoke(object, args):
}
}
二、代理测试类
Filed carEngineFiled = PrivatePlane.class.getDeclared(“privatePlaneEngine");
carEngineFiled.setAccessible(true);
PrivatePlane carEngine = (PrivatePlane) carEngineField.get(privatePlane);
//调用Proxy,newProxyInstance,传入目标实现了字节码,interface接口字节码,及代理类Handler对象
PrivatePlaneEngineInterface
carEngineProxy = (PrivatePlaneEngineInterface) Proxy,newProxyInstance(
privatePlaneEngine.class.getClassLoader(),
new Class[] {PrivatePlaneEngineInterface.class},
new PrivatePlaneProxyHandler(carEngine) );
carEngineField .set(privatePlane , carEngineProxy);
(4)MVP MVC模式
MVC - Model View Controller
数据模型显示和业务逻辑相分离的操做,在改变Ui界面的时候不须要从新的修改业务逻辑
M:处理数据和业务逻辑
V:更新UI显示
C:控制V层与M层通讯,分离更新UI和更新业务逻辑的操做
Android中的MVC:
V:XML文件描述
C:Activity or Fragment 的操做(不进行耗时操做),将数据层的改变交给Ui层
M:JavaBean 实体类
缺陷:
一、全部逻辑都在A or F层进行,形成A/F 层代码的臃肿, 对代码扩展维护不利
二、XML功能太弱,逻辑的简单不能构成复杂的内容代码
三、容许M数据层和V层进行通讯和数据交互
MVP
V:更新UI显示
P:逻辑处理,提供V层和M层之间交互的操做
M:提供数据
Android中的MVP:
V:实现V层的接口,绘制Ui界面,与用户的交互操做,Activity or Fragment 极简操做,进行负责与P层关联(持有P层引用)
P:实现P层的接口,V层和M层交互的核心纽带,ps:将A/F层复杂逻辑交予P层处理,经过接口回调的形式返回给V层 ;持有V、M层引用
M:实现M层的接口,数据的下载,存储及与服务器接口数据交互操做
优点:
将UI层和业务逻辑层的交互经过接口进行
缺陷:
一、P层继承了Activity or Fragment的随着业务代码的增长逻辑愈来愈复杂,代码愈来愈臃肿的问题
二、V层与P层的耦合问题,相互持有引用(问题在MVP解析文章中进行详细描述)
三、接口的颗粒度,接口的过多过少都会影响
(5)sp跨进层问题及apply/commit
sharedPreferences跨进城读写 —does not work reliably
Context.MODE_MULTI_PROCESS
数据安全问题
Sp底层使用的是XML文件的形式,Android的读取有本身的缓存策略。在使用的时候会在内存中缓存一份sp的缓存。这样就会致使在多进程操做下,每个进程都会保存一份sp的缓存文件,形成读写的不一致状况的产生。
相对安全的跨进程数据操做:ContentProvider
apply /commit :提交数据
区别:
- apply 是一个void 没有返回值的异步方法;先将修改的数据提交到内存(commitToMemery),(run)异步将真实数据提交到硬盘中(enqueueDiskWrite(mcr,runnable))
ps:提交失败不会收到通知;
在子线程中提交数据到HD中
- commit是一个boolean 类型返回值的同步方法;同步将数据提交保存到内存和硬盘中,多个并发提交commit时候会阻塞,会等待真正执行的commit执行完后才会执行相应的其余commit操做——enqueueDiskWrite(mcr,null)在当前主线程操做
ps:提交失败会有boolean值反馈,不须要返回值反馈的时候推荐使用apply
在当前主线程中同步的阻塞式提交数据
在执行了apply(),再执行commit()方法 ,commit()会被阻塞