【186】okHttp 3.0 封装分析

0.简单说明

对是基于okhttp 2.4.0 封装

 参考文章:https://blog.csdn.net/lmj623565791/article/details/47911083

 compile 'com.squareup.okhttp:okhttp:2.4.0'
 compile 'com.squareup.okio:okio:1.5.0'

1.外部调用方法

   //[1]同步Get请求-返回原始response
    public static Response getAsyn(String url) throws IOException {
        return getInstance()._getAsyn(url);
    }
//[2]同步Get请求-返回String public static String getAsString(String url) throws IOException { return getInstance()._getAsString(url); }
//[3]异步Get请求-返回原始请求 public static void getAsyn(String url, ResultCallback callback) { getInstance()._getAsyn(url, callback); }
//[4]同步Post请求--返回原始response public static Response post(String url, Param... params) throws IOException { return getInstance()._post(url, params); }
//[5]同步Post请求--返回string public static String postAsString(String url, Param... params) throws IOException { return getInstance()._postAsString(url, params); } //[6]异步post请求封装 public static void postAsyn(String url, final ResultCallback callback, Param... params) { getInstance()._postAsyn(url, callback, params); }
//[7]异步post请求封装,参数的形式是map public static void postAsyn(String url, final ResultCallback callback, Map<String, String> params) { getInstance()._postAsyn(url, callback, params); }
//[8]post同步文件上传-file[]+fileKey[]+params public static Response post(String url, File[] files, String[] fileKeys, Param... params) throws IOException { return getInstance()._post(url, files, fileKeys, params); }
//[9]file+fileKey public static Response post(String url, File file, String fileKey) throws IOException { return getInstance()._post(url, file, fileKey); }
//[10]file+fileKey+params public static Response post(String url, File file, String fileKey, Param... params) throws IOException { return getInstance()._post(url, file, fileKey, params); }
//[11]异步post文件上传 public static void postAsyn(String url, ResultCallback callback, File[] files, String[] fileKeys, Param... params) throws IOException { getInstance()._postAsyn(url, callback, files, fileKeys, params); }
//[12]异步post文件上传,单文件不带参数上传 public static void postAsyn(String url, ResultCallback callback, File file, String fileKey) throws IOException { getInstance()._postAsyn(url, callback, file, fileKey); }
//[13]异步post文件上传,单文件且携带其他form参数上传 public static void postAsyn(String url, ResultCallback callback, File file, String fileKey, Param... params) throws IOException { getInstance()._postAsyn(url, callback, file, fileKey, params); }
//[14]下载图片并加载图片,可以设置默认加载失败的图片 public static void displayImage(final ImageView view, String url, int errorResId) throws IOException { getInstance()._displayImage(view, url, errorResId); }
//[15]下载图片并加载图片 public static void displayImage(final ImageView view, String url) { getInstance()._displayImage(view, url, -1); }
//[16]异步下载文件 public static void downloadAsyn(String url, String destDir, ResultCallback callback) { getInstance()._downloadAsyn(url, destDir, callback); }

2.构造方法

 1 private OkHttpClientManager() {
 2         mOkHttpClient = new OkHttpClient(); //建立OkHttpClient类;
 3         /*
 4         【1】使能cookie enabled
 5           三种请求:
 6           ACCEPT_ALL :accepts all cookies
 7           ACCEPT_NONE:accepts no cookies
 8           ACCEPT_ORIGINAL_SERVER :only accepts cookies from original server
 9          */
10         mOkHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER));
11         mDelivery = new Handler(Looper.getMainLooper()); //对子线程的的网路请求;
12         mGson = new Gson(); //Gson对象;
13     }

3.单例对象

 1 /**
 2      * 【2】单例
 3      * @return
 4      */
 5     public static OkHttpClientManager getInstance() {
 6         if (mInstance == null) {
 7             synchronized (OkHttpClientManager.class) {
 8                 if (mInstance == null) {
 9                     mInstance = new OkHttpClientManager();
10                 }
11             }
12         }
13         return mInstance;
14     }

