【长篇】OkHttp&Retrofit

OkHttp&Retrofit大家都在用,原理很懵懂?2021玩转网络框架
本文是okhttp3.14版本

okhttp框架介绍

2.png

同步get请求实现

//这个尽量做成单例,减少创建次数
OkhttpClient okhttpClient;
...
okhttpClient = new OkhttpClient.Builder().build();
...

private  void textGet(){
  Request request = new Request.Builder()
                .url("url")
                .build();
  //同步请求
  new Thread(new Runnable(){
    @Override
    public void run(){
      try{
        Response response = okhttpClient.newCall(rrquest).execute();
        String result = response.body().string();
        runOnUiThread(new Runnable(){
          @Override
          public void run(){
            textView.setText(result);
          }
        });
      }catch(IOException e){
        e.printStackTrace();
      }  
    }
  }).start();
}

异步body表单post实现

private  void textGet(){
  FormBody formBody =new FormBody.Builder()
                .add("test","这是一个测试的参数")
                .build();
  //如果服务端支持RequestBody 则也可以:
  RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),"\"requestBody实体数据\"");
  
  Request request = new Request.Builder()
                .url("url")
                .post(formBody)
              //.post(requestBody )
                .build();
  //异步请求
  okthhpClient.newCall(request).enqueue(new Callback(){
    @Override
    public void onFailure(Call call,IOException e){

    }

    @Override 
    public void onResponse(Call call,Respon respon) throws IOException {
      String result = response.body().string();
      runOnUiThread(new Runnable(){
          @Override
          public void run(){
            textView.setText(result);
          }
       });    
    }

  });
}

文件上传实现

记得加入权限,可以导入第三方权限框架

public void upload(String path){
  File file = new File(path);
  RequestBody body =new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file",file.getName(),MultipartBody.create(MediaType.parse("multipart/form-data"),new File(path)))
                .build();

  Request request = new Request.Builder()
                .url("url")
                .post(body)
                .build();
  //异步请求
  okthhpClient.newCall(request).enqueue(new Callback(){
    @Override
    public void onFailure(Call call,IOException e){

    }

    @Override 
    public void onResponse(Call call,Respon respon) throws IOException {
      String result = response.body().string();
      runOnUiThread(new Runnable(){
          @Override
          public void run(){
            textView.setText(result);
          }
       });    
    }
  });
}

使用自定义拦截器

public class LogIntercept implement Interceptor{
  @Override
  public Response intercept(Chain chain) throws IOException{
    Request request = chain.request();
    //...在这之间插入自己的东西
    Log.i("z","intercept:REQUEST="+request.toString());
    //...
    Response response = chain.proceed(request);
    //response.body.toString()//不能这么写,这个是流,直接消费掉了
    Log.i("z","intercept:REQUEST="+response.toString());
    return response ;
  }
}

okhttpClient = new OkhttpClient().Builder()
                .addInterceptor(new LogIntercept())
                .build();
1.png

进入原理分析

okhttp双任务队列机制

3.png

从newCall()的enqueue()方法进入,查看异步请求流程
具体实现的由RealCall

4.png

拿到dispatcher,然后调用enqueue方法 ,并创建AsyncCall

进入dispatcher的enqueue方法:

5.png

分析AsyncCall是什么

6.png

AsyncCall就是一个Runnable

回到dispatcher
会把AsyncCall 添加到一个双向队列中去

readyAsyncCalls.add(call);

readyAsyncCalls是一个等待中的队列,所有新进的都加进去

promoteAndExecute();
7.png
8.png

查看executorService(),线程池方法:

9.png

无核心线程,临时工无限多
任务进来直接进临时,创建一个线程去立即执行
线程生存周期只有60s存活

回到AsyncCall execute看执行结束干了什么:

client.dispatcher().finished(this);
10.png

亮点所在:
通过结束又来推进当前整个流程;
Volley是靠while循环遍历,while会占用一部分CPU资源
他是靠手动循环的,类似于推进器。
大大减少资源浪费。

11.png

责任链模式与拦截器

12.png
13.png
14.png

前一个对象记住下一个对象的引用而连成一条链

实现拦截器的写法

public abstract class Handler{
  protate Handler next;//下一个的对象
  public Handler getNext(){
    return next;
  }
  public void setNext(Handler next){
    this.next = next;
  }
  
  public abstrast void hangleRequest(String request);
}

具体拦截处理者

