【长篇】OkHttp&Retrofit

作者: flynnny | 来源:发表于2021-05-01 21:27 被阅读0次

    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

    相关文章

      网友评论

        本文标题:【长篇】OkHttp&Retrofit

        本文链接:https://www.haomeiwen.com/subject/dbgarltx.html