3.同步Get请求

【返回原始response】

 1 /**
 2      * 同步的Get请求-返回原始response 3      * 
 4      * 说明:okhttp默认是get请求。
 5      * @param url
 6      * @return Response
 7      */
 8     private Response _getAsyn(String url) throws IOException {
 9         
10         final Request request = new Request.Builder()
11                 .url(url)
12                 .build();
13         Call call = mOkHttpClient.newCall(request);
14         Response execute = call.execute();
15         return execute;
16     }
【返回String类型数据】
 1 /**
 2      * 同步的Get请求
 3      * @param url
 4      * @return 字符串
 5      * 
 6      * /直接调用了返回原始response方法,然后解析为string;
 7      * 
 8      */
 9     private String _getAsString(String url) throws IOException {
10         Response execute = _getAsyn(url);  
11         return execute.body().string();
12     }

4. 异步get请求

 1 /**
 2      * 异步get请求
 3      *
 4      * @param url
 5      * @param callback 封转之后的callback
 6      */
 7     private void _getAsyn(String url, final ResultCallback callback) {
 8         final Request request = new Request.Builder()
 9                 .url(url)
10                 .build();
11         deliveryResult(callback, request); //封装了异步请求;
12     }
【异步请求封装-错误信息的返回】
【说明】此处的mDelivery是声明的Handler对象
 1 private void sendFailedStringCallback(final Request request, final Exception e, final ResultCallback callback) {
 2         //使用handler在子线程中将错误response和error码返回
 3         mDelivery.post(new Runnable() {
 4             @Override
 5             public void run() {
 6                 if (callback != null)
 7                     callback.onError(request, e);
 8             }
 9         });
10     }
【异步请求封装-原始信息的返回】此处传入的参数是object,后面会根据封装的callback的mType判断不同的情况然后进行不同的处理。
 1 private void sendSuccessResultCallback(final Object object, final ResultCallback callback) {
 2         //使用handler在子线程中将response结果返回
 3         mDelivery.post(new Runnable() {
 4             @Override
 5             public void run() {
 6                 if (callback != null) {
 7                     callback.onResponse(object);
 8                 }
 9             }
10         });
11     }
【异步请求的完整封装】
 1 private void deliveryResult(final ResultCallback callback, Request request) {
 2         mOkHttpClient.newCall(request).enqueue(new Callback() { //直接调用okhttp的异步请求
 3             @Override
 4             public void onFailure(final Request request, final IOException e) {
 5                 sendFailedStringCallback(request, e, callback);
 6             }
 7 
 8             @Override
 9             public void onResponse(final Response response) {
10                 try {
11                     final String string = response.body().string();
12                     //如果结果是String类型,返回string
13                     if (callback.mType == String.class) {
14                         sendSuccessResultCallback(string, callback);
15                     } else {
16                         //否则,使用Gson解析返回json数据;
17                         Object o = mGson.fromJson(string, callback.mType);
18                         sendSuccessResultCallback(o, callback);
19                     }
20                 } catch (IOException e) {
21                     sendFailedStringCallback(response.request(), e, callback);//返回错误信息
22                 } catch (com.google.gson.JsonParseException e){
23                     sendFailedStringCallback(response.request(), e, callback);//返回json解析错误;
24                 }
25             }
26         });
27     }

