OkHttp简介

OkHttp是一个http协议网络请求的框架,OkHttp是一个高效的HTTP客户端,适用于Android和Java应用程序。从Android 4.4开始google已经开始将源码中的HttpURLConnection替换为OkHttp,而在Android 6.0之后的SDK中google更是移除了对于HttpClient的支持,而现在流行的Retrofit同样是使用OkHttp进行再次封装而来的。

本文章主要讲的:

1.Okhttp3简单使用

2.Okhttp3封装使用

效果图

效果图.gif

 

项目地址:https://github.com/pengjunshan/UseOkhttp3

拿到代码后移到自己项目中根据自己项目需求修改即可使用。

OkHttp3特性

  • 支持http2,使得对同一个主机发出的所有请求都可以共享相同的socket套接字连接;
  • 使用连接池来复用连接以减少延迟、提高效率;
  • 支持Gzip压缩响应体,降低传输内容的大小;
  • 支持Http缓存,避免重复请求;
  • 请求失败时会自动重试主机中的其他IP地址自动重定向;
  • 使用Okio来简化数据的访问与存储,提高性能;
  • 使用了创建者设计模式;

Http简介

HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展。目前在WWW中使用的是HTTP/1.1版本。2.0版本目前也有在使用,只是使用不广泛。HTTP协议工作于客户端-服务端架构为上,我们把Http协议中通信的两方称作Client和Server,Client端向Server端通过http协议发送一个Request请求,Server端收到Client端发来的Request请求后经过一系列的处理返回Client一个Response,过程如下图。

 

Client客户端 - Server服务端

HTTP请求报文格式

  • HTTP请求报文主要由请求行、请求头部、请求正文3部分组成。

  • 请求行:由请求方法,URL,协议版本三部分构成。

    • URL是请求服务器地址。
    • 协议版本有HTTP1.0、HTTP1.1,目前HTTP2.0也有使用。
    • HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
    • HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
    • 请求方法

  • 请求头部为请求报文添加了一些附加信息,由“名/值”对组成,每行一对,名和值之间使用冒号分隔。
    Host ----接受请求的服务器地址,可以是IP:端口号,也可以是域名
    User-Agent ----发送请求的应用程序名称
    Connection ---- 指定与连接相关的属性,如Connection:Keep-Alive
    Accept-Charset ---- 通知服务端可以发送的编码格式
    Accept-Encoding ---- 通知服务端可以发送的数据压缩格式
    Accept-Language ---- 通知服务端可以发送的语言

  • 请求正文,可选部分,GET请求就没有请求正文。

HTTP响应报文格式

  • HTTP响应报文主要由状态码、响应头部、响应正文3部分组成。
    • 常用状态码
      200:响应成功
      302:重定向跳转,跳转地址通过响应头中的Location属性指定
      400:客户端请求有语法错误,参数错误,不能被服务器识别
      403:服务器接收到请求,但是拒绝提供服务(认证失败)
      404:请求资源不存在
      500:服务器内部错误

    • 响应头部,与请求头部类似,为响应报文添加了一些附加信息
      Server - 服务器应用程序软件的名称和版本
      Content-Type - 响应正文的类型(是图片还是二进制字符串)
      Content-Length - 响应正文长度
      Content-Charset - 响应正文使用的编码
      Content-Encoding - 响应正文使用的数据压缩格式
      Content-Language - 响应正文使用的语言

    • 响应正文,是请求响应的最终结果,都在响应体里。可以是字符串可以是字符流。

配置

  • maven方式:
  com.squareup.okhttp3  okhttp  3.12.0

gradle方式:

   compile 'com.squareup.okhttp3:okhttp:3.12.0'

 

Android3.0 以后已经不允许在主线程访问网络。需要注意的是这个onResponse回调方法不是在主线程回调,可以使用runOnUIThread(new Runnable(){})更新UI,或者使用Handler在主线程中更新UI。

GET请求