public class MainHandler1 extends Handler{
  @Override
  public void handleRequest(String request){
    //自己的逻辑
    if(request.equals("one")){
      Log.d("z","具体处理者1处理该请求");
    }else{
      if(getNext()!= null){
        next.handleRequest(request);
      }else{
        Log.d("z","没有人处理该请求");
      }
    }
  }
}

public class MainHandler2 extends Handler{
  @Override
  public void handleRequest(String request){
    //自己的逻辑
    if(request.equals("two")){
      Log.d("z","具体处理者2处理该请求");
    }else{
      if(getNext()!= null){
        next.handleRequest(request);
      }else{
        Log.d("z","没有人处理该请求");
      }
    }
  }
}

public class MainHandler3 extends Handler{
  @Override
  public void handleRequest(String request){
    //自己的逻辑
    if(request.equals("three")){
      Log.d("z","具体处理者3处理该请求");
    }else{
      if(getNext()!= null){
        next.handleRequest(request);
      }else{
        Log.d("z","没有人处理该请求");
      }
    }
  }
}
Handler  hangler1 = new MainHandler1();
Handler  hangler2 = new MainHandler2();
Handler  hangler3 = new MainHandler3();

hangler1.setNext(handler2);
hangler2.setNext(handler3);
hangler1.handleRequest("one");//具体处理者1处理该请求
hangler1.handleRequest("three");//具体处理者3处理该请求

TCP三次握手与四次挥手

15.png

服务端收到断开时可能有数据没发送完,发送完服务端在发送断开连接。

Socket连接池复用机制

okhttp考虑到三次握手、四次挥手的消耗问题,利用KeepAlive机制,做了优化

16.png

默认保持5个存活,5分钟

okhttp小结

双任务队列、
责任链(自定义的在最上面,没往下传就断了)

Retrofit框架介绍

17.png

RESTful API

18.png
19.png

幂等安全:多次操作结果一样

20.png

创建请求接口和实例

初始工作:

21.png
24.png
22.png
23.png
public interface ApiService{
  //定义请求方法
  @GET("fc/test")
  Call<String > test(@Querty("test") String str); //是retrufit里的call
}

MainActivity

Retrofit retrofit;
OkHttpClient client;
...
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger(){
   @Override
    public void log(String message){
      Log.i("z",message);
    }   
 }).setLevel(HttpLoggingInterceptor.Level.BODY);

client = new OkHttpClient.Builder()
          .addInterceptor(interceptor)//日志拦截
          .build();

retrofit = new Retrofit.Build()
          .baseUrl("urlllllll")
          .client(client)
          .addConverterFactory(ScalarsConverterFactory.create())
          .build();

ApiService apiService =retrofit.create(ApiService.class);

同步GET-POST请求实现

new Thread(new Runnable(){
  @Override
  public void run(){
    try{
      Response<String> response = apiService.test("这是一个参数").execute();

      Log.d("z","response"+response.body());
    }catch(IOException e){
      e.printStaceTrace();
    }
  }
}).start();
25.png

异步Post请求实现

apiService.test("这是一个参数").enqueue(new Callback<String>(){
  @Override
  public void onResponse(Call<String> call,Response<String> response){
    Log.d("z","response = "+response.body());
  }
  @Override
  public void onFailure(Call<String> call,Throwable t){
    
  }
});

结果和上面是一样的

post请求该怎么写呢?

public interface ApiService{
  //定义请求方法
  @GET("fc/test")//GET改为POST即可
  Call<String > test(@Querty("test") String str); //是retrufit里的call
  ....修改为....
  @POST("fc/test")
  @FormUrlEncoded
  Call<String > test(@Field("test") String str);
  //加上FormUrlEncoded,Querty改为Field,会把内容放到表单里
  @POST("fc/test")
  @FormUrlEncoded
  Call<String > test(@FieldMap Map<String,String> map);
  //这样就放在一个map里<K,V>

  //定义一个方法提交一个body
  @POST("fc/test2")//不能是get
  Call<String> uploadBody(@Body User user);
  
}
26.png

传一个body

public class User{
  private String username;
  public User(String username){
    this.username = username;
  }
  get()..
  set()...
}
 apiService.uploadBody(new User("zz")).enqueue(new Callback<String>(){
  @Override
  public void onResponse(Call<String> call,Response<String> response){
    Log.d("z","response = "+response.body());
  }
  @Override
  public void onFailure(Call<String> call,Throwable t){
    
  }
});

上面的ConverterFactory改为Gson

retrofit = new Retrofit.Build()
          .baseUrl("urlllllll")
          .client(client)
          .addConverterFactory(GsonConverterFactory.create())
          .build();

27.png