5.ResultCallback 封装

 1 /**
 2      * 将callback封装成抽象类
 3      * @param <T>
 4      */
 5     public static abstract class ResultCallback<T> {
 6         Type mType;  //返回的response数据具有不同的类型,此处必须判断当前的
 7 
 8         public ResultCallback() {
 9             mType = getSuperclassTypeParameter(getClass());
10         }
11 
12         static Type getSuperclassTypeParameter(Class<?> subclass) {
13             Type superclass = subclass.getGenericSuperclass();
14             if (superclass instanceof Class) {
15                 throw new RuntimeException("Missing type parameter.");
16             }
17             //对应找到Gson的类型;
18             ParameterizedType parameterized = (ParameterizedType) superclass;
19             return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
20         }
21 
22         //对应onFailuer方法;
23         public abstract void onError(Request request, Exception e);
24         //对应onResponse方法;
25         public abstract void onResponse(T response);
26     }

 6.同步post请求封装--返回原始response

 1 /**
 2      * 同步的Post请求
 3      *
 4      * @param url
 5      * @param params post的参数
 6      * @return 返回原始响应数据
 7      */
 8     private Response _post(String url, Param... params) throws IOException {
 9         Request request = buildPostRequest(url, params); //对post的Request的封装;
10         Response response = mOkHttpClient.newCall(request).execute();
11         return response;  //返回原始响应数据
12     }

【PostRequest的封装】

 1 /**
 2      * 生成FormEncodingBuilder/FormBody,然后填充数据;
 3      * @param url
 4      * @param params
 5      * @return
 6      */
 7     private Request buildPostRequest(String url, Param[] params) {
 8         if (params == null) {
 9             params = new Param[0];
10         }
11         
12         /*
13             okHttp3的写法;
14         FormBody.Builder builder1 = new FormBody.Builder();
15         for (Param param:params){
16             builder1.add(param.key,param.value);
17         }*/
18         
19         //创建一个fromBody--okHttp3的写法;
20         FormEncodingBuilder builder = new FormEncodingBuilder();
21         for (Param param : params) {
22             builder.add(param.key, param.value);
23         }
24         RequestBody requestBody = builder.build();
25         return new Request.Builder()
26                 .url(url)
27                 .post(requestBody)
28                 .build();
29     }

7.同步post请求封装--返回String

 1 /**
 2      * 同步的Post请求--返回String
 3      *
 4      * @param url
 5      * @param params post的参数
 6      * @return 字符串
 7      */
 8     private String _postAsString(String url, Param... params) throws IOException {
 9         Response response = _post(url, params); //调用上一个post请求,只是返回的参数进行了String提取;
10         return response.body().string(); //返回String
11     }

8.异步post请求封装

 1 /**
 2      * 异步post请求封装
 3      *
 4      * @param url
 5      * @param callback
 6      * @param params
 7      */
 8     private void _postAsyn(String url, final ResultCallback callback, Param... params) {
 9         Request request = buildPostRequest(url, params);
10         deliveryResult(callback, request);
11     }
12 
13     /**
14      * 异步的post请求
15      *
16      * @param url
17      * @param callback
18      * @param params
19      */
20     private void _postAsyn(String url, final ResultCallback callback, Map<String, String> params) {
21         Param[] paramsArr = map2Params(params);//params如果是map形式,则转化为数组;
22         Request request = buildPostRequest(url, paramsArr);
23         deliveryResult(callback, request);
24     }
首先认识Parmas类是键值对
 1 public static class Param {
 2         public Param() {
 3         }
 4 
 5         public Param(String key, String value) {
 6             this.key = key;
 7             this.value = value;
 8         }
 9 
10         String key;
11         String value;
12     }
【map->array转化】
 1 private Param[] map2Params(Map<String, String> params) {
 2         if (params == null)
 3             return new Param[0];
 4 
 5         int size = params.size();
 6         Param[] res = new Param[size];
 7         Set<Map.Entry<String, String>> entries = params.entrySet();
 8         int i = 0;
 9         for (Map.Entry<String, String> entry : entries) {
10             res[i++] = new Param(entry.getKey(), entry.getValue());
11         }
12         return res;
13     }