1.第一步创建OkHttpClient对象
2.如果需要拼接参数 (一般有参数的都会用Post请求,除非参数不重要)
3.第二步创建request对象
4.新建一个Call对象
5.同步请求网络execute()
6.异步请求网络enqueue(Callback)

 

  • 自动下载okio资源

     

    Libraries资源

  • 联网权限
  •   

    1.简单使用

    HTTP工作中常用方式:

  • get请求
  • post请求
  • 文件上传
  • 文件下载
  • 图文混合上传
  • Android3.0 以后已经不允许在主线程访问网络。需要注意的是这个onResponse回调方法不是在主线程回调,可以使用runOnUIThread(new Runnable(){})更新UI,或者使用Handler在主线程中更新UI。

    GET请求

    1.第一步创建OkHttpClient对象
    2.如果需要拼接参数 (一般有参数的都会用Post请求,除非参数不重要)
    3.第二步创建request对象
    4.新建一个Call对象
    5.同步请求网络execute()
    6.异步请求网络enqueue(Callback)

     

          /**     * 获取轮播图接口     * GET请求     */   private void requestBannerApi(){        //1.第一步创建OkHttpClient对象        final OkHttpClient okHttpClient = new OkHttpClient();        String url ="http://www.wanandroid.com/banner/json";        //2. 如果需要拼接参数 (一般有参数的都会用Post请求,除非参数不重要)//        Map params = new HashMap<>();//        params.put("movieid", "246363");//        params.put("limit", "3");//        params.put("offset", "5");//        url = appendParams(url,params);        //3.第二步创建request        Request.Builder builder = new Request.Builder();        final Request request = builder.url(url)                .get()                .build();        //4.新建一个Call对象        final Call call = okHttpClient.newCall(request);        //5.同步请求网络execute()           new Thread(new Runnable() {            @Override            public void run() {                try {                    Response response = call.execute();                    if(response.isSuccessful()){                        Log.e("Benner请求成功同步=",response.body().string());                    }else{                        throw new IOException("Unexpected code " + response);                    }                } catch (IOException e) {                    e.printStackTrace();                }            }        }).start();        //6.异步请求网络enqueue(Callback)        call.enqueue(new Callback() {            @Override            public void onFailure(Call call, IOException e) {                Log.e("TAG", "Benner请求失败="+e.getMessage());            }            @Override            public void onResponse(Call call, Response response) throws IOException {                String json = response.body().string();                Log.e("TAG", "Benner请求成功异步="+json);            }        });    }    /**     * 拼接参数     * @param url     * @param params     * @return     */    private String appendParams(String url, Map params) {        StringBuilder sb = new StringBuilder();        sb.append(url + "?");        if (params != null && !params.isEmpty()) {            for (String key : params.keySet()) {                sb.append(key).append("=").append(params.get(key)).append("&");            }        }        sb = sb.deleteCharAt(sb.length() - 1);        return sb.toString();    }

    注意execute()同步方式会阻塞调用线程,所以在Android中应放在子线程中执行,否则有可能引起ANR异常。一般都会使用enqueue()异步请求服务器。

    POST请求(键值对 key-value)

    1.拿到okhttpClient对象
    2.创建 FormBody 添加需要的键值对
    3.构造Request
    4.创建一个Call对象
    5.异步请求enqueue(Callback)

     

    /**    * 登录接口    * POST请求    * @param account 用户名    * @param pwd 密码    */   private void requestLoginApi(String account, String pwd) {       // 1.拿到okhttpClient对象       OkHttpClient okHttpClient = new OkHttpClient();       //2.创建 FormBody 添加需要的键值对       FormBody formBody = new FormBody.Builder()               .add("username",account)               .add("password",pwd)               .build();           // 3.构造Request       Request.Builder builder = new Request.Builder();       Request request = builder.url("http://www.wanandroid.com/user/login")               .post(formBody)//键值对               .build();       //4.创建一个Call对象       Call call = okHttpClient.newCall(request);       //5.异步请求enqueue(Callback)       call.enqueue(new Callback() {           @Override           public void onFailure(Call call, IOException e) {               Log.e("TAG", "登录失败="+e.getMessage());           }           @Override           public void onResponse(Call call, Response response) throws IOException {               String json = response.body().string();               UserInfo userInfo = new Gson().fromJson(json,UserInfo.class);               if(userInfo!=null) {                   if(userInfo.getErrorCode()!=0) {                       Log.e("TAG", userInfo.getErrorMsg());                   }else {                       Log.e("TAG", "登录成功="+json);                   }               }           }       });   }

    如果Post提交的数据是键值对就构造一个FormBody对象,可以添加N个键值对。

    POST请求(json字符串)

    1.拿到okhttpClient对象
    2.设置提交类型MediaType+json字符串
    3.构造Request
    4.创建一个Call对象
    5.异步请求enqueue(Callback)

     

       private void requestLoginApi(String account, String pwd) {       // 1.拿到okhttpClient对象       OkHttpClient okHttpClient = new OkHttpClient();       //需要提交的json字符串      String jsonStr = "hahaha";      //2.创建 RequestBody 设置提交类型MediaType+json字符串     RequestBody requestBody =  RequestBody.create(MediaType.parse("application/json"),jsonStr);       // 3.构造Request       Request.Builder builder = new Request.Builder();       Request request = builder.url("http://www.wanandroid.com/user/login")               .post(requestBody)//字符串               .build();       //4.创建一个Call对象       Call call = okHttpClient.newCall(request);       //5.异步请求enqueue(Callback)       call.enqueue(new Callback() {           @Override           public void onFailure(Call call, IOException e) {               Log.e("TAG", "登录失败="+e.getMessage());           }           @Override           public void onResponse(Call call, Response response) throws IOException {               String json = response.body().string();               UserInfo userInfo = new Gson().fromJson(json,UserInfo.class);               if(userInfo!=null) {                   if(userInfo.getErrorCode()!=0) {                       Log.e("TAG", userInfo.getErrorMsg());                   }else {                       Log.e("TAG", "登录成功="+json);                   }               }           }       });   }

    如果提交json字符串需要构造一个RequestBody对象,用它来携带我们要提交的json字符串数据。在构造 RequestBody 需要指定MediaType,用于描述请求/响应 body 的内容类型。

    RequstBody的几种构造方式

    POST上传(文件)

    1.创建OkHttpClient对象
    2.获取文件地址,设置上传文件类型,构造RequestBody对象
    3.构造Requst对象
    4.构造Call对象进行 异步请求enqueue(Callback)

     

     /**     * 提交txt文件     * POST请求     */    private void requestPostFileTxt(){        //1.创建OkHttpClient对象        OkHttpClient okHttpClient = new OkHttpClient();        //2.获取文件地址,设置上传文件类型,构造RequestBody对象        File fileAdress = new File("/sdcard/wangshu.txt");        MediaType mediaType = MediaType.parse("text/plain; charset=utf-8");        RequestBody requestBody = RequestBody.create(mediaType,fileAdress);        //3.构造Requst对象        Request request = new Request.Builder()                .url("http://www.baidu.com")                .post(requestBody)                .build();        //4.构造Call对象进行 异步请求enqueue(Callback)        okHttpClient.newCall(request).enqueue(new Callback() {            @Override            public void onFailure(Call call, IOException e) {                Log.e("TAG", "post"+e.getMessage());            }            @Override            public void onResponse(Call call, Response response) throws IOException {                String json = response.body().string();                Log.e("TAG", "Benner请求成功异步="+json);            }        });    }

    上传文件本身也是一个post请求,向服务器发送文件时需要备注文件类型Content-Type,可以用MultipartBody上传多个文件。如果没有添加charset也没关系,RequestBody 中已经帮我们写好了。

    charset


    常用的Content-Type:
    text/plain :纯文本格式 .txt
    text/xml : XML格式 .xml
    image/gif :gif图片格式 .gif
    image/jpeg :jpg图片格式 .jpg
    image/png:png图片格式 .png
    audio/mp3 : 音频mp3格式 .mp3
    audio/rn-mpeg :音频mpga格式 .mpga
    video/mpeg4 : 视频mp4格式 .mp4
    video/x-mpg : 视频mpa格式 .mpg
    video/x-mpeg :视频mpeg格式 .mpeg
    video/mpg : 视频mpg格式 .mpg
    以application开头的媒体格式类型:
    application/xhtml+xml :XHTML格式
    application/xml : XML数据格式
    application/atom+xml :Atom XML聚合格式
    application/json : JSON数据格式
    application/pdf :pdf格式
    application/msword : Word文档格式
    application/octet-stream : 二进制流数据(如常见的文件下载)

     

    POST上传图片

    1.创建OkHttpClient对象
    2.设置文件类型
    3.构造RequestBody 指定文件类型和文件
    4.创建Request对象
    5.异步请求newCall(Callback)

     

        /**     * 上传图片     * @param file     */    private void requestPostImg( File file) {        //1.创建OkHttpClient对象        OkHttpClient okHttpClient = new OkHttpClient();        //2.设置文件类型        MediaType mediaType = MediaType.parse("image/png");        if (file != null && file.exists()) {            //3.构造RequestBody 指定文件类型和文件            RequestBody image = RequestBody.create(mediaType, file);            RequestBody requestBody = new MultipartBody.Builder()                    .setType(MultipartBody.FORM)                    .addFormDataPart("img", file.getName(), image)                    .build();            //4.创建Request对象            Request request = new Request.Builder()                    .header("Authorization", "Client-ID " + "...")                    .url("www.baidu.login")                    .post(multipartBody)                    .build();            //5.异步请求newCall(Callback)            okHttpClient.newCall(request).enqueue(new Callback() {                @Override                public void onFailure(Call call, IOException e) {                    Log.e("TAG", "图片上传失败="+e.getMessage());                }                @Override                public void onResponse(Call call, Response response) throws IOException {                    String result = response.body().string();                    Log.e("TAG", "成功上传图片=" + result);                }            });        }    }

    MultipartBody继承RequestBody,具有自己的contentType+BufferedSink,是POST请求的最外层封装,需要添加多个Part
    Part对象组成:Headers+RequestBody。是MultipartBody的成员变量,需要写入MultipartBody的BufferedSink中。

    GET下载图片

    1.创建OkHttpClient对象
    2.创建Request对象
    3.异步请求newCall(Callback)
    4.用文件流下载在本地文件夹下

     

     /**   * 上传图片   * 没有测试服务器地址   */  public void PostImgRequet(View view) {    //1.创建OkHttpClient对象    OkHttpClient okHttpClient = new OkHttpClient();    //2.设置文件类型    MediaType mediaType = MediaType.parse("image/png");    if (file != null && file.exists()) {      //3.构造RequestBody 指定文件类型和文件      RequestBody image = RequestBody.create(mediaType, file);      RequestBody requestBody = new MultipartBody.Builder()          .setType(MultipartBody.FORM)          .addFormDataPart("img", file.getName(), image)          .build();      //4.创建Request对象      Request request = new Request.Builder()          .header("Authorization", "Client-ID " + "...")          .url("www.baidu.login")          .post(requestBody)          .build();      //5.异步请求newCall(Callback)      okHttpClient.newCall(request).enqueue(new Callback() {        @Override        public void onFailure(Call call, IOException e) {          mHandler.sendEmptyMessage(0);          Log.e("TAG", "图片上传失败="+e.getMessage());        }        @Override        public void onResponse(Call call, Response response) throws IOException {          String result = response.body().string();          mHandler.sendEmptyMessage(1);          Log.e("TAG", "成功上传图片=" + result);        }      });    }  }  /**   * 下载图片   */  public void GetImgRequetSimpleness(View view) {    //1.创建OkHttpClient对象    OkHttpClient okHttpClient = new OkHttpClient();    String url = "http://p0.meituan.net/165.220/movie/7f32684e28253f39fe2002868a1f3c95373851.jpg";    //2.创建Request对象    Request request  = new Request.Builder()        .url(url)        .build();    //3.异步请求newCall(Callback)    okHttpClient.newCall(request).enqueue(new Callback() {      @Override      public void onFailure(Call call, IOException e) {        Log.e("TAG", "下载失败");        mHandler.sendEmptyMessage(0);      }      @Override      public void onResponse(Call call, Response response) throws IOException {        runOnUiThread(new Runnable() {          @Override          public void run() {            Toast.makeText(SimplenessActivity.this, "下载图片成功", Toast.LENGTH_SHORT).show();          }        });        /**         * 用java文件输入流下载图片         */               /* InputStream inputStream = response.body().byteStream();                FileOutputStream fileOutputStream = null;                try {                    fileOutputStream = new FileOutputStream(new File("/sdcard/okhttp.jpg"));                    byte[] buffer = new byte[2048];                    int len = 0;                    while ((len = inputStream.read(buffer)) != -1) {                        fileOutputStream.write(buffer, 0, len);                    }                    fileOutputStream.flush();                } catch (IOException e) {                    Log.i("TAG", "IOException");                    e.printStackTrace();                }*/        //方法一,获取byte数组,然后转换成图片        byte[] bytes = response.body().bytes();        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);        //方法二,可以获取字节流,然后转换成图片//        InputStream inputStream = response.body().byteStream();//        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);        /**         * 保存图片         */ /*       File file=new File("/sdcard/okhttp.jpg");        file.createNewFile();        //创建文件输出流对象用来向文件中写入数据        FileOutputStream out=new FileOutputStream(file);        //将bitmap存储为jpg格式的图片        bitmap.compress(Bitmap.CompressFormat.JPEG,100,out);        //刷新文件流        out.flush();        out.close();*/        if(bitmap!=null) {          Log.e("TAG", "图片下载成功");        }      }    });  }

    下载图片可以用java文件输入流下载图片、BitmapFactory.decodeByteArray、 BitmapFactory.decodeStream,根据自己的需求来使用。

    图文混合上传

    1.创建OkHttpClient对象
    2.构建多部件builder
    3.创建 Map 添加需要的键值对
    4.获取要上传的图片集合
    5.获取参数并放到请求体中
    6.添加图片集合到请求体中
    7.构造Request
    8.异步请求enqueue(Callback)

     

    /**   * 图文混合上传   * @param view   */  public void PostImgKeyValueRequet(View view) {    //1.创建OkHttpClient对象    OkHttpClient okHttpClient = new OkHttpClient();    //2.构建多部件builder    MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);    //3.创建 Map 添加需要的键值对    Map params = new HashMap<>();    params.put("username","15294792877");    params.put("password","15294792877pp");    //4.获取要上传的图片集合    List fileList = new ArrayList<>();    //5.获取参数并放到请求体中    try {      if (params != null) {        for (Map.Entry entry : params.entrySet()) {          //将请求参数逐一遍历添加到我们的请求构建类中          bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());        }      }    } catch (Exception e) {      e.printStackTrace();    }    //6.添加图片集合到请求体中    if (fileList != null) {      for (File f : fileList) {        bodyBuilder.addFormDataPart("files", f.getName(),            RequestBody.create(MediaType.parse("image/png"), f));      }    }    //7.构造Request    Request request = new Request.Builder()        .url("https://www.wanandroid.com/user/login")        .post(bodyBuilder.build())        .build();    //8.异步请求enqueue(Callback)    okHttpClient.newCall(request).enqueue(new Callback() {      @Override      public void onFailure(Call call, IOException e) {        mHandler.sendEmptyMessage(0);        Log.e("TAG", "失败="+e.getMessage());      }      @Override      public void onResponse(Call call, Response response) throws IOException {        String json = response.body().string();        Log.e("TAG", "成功="+json);      }    });  }

    要构建一个多部件MultipartBody.Builder,设置其类型为FORM("multipart/form-data")。然后把需要上传的key-value键值对和图片都通过addFormDataPart()方法添加进去。添加图片时name要和后端接口指定name相同,还要添加RequestBody指定类型("image/png")。

    封装使用

    如果不封装使用起来还是很繁琐的,比如:写重复的代码、增加类的代码量、不易维护、回调函数不在主线程... 接下来我们就来封装一个。(加泛型使用)

    先看下封装后请求代码

    一个get请求,一个post请求,和上面没有封装时相比是不是代码很简洁清晰。下面就简单的讲一下封装的过程,想详细的看封装过程请下载demo查看,每个类都有注解。

     

    /**   * GET请求    * 返回类型要Json字符串   */  public void GetRequet(View view) {    HttpRequest.getBannerApi(null, new ResponseCallback() {      @Override      public void onSuccess(String s) {        Toast.makeText(EncapsulationActivity.this, "请求成功" + s.toString(), Toast.LENGTH_SHORT)            .show();      }      @Override      public void onFailure(OkHttpException failuer) {        Toast.makeText(EncapsulationActivity.this, "请求失败=" + failuer.getEmsg(), Toast.LENGTH_SHORT)            .show();      }    });  }  /**   * POST请求    * 返回类型我要实体类   */  public void PostKeyValueRequet(View view) {    RequestParams params = new RequestParams();    params.put("username", "15294792877");    params.put("password", "15294792877pp");    HttpRequest.postLoginApi(params, new ResponseCallback>() {      @Override      public void onSuccess(BaseBean infoBaseBean) {        Toast.makeText(EncapsulationActivity.this, "成功=" + infoBaseBean.toString(),            Toast.LENGTH_SHORT).show();      }      @Override      public void onFailure(OkHttpException failuer) {        Toast.makeText(EncapsulationActivity.this, "失败=" + failuer.getEmsg(), Toast.LENGTH_SHORT)            .show();      }    });  }  /**   * 下载图片   *  可以用GET方式||POST方式,一般是用POST方式 除非你们公司不注重隐式,   *   本案例用的是GET方式,因为没有找到免费的POST请求api。   *   * @param view   */  public void GetImgRequet(View view) {    HttpRequest.getImgApi(null, String.valueOf(System.currentTimeMillis()) + ".png",        new ResponseByteCallback() {          @Override          public void onSuccess(File file) {            Toast.makeText(EncapsulationActivity.this, "图片下载成功="+file.getAbsolutePath(), Toast.LENGTH_SHORT).show();            Log.e("TAG", "图片下载成功="+file.getAbsolutePath());          }          @Override          public void onFailure(String failureMsg) {            Toast.makeText(EncapsulationActivity.this, "图片下载失败="+failureMsg, Toast.LENGTH_SHORT).show();            Log.e("TAG", "图片下载失败="+failureMsg);          }        });  }  /**   * 图文混合   * @param view   */  public void PostImgKeyValueRequet(View view) {    RequestParams params = new RequestParams();    params.put("name", "aaaaaaa");    //添加图片    List fileList = new ArrayList<>();//    HttpRequest.postMultipartApi(params, fileList, new ResponseCallback() {//      @Override//      public void onSuccess(Object responseObj) {////      }////      @Override//      public void onFailure(OkHttpException failuer) {////      }//    });  }

    请求日志.png

    OkHttpClient对象

    初始化全局OkHttpClient对象,为我们的Client配置参数,使用静态语句块来配置,只执行一次,运行一开始就开辟了内存,内存放在全局。主要设置有缓存、超时时间、重定向、拦截器、Https支持,根据自己项目需求类配置就行了。

     

    /** * @author:PengJunShan. * 时间:On 2019-05-05. * 描述:OkHttpClient对象 */public class CommonOkHttpClient {    /**     * 超时时间     */    private static final int TIME_OUT = 30;    private static OkHttpClient mOkHttpClient;    /**     * 为我们的Client配置参数,使用静态语句块来配置     * 只执行一次,运行一开始就开辟了内存,内存放在全局     */    static {        //获取缓存路径        File cacheDir = MyApplication.context.getExternalCacheDir();        //设置缓存的大小        int cacheSize = 10 * 1024 *1024 ;        //创建我们Client对象的构建者        OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();        okHttpBuilder                //为构建者设置超时时间                .connectTimeout(TIME_OUT, TimeUnit.SECONDS)                .readTimeout(TIME_OUT, TimeUnit.SECONDS)                .writeTimeout(TIME_OUT, TimeUnit.SECONDS)                ////websocket轮训间隔(单位:秒)                .pingInterval(20, TimeUnit.SECONDS)                //设置缓存                .cache(new Cache(cacheDir.getAbsoluteFile(), cacheSize))                //允许重定向                .followRedirects(true)                //设置拦截器                .addInterceptor(new RequetInterceptor())                //添加https支持                .hostnameVerifier(new HostnameVerifier() {                    @Override                    public boolean verify(String s, SSLSession sslSession) {                        return true;                    }                })                .sslSocketFactory(HttpsUtils.initSSLSocketFactory(), HttpsUtils.initTrustManager());        mOkHttpClient = okHttpBuilder.build();    }    /**     * 发送具体的HTTP以及Https请求     */    public static Call sendRequest(Request request, CommonJsonCallback commonCallback) {        Call call = mOkHttpClient.newCall(request);        call.enqueue(commonCallback);        return call;    }    /**     * GET请求     */    public static Call get(Request request, ResposeDataHandle handle) {        Call call = mOkHttpClient.newCall(request);        call.enqueue(new CommonJsonCallback(handle));        return call;    }    /**     * POST请求     */    public static Call post(Request request, ResposeDataHandle handle) {        Call call = mOkHttpClient.newCall(request);        call.enqueue(new CommonJsonCallback(handle));        return call;    } /**   * POST请求图片   */  public static Call downLadImg(Request request, final String imgPath,      final ResponseByteCallback callback) {    Call call = mOkHttpClient.newCall(request);    call.enqueue(new Callback() {      @Override      public void onFailure(Call call, IOException e) {        Log.e("TAG", "下载图片失败=" + e.getMessage());        new Handler().post(new Runnable() {          @Override          public void run() {            callback.onFailure(e.getMessage());          }        });      }      @Override      public void onResponse(Call call, Response response) throws IOException {        Log.e("TAG", "下载图片成功=" + response);        File file = null;        try {          InputStream is = response.body().byteStream();          int len = 0;          // 文件夹路径          String pathUrl =              Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator                  + "/sgcc/";          File filepath = new File(pathUrl);          if (!filepath.exists()) {            filepath.mkdirs();// 创建文件夹          }          file = new File(pathUrl, imgPath);          FileOutputStream fos = new FileOutputStream(file);          byte[] buf = new byte[2048];          while ((len = is.read(buf)) != -1) {            fos.write(buf, 0, len);          }          fos.flush();          fos.close();          is.close();          File finalFile = file;          new Handler(Looper.getMainLooper()).post(new Runnable() {            @Override            public void run() {              callback.onSuccess(finalFile);            }          });        } catch (final Exception e) {          new Handler(Looper.getMainLooper()).post(new Runnable() {            @Override            public void run() {              callback.onFailure(e.getMessage());            }          });                 }      }    });    return call;  }}

    公共入参(CommonRequest)

    我们每次请求都会创建Request对象,写着重复的代码,那我们就写一个类专门处理入参然后返回Request对象。

     

    /** * 创建: PengJunShan * 描述: 公共入参 */public class CommonRequest {  /**   * 创建Get请求的Request   */  public static Request createGetRequest(String url, RequestParams params) {    StringBuilder urlBuilder = new StringBuilder(url).append("?");    if (params != null) {      for (Map.Entry entry : params.urlParams.entrySet()) {        urlBuilder            .append(entry.getKey())            .append("=")            .append(entry.getValue())            .append("&");      }    }    return new Request.Builder().url(urlBuilder.substring(0, urlBuilder.length() - 1))        .get().build();  }  /**   * 创建Post请求的Request   *   * @return 返回一个创建好的Request对象   */  public static Request createPostRequest(String url, RequestParams params) {    FormBody.Builder mFromBodyBuilder = new FormBody.Builder();    //将请求参数逐一遍历添加到我们的请求构建类中    for (Map.Entry entry : params.urlParams.entrySet()) {      mFromBodyBuilder.add(entry.getKey(), entry.getValue());    }    //通过请求构建类的build方法获取到真正的请求体对象    FormBody mFormBody = mFromBodyBuilder.build();    Request request = new Request.Builder()        .url(url)        .post(mFormBody)        .build();    return request;  }  /**   * 混合form和图片   * @return 返回一个创建好的Request对象   */  public static Request createMultipartRequest(String url, RequestParams params, List files) {    //构建多部件builder    MultipartBody.Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);    //获取参数并放到请求体中    try {      if (params != null) {        JSONObject jsonObject = new JSONObject();        for (Map.Entry entry : params.urlParams.entrySet()) {          //将请求参数逐一遍历添加到我们的请求构建类中          bodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());          jsonObject.put(entry.getKey(), entry.getValue());        }        Log.e("TAG", "入参:   " + jsonObject.toString());      }    } catch (JSONException e) {      e.printStackTrace();    }    //添加图片集合放到请求体中    if (files != null) {      for (File f : files) {        bodyBuilder.addFormDataPart("files", f.getName(),            RequestBody.create(MediaType.parse("image/png"), f));      }    }    Request request = new Request.Builder()        .url(url)        .post(bodyBuilder.build())        .build();    return request;  }}

    请求模式(RequestMode)

    实际工作中常用的请求模式有:get(无参)、post(key-value)、图文混合、图片下载。

     

    /** * 创建: PengJunShan * 描述:请求模式 */public class RequestMode {  /**   * GET请求   * @param url URL请求地址   * @param params 入参   * @param callback 回调接口   * @param clazz 需要解析的实体类   */  public static void getRequest(String url, RequestParams params,      ResponseCallback callback, Class<?> clazz) {    CommonOkHttpClient.get(CommonRequest.createGetRequest(url, params),        new ResposeDataHandle(callback, clazz));  }  /**   * POST请求   * @param url URL请求地址   * @param params 入参   * @param callback 回调接口   * @param clazz 需要解析的实体类   */  public static void postRequest(String url, RequestParams params,      ResponseCallback callback, Class<?> clazz) {    CommonOkHttpClient.post(CommonRequest.createPostRequest(url, params),        new ResposeDataHandle(callback, clazz));  }  /**   * 下载图片 Get方式   */  public static void getLoadImg(String url,RequestParams params,String imgPath, ResponseByteCallback callback){    CommonOkHttpClient.downLadImg(CommonRequest.createGetRequest(url, params),imgPath,callback);  }  /**   * 下载图片 Post方式   */  public static void postLoadImg(String url,RequestParams params,String imgPath, ResponseByteCallback callback){    CommonOkHttpClient.downLadImg(CommonRequest.createPostRequest(url, params),imgPath,callback);  }  /**   * 表单和媒体 图文混合   */  public static void postMultipart(String url, RequestParams params,      List files, ResponseCallback callback, Class<?> clazz) {    CommonOkHttpClient.post(CommonRequest.createMultipartRequest(url, params, files),        new ResposeDataHandle(callback, clazz));  }}

    HttpRequest

    HttpRequest存放所有的请求接口,我们在activity中请求接口最先就是调用的这个类中的方法。

     

    /** * 作者:PengJunShan. * 时间:On 2019-05-05. * 描述:所有的请求接口 */public class HttpRequest {  /**   * @param params 入参   * @param callback 回调接口   */  public static void getBannerApi(RequestParams params, ResponseCallback callback) {    RequestMode.getRequest("https://www.wanandroid.com/banner/json", params, callback);  }  /**   * @param params 入参   * @param callback 回调接口   */  public static void postLoginApi(RequestParams params, ResponseCallback> callback) {    RequestMode.postRequest("https://www.wanandroid.com/user/login", params, callback);  }  /**   * 下载图片 Get方式   * @param params 入参   * @param imgPath 存储地址   * @param callback 回调接口   */  public static void getImgApi(RequestParams params,String imgPath, ResponseByteCallback callback) {    RequestMode.getLoadImg("http://p0.meituan.net/165.220/movie/7f32684e28253f39fe2002868a1f3c95373851.jpg",params,imgPath,callback);  }  /**   * 下载图片 Post方式   * @param params 入参   * @param imgPath 存储地址   * @param callback 回调接口   */  public static void postImgApi(RequestParams params,String imgPath, ResponseByteCallback callback) {    RequestMode.postLoadImg("url地址",params,imgPath,callback);  }  /**   * 图文混合上传服务器   * @param params   * @param files   * @param callback   */  public static void postMultipartApi(RequestParams params, List files, ResponseCallback callback) {    RequestMode.postMultipart("url地址", params, files, callback, null);  }}

    回调ResponseCallback

    每次请求api都创建这个抽象类并实现其抽象方法,通过回传把数据回调。

     

    /** * 创建: PengJunShan 描述:回调 使用泛型 */public abstract class ResponseCallback {  Type mType;  public ResponseCallback() {    //Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。    Type superclass = getClass().getGenericSuperclass();    if (superclass instanceof Class) {//      throw new RuntimeException("请传入实体类");      mType = null;    } else {      //ParameterizedType参数化类型,即泛型      ParameterizedType parameterized = (ParameterizedType) superclass;      //getActualTypeArguments获取参数化类型的数组,泛型可能有多个      //将Java 中的Type实现,转化为自己内部的数据实现,得到gson解析需要的泛型      mType = $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);    }  }  //请求成功回调事件处理  public abstract void onSuccess(T t);  //请求失败回调事件处理  public abstract void onFailure(OkHttpException failuer);}

    处理JSON数据(CommonJsonCallback)

    当我们请求到Json数据后不是直接返给最前端的接口,而是先进行解析处理。让这个类继承Callback接口,实现onFailure()、onResponse()方法。如果走了onFailure()失败中,通过Exception类型判断失败原因。如果走了onResponse()中,首先是获取errorMsg值来判断是否成功然后解析数据。大家都知道CallBck的回调是在子线程中不能操作UI,那该怎么办呢?我们在创建Handler时通过Looper.getMainLooper(),获得主线程的Looper,将其绑定到此Handler对象上,这种情况下,Runnable对象是运行在主线程中可以更新UI操作。

     

    /** * 创建: PengJunShan * 描述:专门处理JSON数据的回调响应 */public class CommonJsonCallback implements Callback {  /**   * errorCode是根据接口返回的标识 实际根据自己接口返回为准   */  protected final String RESULT_CODE = "errorCode";  protected final int RESULT_CODE_VALUE = 0;  /**   * errorMsg字段提示信息,实际根据自己接口返回为准   */  protected final String ERROR_MSG = "errorMsg";  protected final String NETWORK_MSG = "请求失败";  protected final String JSON_MSG = "解析失败";  /**   * 自定义异常类型   */  protected final int NETWORK_ERROR = -1; //网络失败  protected final int JSON_ERROR = -2; //解析失败  protected final int OTHER_ERROR = -3; //未知错误  protected final int TIMEOUT_ERROR = -4; //请求超时  private Handler mDeliveryHandler; //进行消息的转发  private ResponseCallback mListener;  public CommonJsonCallback(ResposeDataHandle handle) {    this.mListener = handle.mListener;    this.mDeliveryHandler = new Handler(Looper.getMainLooper());  }  /**   * 请求失败的处理   */  @Override  public void onFailure(@NonNull Call call, @NonNull final IOException e) {    Log.e("TAG", "请求失败=" + e.getMessage());    mDeliveryHandler.post(new Runnable() {      @Override      public void run() {        if (!Utils.isConnected(MyApplication.context)) {          mListener.onFailure(new OkHttpException(NETWORK_ERROR, "请检查网络"));        } else if (e instanceof SocketTimeoutException) {          //判断超时异常          mListener.onFailure(new OkHttpException(TIMEOUT_ERROR, "请求超时"));        } else if (e instanceof ConnectException) {          //判断超时异常          mListener.onFailure(new OkHttpException(OTHER_ERROR, "请求服务器失败"));        } else {          mListener.onFailure(new OkHttpException(NETWORK_ERROR, e.getMessage()));        }      }    });  }  /**   * 请求成功的处理 回调在主线程   */  @Override  public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {    final String result = response.body().string();    mDeliveryHandler.post(new Runnable() {      @Override      public void run() {        handleResponse(result);      }    });  }  /**   * 处理Http成功的响应   */  private void handleResponse(Object responseObj) {    if (responseObj == null && responseObj.toString().trim().equals("")) {      mListener.onFailure(new OkHttpException(NETWORK_ERROR, NETWORK_MSG));      return;    }    try {      JSONObject result = new JSONObject(responseObj.toString());      if (result.has(RESULT_CODE)) {        //从JSON对象中取出我们的响应码,如果为0,则是正确的响应 (实际情况按你们接口文档)        if (result.getInt(RESULT_CODE) == RESULT_CODE_VALUE) {          /**           * 判断是否需要解析成实体类还是json字符串           * class com.google.gson.internal.$Gson$Types$ParameterizedTypeImpl           */          Gson gson = new GsonBuilder().serializeNulls().create();          T obj = null;          if (!mListener.mType.getClass().equals("java.lang.Class")) {            obj = gson.fromJson((String) responseObj, mListener.mType);          } else {            obj = (T) responseObj;          }          if (obj != null) {            mListener.onSuccess(obj);          } else {            mListener.onFailure(new OkHttpException(JSON_ERROR, JSON_MSG));          }        } else { //将服务端返回的异常回调到应用层去处理          mListener.onFailure(new OkHttpException(OTHER_ERROR, result.get(ERROR_MSG) + ""));          Log.e("TAG", "onResponse处理失败");        }      }    } catch (Exception e) {      e.printStackTrace();      mListener.onFailure(new OkHttpException(OTHER_ERROR, e.getMessage()));      Log.e("TAG", "onResponse处理失败" + e.getMessage());    }  }}

    日志拦截器(RequetInterceptor)

    拦截器的作用还是很大的,一般我们工作中请求头部都会传入token、用户id标识认证参数。连接器中可以拦截到Request对象,然后添加头部公共参数。通过获取FormBody可以打印出入参参数,通过获取Response可以打印出出参参数。不能直接使用response.body().string()的方式输出日志,因为response.body().string()之后,response中的流会被关闭,我们需要创建出一个新的response给应用层处理。

     

    /** * @author:PengJunShan. * 时间:On 2019-05-05. * 描述:日志拦截器 */public class RequetInterceptor implements Interceptor {  /**   * 这个chain里面包含了request和response,所以你要什么都可以从这里拿   */  @Override  public Response intercept(Chain chain) throws IOException {    /**     * 可以添加公共头部参数如token     */    Request request = chain.request()        .newBuilder()//        .header("TOKEN", token)//        .header("ID", id)        .build();    /**     * 开始时间     */    long startTime = System.currentTimeMillis();    Log.e("TAG","\n"+"requestUrl=" + request.url());    String method = request.method();    if ("POST".equals(method)) {      try {        JSONObject jsonObject = new JSONObject();        if (request.body() instanceof FormBody) {          FormBody body = (FormBody) request.body();          for (int i = 0; i < body.size(); i++) {            jsonObject.put(body.encodedName(i), body.encodedValue(i));          }          Log.e("TAG","入参JSON= " + jsonObject.toString());        }      } catch (JSONException e) {        e.printStackTrace();      }    }    Response response = chain.proceed(request);    /**     * 这里不能直接使用response.body().string()的方式输出日志     * 因为response.body().string()之后,response中的流会被关闭,程序会报错,我们需要创建出一个新的response给应用层处理     */    ResponseBody responseBody = response.peekBody(1024 * 1024);    Log.e("TAG","出参JSON=" + responseBody.string());    long endTime = System.currentTimeMillis();    long duration = endTime - startTime;    Log.e("TAG","----------" + "耗时:" + duration + "毫秒----------");    return response;  }}

    结束

    网上有很多别人封装好的库,但是符不符合自己的项目使用就是另外一回事了。还不如自己花点时间封装一个既简单又符合自己项目使用的代码,文章中贴出来的代码是核心代码并不是所有的代码,下载代码根据自己项目需求修改一下就可以用的。下篇我要写一个以retrofit2+rxjava2进行网络请求封装。

    项目地址:https://github.com/pengjunshan/UseOkhttp3

    拿到代码后移到自己项目中根据自己项目需求修改即可使用。

更多相关文章

  1. Android(安卓)通过API获取数据库中的图片文件方式
  2. Android(安卓)Shortcuts 为其他应用创建桌面快捷方式,替换图标等
  3. Android中创建倒影效果的工具类
  4. Android网络框架-OkHttp使用
  5. [转]Android(安卓)Launcher 分析
  6. 【Android】16.4 IntentService类
  7. 如何为android创建icon?
  8. Android中实现JSON字符串和JSON对象的转换
  9. Android(安卓)5.0 Lollipop新的摄像头API

随机推荐

  1. Android之RemoteViews
  2. Gradle 打不同的安装包
  3. Android createWindowSurface failed EGL
  4. Android中TextView实现文字跑马灯效果(滚
  5. ClipDrawable制作图片渐进效果
  6. android点击事件流程
  7. JavaEE + Android 教程
  8. android Fragment相关问题
  9. 关于Android import-module 和NDK_MODULE
  10. Android使用WindowManager做一个可拖动的