GsonConverterFactory解析实体类

定义一个数据的具体类型

public class ResponseData{
  private String code;
  private String msg;
  private T data;

  getxxx()...
  setxxx()...
}

改变接收数据类型:

public interface ApiService{
  ...
  //定义一个方法提交一个body
  @POST("fc/test2")
  Call<ResponseData<String>> uploadBody(@Body User user);
  //String改为ResponseData<String>
}

使用时修改:

 apiService.uploadBody(new User("zz")).enqueue(new Callback<ResponseData<String>>(){
  @Override
  public void onResponse(Call<ResponseData<String>> call,Response<ResponseData<String>> response){
    Log.d("z","response = "+response.body().getData());
  }
  @Override
  public void onFailure(Call<ResponseData<String>> call,Throwable t){
    
  }
});
28.png

可以指定data类型了,具体还要跟服务端沟通。

使用Retrofit上传文件

//multipart/form-data

public interface ApiService{
  ...
  //multipart/form-data
  @Multipart
  @Post("fc/upload")
  Call<ResponseData<String>> uploadFile(@Part MultipartBody.Part part);
}

MainActivity:

public void shangchuan(String path){
  File file = new File(path);
  RequestBody requestBody = RequestBody.create(MediaType.parse("image/jp"),file);
  MultipartBody.Part part = MultipartBody.Part.createFromData("file",file.getName(),requestBody);

  apiService.uploadFile(part).enqueue(new Callback<String>(){
  @Override
  public void onResponse(Call<String> call,Response<String> response){
    Log.d("z","response = "+response.body().getData());
  }
  @Override
  public void onFailure(Call<String> call,Throwable t){
    
  }
});
  
}

别忘了配置provider..

29.png

可以创建一个FileInfo类,把上面Response<String>改为Response<FileInfo>

网络模块用Hilt注入

30.png

Hilt---依赖注入--》灵活
RxJava---线程切换

31.png
32.png
33.png
34.png

RxJava组合Retrofit使用

强大的为异步而生的框架

35.png
36.png

别忘了NetWorkModule加上

37.png

网络模块完整搭建

封装异常处理

38.png
39.png
40.png
41.png
42.png

最终

43.png

Retrofit原理分析

Retrofit代理模式

从retrofit.create()进入

44.png
45.png

静态代理及实现

代理持有被代理的引用

被代理类

public class Subject{
  public void test(){
    System.out.println("被代理对象执行test...");
  }
}

代理

public class Proxy{
  private Subject subject;
  public Proxy(Subject subject){
    this.subject=subject;
  }

  public void test(){
    System.out.println("代理对象执行一些不方便的事...");
    subject.test();
    System.out.println("执行结束...");
  }
}

逻辑增强了

46.png

如果后很多代理对象,就要有很多代理类。用动态代理解决。

动态代理优势及实现

动态生成的是类的class数据(字节码数据)并加载到jvm中了。

JDK提供了方法
代理的只能是接口
先定义一个接口

public interface UserInterface{
  void test();
}
public class UserInterfaceImpl implement UserInterface{
  @Override
  public void test(){
    System.out.println("test.....");
  }
}
public class LogHandler implement InvocationHandler{
  UserInterfaceImpl impl= new UserInterfaceImpl();

  public LogHandler(UserInterfaceImpl impl){
    this.impl = impl
  }

  @Override
  public Object invoke(Object proxy,Method method,Object[] args){
    //method就是接口中被代理的方法
    if(method.getName().equals("test")){
      System.out.println("被代理对象执行test之前.....");
      Object obj =  method.invoke(impl,args);
      System.out.println("被代理对象执行test之后.....");
      return obj ;
    }
     return method.invoke(impl,args);//原来的别忘了
    }
}
public class JDKProxyTest{
  public static void main(String[] args){
    //创建代理对象
    UserInterfaceImpl Impl= new UserInterfaceImpl();
    //获取classLoader
    ClassLoader classLoader = impl.getClass().getClassLoader();
    //获取接口
    Class<?>[] interfaces = impl.getClass().getInterfaces();
    //创建InvercationHandler
    InvercationHandler handler = new LogHandler(impl);
    
    UserInterface proxy = (UserInterface)Proxy.newProxyInstance(classLoader,interface,handler);
    proxy.test();
}

不需要增加新的静态代理类了!!!

JDK动态代理原理

对接口进行处理,拿到相关信息组装成class,加载到内存中,生成字节码对象

ServiceMethod详解

47.png

Retrofit小结

Retrofit 搭配Rxjava 、hilt

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容