9.post同步文件上传

 1 /**
 2      * 同步基于post的文件上传
 3      *
 4      * @param params
 5      * @return
 6      */
 7     //file[]+fileKey[]+params
 8     private Response _post(String url, File[] files, String[] fileKeys, Param... params) throws IOException {
 9         Request request = buildMultipartFormRequest(url, files, fileKeys, params);
10         return mOkHttpClient.newCall(request).execute();
11     }
12 
13     //file+fileKey
14     private Response _post(String url, File file, String fileKey) throws IOException {
15         Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, null);
16         return mOkHttpClient.newCall(request).execute();
17     }
18     //file+fileKey+params
19     private Response _post(String url, File file, String fileKey, Param... params) throws IOException {
20         Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, params);
21         return mOkHttpClient.newCall(request).execute();
22     }
【获取文件的Content-Type(Mime-Type)值】
 1 //获取文件的Content-Type(Mime-Type)值
 2     private String guessMimeType(String path) {
 3         //获取文件Content-Type(Mime-Type)
 4         FileNameMap fileNameMap = URLConnection.getFileNameMap();
 5         String contentTypeFor = fileNameMap.getContentTypeFor(path);
 6         if (contentTypeFor == null) {
 7             contentTypeFor = "application/octet-stream"; //默认为"application/octet-stream"
 8         }
 9         return contentTypeFor;
10     }
【增加对params为空的赋值】
1 //增加对params为空的赋值
2     private Param[] validateParam(Param[] params) {
3         if (params == null)
4             return new Param[0];
5         else return params;
6     }
【MultipartFormRequest构建】
 1 private Request buildMultipartFormRequest(String url, File[] files,
 2                                               String[] fileKeys, Param[] params) {
         params = validateParam(params);
 4         //构建MultipartBuilder
 5         MultipartBuilder builder = new MultipartBuilder()
 6                 .type(MultipartBuilder.FORM);  //设置为表单数据
 7         //[1]params的requestBody的构建
 8         for (Param param : params) {
 9             builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + param.key + "\""),
10                     RequestBody.create(null, param.value));
11         }
12         //[2]file的requestBody的构建
13         if (files != null) {
14             RequestBody fileBody = null;
15             for (int i = 0; i < files.length; i++) {
16                 File file = files[i];
17                 String fileName = file.getName();
18                 fileBody = RequestBody.create(MediaType.parse(guessMimeType(fileName)), file);
19                 //TODO 根据文件名设置contentType
20                 builder.addPart(Headers.of("Content-Disposition",
21                         "form-data; name=\"" + fileKeys[i] + "\"; filename=\"" + fileName + "\""),
22                         fileBody);
23             }
24         }
25         RequestBody requestBody = builder.build();
26         return new Request.Builder()
27                 .url(url)
28                 .post(requestBody)
29                 .build();
30     }

10. post异步文件上传

 1 /**
 2      * 异步基于post的文件上传
 3      *
 4      * @param url
 5      * @param callback
 6      * @param files
 7      * @param fileKeys
 8      * @throws IOException
 9      */
10     private void _postAsyn(String url, ResultCallback callback, File[] files, String[] fileKeys, Param... params) throws IOException {
11         Request request = buildMultipartFormRequest(url, files, fileKeys, params);
12         deliveryResult(callback, request);
13     }
14 
15     /**
16      * 异步基于post的文件上传,单文件不带参数上传
17      *
18      * @param url
19      * @param callback
20      * @param file
21      * @param fileKey
22      * @throws IOException
23      */
24     private void _postAsyn(String url, ResultCallback callback, File file, String fileKey) throws IOException {
25         Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, null);
26         deliveryResult(callback, request);
27     }
28 
29     /**
30      * 异步基于post的文件上传,单文件且携带其他form参数上传
31      *
32      * @param url
33      * @param callback
34      * @param file
35      * @param fileKey
36      * @param params
37      * @throws IOException
38      */
39     private void _postAsyn(String url, ResultCallback callback, File file, String fileKey, Param... params) throws IOException {
40         Request request = buildMultipartFormRequest(url, new File[]{file}, new String[]{fileKey}, params);
41         deliveryResult(callback, request);
42     }

 11.get异步下载文件

 1 private void _downloadAsyn(final String url, final String destFileDir, final ResultCallback callback) {
 2         final Request request = new Request.Builder()
 3                 .url(url)
 4                 .build();
 5 
 6         final Call call = mOkHttpClient.newCall(request);
 7         call.enqueue(new Callback() {
 8             @Override
 9             public void onFailure(final Request request, final IOException e) {
10                 sendFailedStringCallback(request, e, callback);
11             }
12 
13             @Override
14             public void onResponse(Response response) {
15                 InputStream is = null;
16                 byte[] buf = new byte[2048];
17                 int len = 0;
18                 FileOutputStream fos = null;
19                 try {
20                     is = response.body().byteStream(); //获取字节流数据;
21                     File file = new File(destFileDir, getFileName(url));
22                     fos = new FileOutputStream(file);
23                     while ((len = is.read(buf)) != -1) {
24                         fos.write(buf, 0, len);
25                     }
26                     fos.flush();
27                     //如果下载文件成功,第一个参数为文件的绝对路径
28                     sendSuccessResultCallback(file.getAbsolutePath(), callback);
29                 } catch (IOException e) {
30                     sendFailedStringCallback(response.request(), e, callback);
31                 } finally {
32                     try {
33                         if (is != null)
34                             is.close();
35                     } catch (IOException e) {
36                     }
37                     try {
38                         if (fos != null)
39                             fos.close();
40                     } catch (IOException e) {
41                     }
42                 }
43             }
44         });
45     }

【文件名称的获取】

【substring方法】

      返回一个字符串,该字符串是此字符串的子字符串。
      该substring从指定的{@code beginIndex}开始
      扩展到索引{@code endIndex - 1}处的字符。
      因此子串的长度是{@code endIndex-beginIndex}。

     String substring(int beginIndex, int endIndex)

     * "hamburger".substring(4, 8) returns "urge"  
       
     * "smiles".substring(1, 5) returns "mile"
     
             * * * * 
     h a m b u r g e r  len = 4; 4--7开始;"urge"
     0 1 2 3 4 5 6 7 8
    
     
       * * * * 
     s m i l e s  len = 4; 1--4开始;"mile"
     0 1 2 3 4 5

 

1 //一般的文件下载路径:
2     //  http://down.360safe.com/360Root/3 6 0 R o o t S e t u p . e x e
3     //  0                               32                            47
4     //  separatorIndex = 31; path.length=47;
5     // 32--47 len = 15; 32-47开始;
6     private String getFileName(String path) {
7         int separatorIndex = path.lastIndexOf("/"); //获取最后一个"/"的索引值;
8         return (separatorIndex < 0) ? path : path.substring(separatorIndex + 1, path.length());
9     }

12. 下载图片并加载图片

 1     /**
 2      * 下载图片并加载图片
 3      *
 4      * @param view :设置图片的ImageView
 5      * @param url :下载的图片的地址;
 6      * @param errorResId :下载失败之后的该ImageView显示的默认图片
 7      * @throws IOException
 8      */
 9     private void _displayImage(final ImageView view, final String url, final int errorResId) {
10         final Request request = new Request.Builder()
11                 .url(url)
12                 .build();
13         Call call = mOkHttpClient.newCall(request);
14         call.enqueue(new Callback() {
15             @Override
16             public void onFailure(Request request, IOException e) {
17                 setErrorResId(view, errorResId);
18             }
19 
20             @Override
21             public void onResponse(Response response) {
22                 InputStream is = null;
23                 try {
24                     is = response.body().byteStream(); //reponse 字节流,第一次请求stream是为了计算图片的inSampleSize
25                     ImageUtils.ImageSize actualImageSize = ImageUtils.getImageSize(is);
26                     ImageUtils.ImageSize imageViewSize = ImageUtils.getImageViewSize(view);
27                     //计算bitmpa的inSample比例
28                     int inSampleSize = ImageUtils.calculateInSampleSize(actualImageSize, imageViewSize);
29                     try {
30                         is.reset();
31                     } catch (IOException e) {
32                         response = _getAsyn(url);
33                         is = response.body().byteStream(); //reponse 字节流,第2次请求stream为了加载图片;
34                     }
35 
36                     BitmapFactory.Options ops = new BitmapFactory.Options();
37                     ops.inJustDecodeBounds = false; //计算结束之后设置为false,在ImageUtils时设置为了true;
38                     ops.inSampleSize = inSampleSize;
39                     final Bitmap bm = BitmapFactory.decodeStream(is, null, ops);
40                     //网络请求为耗时操作,必须放在子线程操作,更新ui要放在主线程;
41                     mDelivery.post(new Runnable() {
42                         @Override
43                         public void run() {
44                             view.setImageBitmap(bm);
45                         }
46                     });
47                 } catch (Exception e) {
48                     setErrorResId(view, errorResId);
49                 } finally {
50                     if (is != null) try {
51                         is.close();
52                     } catch (IOException e) {
53                         e.printStackTrace();
54                     }
55                 }
56             }
57         });
58     }

【根据InputStream获取图片实际的宽度和高度】

 1 /**
 2      * 根据InputStream获取图片实际的宽度和高度
 3      *
 4      * @param imageStream
 5      * @return
 6      */
 7     public static ImageSize getImageSize(InputStream imageStream) {
 8         BitmapFactory.Options options = new BitmapFactory.Options();
 9         options.inJustDecodeBounds = true;
10         BitmapFactory.decodeStream(imageStream, null, options);
11         return new ImageSize(options.outWidth, options.outHeight);
12     }

【根据ImageView获适当的压缩的宽和高】

 1 /**
 2      * 根据ImageView获适当的压缩的宽和高
 3      *
 4      * @param view
 5      * @return
 6      */
 7     public static ImageSize getImageViewSize(View view) {
 8 
 9         ImageSize imageSize = new ImageSize();
10 
11         imageSize.width = getExpectWidth(view);
12         imageSize.height = getExpectHeight(view);
13 
14         return imageSize;
15     }
16 
17     /**
18      * 根据view获得期望的高度
19      *
20      * @param view
21      * @return
22      */
23     private static int getExpectHeight(View view) {
24 
25         int height = 0;
26         if (view == null) return 0;
27 
28         final ViewGroup.LayoutParams params = view.getLayoutParams();
29         
30         //如果是WRAP_CONTENT,此时图片还没加载,getWidth根本无效
31         if (params != null && params.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
32             height = view.getWidth(); // 获得实际的宽度
33         }
34         
35         if (height <= 0 && params != null) {
36             height = params.height; // 获得布局文件中的声明的高度
37         }
38 
39         if (height <= 0) {
40             height = getImageViewFieldValue(view, "mMaxHeight");// 获得设置的最大的宽度
41         }
42 
43         //如果宽度还是没有获取到,使用屏幕的高度
44         if (height <= 0) {
45             DisplayMetrics displayMetrics = view.getContext().getResources()
46                     .getDisplayMetrics();
47             height = displayMetrics.heightPixels;
48         }
49 
50         return height;
51     }
52 
53     /**
54      * 根据view获得期望的宽度
55      *
56      * @param view
57      * @return
58      */
59     private static int getExpectWidth(View view) {
60         int width = 0;
61         if (view == null) return 0;
62 
63         final ViewGroup.LayoutParams params = view.getLayoutParams();
64         //如果是WRAP_CONTENT,此时图片还没加载,getWidth根本无效
65         if (params != null && params.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
66             width = view.getWidth(); // 获得实际的宽度
67         }
68         if (width <= 0 && params != null) {
69             width = params.width; // 获得布局文件中的声明的宽度
70         }
71 
72         if (width <= 0){
75             width = getImageViewFieldValue(view, "mMaxWidth");// 获得设置的最大的宽度
76         }
77         //如果宽度还是没有获取到,使用屏幕的宽度
78         if (width <= 0){
81             DisplayMetrics displayMetrics = view.getContext().getResources()
82                     .getDisplayMetrics();
83             width = displayMetrics.widthPixels;
84         }
85 
86         return width;
87     }

 

 

 

posted @ 2018-07-02 16:36  OzTaking  阅读(432)  评论(0)    收藏  举报