• Okhttp源码解析(零)——Okhttp使用


    一.概述

    • 一般的get请求和post请求
    • 基于HTTP的文件上传
    • 基于HTTP的文件下载

    官方github项目

    支持:android2.3以上,JDK1.7以上

    引入:

    对于我使用的是okhttp3.2版本

    在AS下,在gradle文件下

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

    不过对于低版本的,例如2.4版本的,还需要依赖一个IO包,因此总共需要引入两个库

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

    二.使用

    参考OkHttp使用教程

    参考洪洋大神博客

    (一)HTTP get请求

    get请求为最常用的S/C模式中常用的调用接口方法,与post请求不同的是get方式直接将参数暴露在url上,

    例子:url=http://host:post/login.json?email=609931480@qq.com&password=123456

    /**
         * 判断当前手机网络是否可用
         *
         * @return true:是,false:否
         */
        public static boolean isNetWorkAvailable(Context context) {
            ConnectivityManager cm =
                    (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
            if (cm != null) {
                NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
                return activeNetwork != null && activeNetwork.isAvailable();
            } else {
                return false;
            }
        }
    
        private OkHttpClient mOkHttpClient;
        public OkHttpClient getClient() {
            if (mOkHttpClient == null) {
                mOkHttpClient = new OkHttpClient.Builder()
                        .connectTimeout(90, TimeUnit.SECONDS)
                        .writeTimeout(90, TimeUnit.SECONDS)
                        .readTimeout(90, TimeUnit.SECONDS)
                        .build();
            }
            return mOkHttpClient;
        }
        /**
         * http get方式从后台api获取数据
         *
         * @param url url地址
         * @return 结果字符串
         */
        public String getDataFromApi(String url) {
            Request request = new Request.Builder()
                    .url(url)
                    .build();
            String ret;
            if (isNetWorkAvailable(getApplicationContext())) {//判断当前网络状态
                try {
                    Response response = null;
                    response = getClient().newCall(request).execute();
                    if (response != null) {
                        if (response.isSuccessful()) {
                            ret = response.body().string();
                        } else {
                            ret = String.format("{ 'code':%s }", response.code());//http
                        }
                    } else {
                        ret = String.format("{ 'code':%s }", 700);//http请求异常
                    }
                    return ret;
                } catch (Exception ex) {
                    ret = String.format("{ 'code':%s }", 700);//http请求异常
                }
            } else {
                ret = String.format("{ 'code':%s }", 600);//网络不可用
            }
            return ret;
        }

    重点在我写的getDataFromApi()方法

    1.首先构造Request对象,Request.Builder()的源码:

    public Builder() {
          this.method = "GET";
          this.headers = new Headers.Builder();
        }

    只是设置了两个参数而已,“GET”方式是默认的方式。因此简单分析可知我们可以通过Request.Builder()设置其他参数例如,header(为HTTP的请求报头,一般调用接口情况下设置类型为User-Agent)

    .url(url)
                    .addHeader("User-Agent", String.format("Fitmix/%s/%s (Android %s)",
                            ApiUtils.getApkVersionCode(), ApiUtils.getApkVersionName(),
                            ApiUtils.getPhoneSdk() != null ? ApiUtils.getPhoneSdk() : ""))//如Fitmix/44/2.3.0 (Android 4.0)"
    View Code

    、method等

    2.通过request对象构造出Call对象,就是将请求分装成对象,任务的话,执行:execute(),取消:cancel()。

    3.任务分同步和异步之分:例子中代码execute()表示同步,即线程阻塞方式,虽然是阻塞方式,但是对于接口访问,我们仍使用线程阻塞方式,因为毕竟接口访问速度很快。

    适用异步方式的情况:允许相同工作同步执行

    适用阻塞方式的情况:线程池控制,线程一个一个执行完成才能下一个,较稳定。

    如果我们想要使用异步的方式执行请求。调用:call.enquene,就是将call加入调度队列,代码如下:

    Callback callback = new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
    
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
    
                }
            };
    if (request == null)
                return;
            getClient().newCall(request).enqueue(responseCallback);

    4.获取访问结果。

    无论是同步还是异步,返回结果都再Response变量中,通过response.body().toString()获取返回的JSon字符串。response.code()获取返回码

    (二)HTTP post请求

    与Get方式不同的是没有将请求的参数暴露在url上,而是将参数作为请求的参数存入

    例子:url=http://host:post/login.json

    提交json:

    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    JSONObject jsonObject = new JSONObject();
    jsonObject.put("email", 609931480@qq.com);
    jsonObject.put("password", 123456);
    OkHttpClient mOkHttpClient = new OkHttpClient();
    RequestBody body = RequestBody.create(JSON, jsonObject .toString());
    //创建一个Request
    final Request request = new Request.Builder()
    .url(url)
    .post(body)
    .build();

    提交键值对:

    OkHttpClient client = new OkHttpClient();
    String post(String url, String json) throws IOException {
     
         RequestBody formBody = new FormEncodingBuilder()
        .add("platform", "android")
        .add("name", "bug")
        .add("subject", "XXXXXXXXXXXXXXX")
        .build();
     
          Request request = new Request.Builder()
          .url(url)
          .post(body)
          .build();
     
          Response response = client.newCall(request).execute();
        if (response.isSuccessful()) {
            return response.body().string();
        } else {
            throw new IOException("Unexpected code " + response);
        }
    }

    以上就是将url的参数封装成键值对存储在jsonObject对象中,构建RequestBody,最后完成在请求的Request的构建

    二.HTTP的文件上传

    String url = "http://www.baidu.com";
        private String uploadFile(){
            File localFile = new File("xxx");//要上传的文件
            String uploadFileTag = "png";//上传的文件类型,自定义的
            // 2.创建 OkHttp 对象相关
            OkHttpClient client = new OkHttpClient();
            // 5.设置上传http请求头
            Request request;
            if (localFile.exists()) {
                String sString = "form-data; name="" + uploadFileTag + "";filename=""
                        + localFile.getName() + """;
    
                RequestBody requestBody = new MultipartBody.Builder()
                        .setType(MultipartBody.FORM)
                        .addFormDataPart(uploadFileTag, localFile.getName(),
                                RequestBody.create(null, localFile))
                        .addPart(
                                Headers.of("Content-Disposition", sString),
                                RequestBody.create(
                                        MediaType.parse("application/octet-stream"),
                                        localFile)).build();
                request = new Request.Builder().url(url)
                        .post(requestBody).build();
            }else{
                return "";
            }
            // 5.上传操作
            try {
                Response response = client.newCall(request).execute();
                if (response != null) {
                    if (response.isSuccessful()) {
                        //成功
                    } else {
                        //失败
                    }
                    return "{code:" + response.code() + "}";
                }
            } catch (Exception e) {
            }
            return null;
        }

    上述代码向服务器传递了一个文件,使用了MultipartBody,调用addPart()添加文件。就是提交表单消息,POST请求

    三.文件下载

    String url = "http://www.baidu.com";
        File localTempFile = new File("xxx" + ".tmp");//下载到存放的位置
        public String downloadFile() throws Exception {
            // 1.先检查是否有之前的临时文件
            String httpRange = "";
            if (localTempFile.exists()) {
                httpRange = "bytes=" + localTempFile.length() + "-";
            }
            // 2.创建 OkHttp 对象相关
            OkHttpClient client = new OkHttpClient();
            // 3.检测网络状况
            // 4.如果有临时文件,则在下载的头中添加下载区域,即断点续传
            Request request;
            if (!TextUtils.isEmpty(httpRange)) {
                request = new Request.Builder().url(url).header("Range", httpRange)
                        .build();
            } else {
                request = new Request.Builder().url(url)
                        .build();
            }
            Call call = client.newCall(request);
            try {
                bytes2File(call);
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
            return null;
        }
    
        /**
         * 将下载的数据存到本地文件
         *
         * @param call OkHttp的Call对象
         * @throws IOException 下载的异常
         */
        private void bytes2File(Call call) throws IOException {
            //设置输出流.
            OutputStream outPutStream;
            //1.检测是否支持断点续传
            Response response = call.execute();
            ResponseBody responseBody = response.body();
            String responseRange = response.headers().get("Content-Range");
            if (responseRange == null || !responseRange.contains(Long.toString(localTempFile.length()))) {
                //最后的标记为 true 表示下载的数据可以从上一次的位置写入,否则会清空文件数据.
                outPutStream = new FileOutputStream(localTempFile, false);
                // 文件不存在
            } else {
                outPutStream = new FileOutputStream(localTempFile, true);
                // 文件存在,追加下载
            }
            int length;
            byte[] buffer = new byte[1024];//设置缓存大小
            //4.开始写入文件
            InputStream inputStream = responseBody.byteStream();
            while ((length = inputStream.read(buffer)) != -1) {
                //写文件
                outPutStream.write(buffer, 0, length);
            }
            //清空缓冲区
            outPutStream.flush();
            outPutStream.close();
            inputStream.close();
            responseBody.close();
        }

    其中重要的就是通过Call.execute()执行访问,返回访问的Response的回复内容,response.body().byteStream()返回输入流,通过输入流不断在文件上写。同理如果下载图片,一样道理,只不过通过返回的inputStream输入流decode出Bitmap。

     三.封装

    import android.text.TextUtils;
    
    import com.fitmix.sdk.common.Logger;
    
    import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    import okhttp3.Call;
    import okhttp3.Callback;
    import okhttp3.Headers;
    import okhttp3.MediaType;
    import okhttp3.MultipartBody;
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.RequestBody;
    import okhttp3.Response;
    
    public class OkHttpUtil {
    
        /** http MIME json头*/
        public static final MediaType JSON
                = MediaType.parse("application/json; charset=utf-8");
    
        private static OkHttpUtil instance;
        private OkHttpClient mOkHttpClient;
    
        public static OkHttpUtil getInstance() {
            if (instance == null) {
                instance = new OkHttpUtil();
            }
            return instance;
        }
    
        public OkHttpClient getClient() {
            if (mOkHttpClient == null) {
                mOkHttpClient = new OkHttpClient.Builder()
                        .connectTimeout(90, TimeUnit.SECONDS)
                        .writeTimeout(90, TimeUnit.SECONDS)
                        .readTimeout(90, TimeUnit.SECONDS)
                        .build();
            }
            return mOkHttpClient;
        }
    
        /**
         * 阻塞方式http请求。
         *
         * @param request http请求实体
         *
         * @return http响应实体
         */
        public Response execute(Request request) {
            if (request == null)
                return null;
            Response response = null;
            try {
                response = getClient().newCall(request).execute();
            } catch (IOException e) {
                e.printStackTrace();
                Logger.e(Logger.DATA_FLOW_TAG,"execute error:"+e.getMessage());
            }
            if (response == null)
                return null;
            return response;
        }
    
        /**
         * 异步方式http请求。
         *
         * @param request http请求实体
         * @param responseCallback http请求回调
         *
         */
        public void enqueue(Request request, Callback responseCallback) {
            if (request == null)
                return;
            getClient().newCall(request).enqueue(responseCallback);
        }
    
        /**
         * 异步方式http请求,且不在意返回结果(实现空callback)
         *
         * @param request http请求实体
         */
        public void enqueue(Request request) {
            if (request == null)
                return;
            Callback callback = new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
    
                }
    
                @Override
                public void onResponse(Call call, Response response) throws IOException {
    
                }
            };
            enqueue(request, callback);
        }
    
        /**
         * 阻塞方式http请求。
         *
         * @param url http请求url
         *
         * @return http响应实体
         * */
        public Response getResponseFromServer(String url) {
            if (url == null || url.isEmpty())
                return null;
            Request request = new Request.Builder().url(url)
                    .addHeader("User-Agent", String.format("Fitmix/%s/%s (Android %s)",
                            ApiUtils.getApkVersionCode(), ApiUtils.getApkVersionName(),
                            ApiUtils.getPhoneSdk() != null ? ApiUtils.getPhoneSdk() : ""))//如Fitmix/44/2.3.0 (Android 4.0)"
                    .build();
            return execute(request);
        }
    
        /**
         * 阻塞方式http请求
         * @param url http请求url
         *
         * @return 字符串结果
         */
        public String getStringFromServer(String url) {
            Response response = getResponseFromServer(url);
            if (response == null)
                return null;
            if (response.isSuccessful()) {
                String responseUrl = null;
                try {
                    responseUrl = response.body().string();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return responseUrl;
            }
            return "{code:" + response.code() + "}";
    
        }
    
        /**
         * 上传单个文件请求
         *
         * @param url http请求url
         * @param sFile 要上传的文件的本地绝对路径
         * @param sTag 要上传的文件标签
         *
         * @return
         * */
        public String uploadToServer(String url, String sFile, String sTag) {
            if (url == null || url.isEmpty())
                return null;
            if (sFile == null || sFile.isEmpty())
                return null;
            if (sTag == null || sTag.isEmpty())
                return null;
    
            File file = new File(sFile);
            Request request;
            if (file.exists()) {
                String sString = "form-data; name="" + sTag + "";filename=""
                        + file.getName() + """;
    
                RequestBody requestBody = new MultipartBody.Builder()
                        .setType(MultipartBody.FORM)
                        .addFormDataPart(sTag, file.getName(),
                                RequestBody.create(null, file))
                        .addPart(
                                Headers.of("Content-Disposition", sString),
                                RequestBody.create(
                                        MediaType.parse("application/octet-stream"),
                                        file)).build();
                request = new Request.Builder().url(url)
                        .addHeader("User-Agent", String.format("Fitmix/%s/%s (Android %s)",
                                ApiUtils.getApkVersionCode(), ApiUtils.getApkVersionName(),
                                ApiUtils.getPhoneSdk() != null ? ApiUtils.getPhoneSdk() : ""))//如Fitmix/44/2.3.0 (Android 4.0)"
                        .post(requestBody).build();
            } else {
                RequestBody requestBody = new MultipartBody.Builder()
                        .setType(MultipartBody.FORM)
                        .addPart(Headers.of("Content-Disposition","form-data; name="" +  sTag + """),
                                    RequestBody.create(null, ""))
                        .build();
                request = new Request.Builder().url(url)
                        .addHeader("User-Agent", String.format("Fitmix/%s/%s (Android %s)",
                                ApiUtils.getApkVersionCode(), ApiUtils.getApkVersionName(),
                                ApiUtils.getPhoneSdk() != null ? ApiUtils.getPhoneSdk() : ""))//如Fitmix/44/2.3.0 (Android 4.0)"
                        .post(requestBody).build();
            }
            try {
                OkHttpClient client = new OkHttpClient();
                Response response  = client.newCall(request).execute();
                if (response == null)
                    return null;
                if (response.isSuccessful()) {
                    String responseUrl = null;
                    try {
                        responseUrl = response.body().string();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return responseUrl;
                }
                return "{code:" + response.code() + "}";
            }catch (IOException e){
                e.printStackTrace();
            }catch (Exception ex){
                ex.printStackTrace();
            }
            return "{code:" + ApiUtils.HTTP_REQUEST_EXCEPTION + "}";
        }
    
        /**
         * 上传多个文件请求
         *
         * @param url http请求url
         * @param files 要上传的文件的本地绝对路径集合
         * @param tags 要上传的文件标签集合
         *
         * @return
         * */
        public String uploadToServer(String url, List<String> files, List<String> tags) {
            if (url == null || url.isEmpty())
                return null;
            if(files == null || tags == null)return null;
            if(files.size() != tags.size())return null;
    
            boolean bExist = false;
            List<File> listFiles = new ArrayList<>();
            for(String fileName : files){
                if(TextUtils.isEmpty(fileName))
                    continue;
                File file = new File(fileName);
                if(file.exists()){
                    bExist = true;
                }else{
                    file = null;
                }
                listFiles.add(file);
            }
    
            Request request;
            if (bExist) {
                MultipartBody.Builder builder = new MultipartBody.Builder();
                for(int i = 0; i < files.size(); i++) {
                    if(listFiles.get(i) == null)continue;
                    String sString = "form-data;name="" + tags.get(i) + "";filename=""
                            + listFiles.get(i).getName() + """;
                    builder.addPart(
                            Headers.of("Content-Disposition", sString),
                            RequestBody.create(
                                    MediaType.parse("application/octet-stream"),
                                    listFiles.get(i)));
                }
                RequestBody requestBody = builder.setType(MultipartBody.FORM).build();
                request = new Request.Builder().url(url)
                        .addHeader("User-Agent", String.format("Fitmix/%s/%s (Android %s)",
                                ApiUtils.getApkVersionCode(),ApiUtils.getApkVersionName(),
                                ApiUtils.getPhoneSdk() != null ? ApiUtils.getPhoneSdk():""))//如Fitmix/44/2.3.0 (Android 4.0)"
                        .post(requestBody)
                        .build();
            } else {
                RequestBody requestBody = new MultipartBody.Builder()
                        .setType(MultipartBody.FORM)
                        .addPart(Headers.of("Content-Disposition","form-data; name="" +  tags.get(0) + """),
                                    RequestBody.create(null, ""))
                        .build();
                request = new Request.Builder().url(url)
                        .addHeader("User-Agent", String.format("Fitmix/%s/%s (Android %s)",
                                ApiUtils.getApkVersionCode(),ApiUtils.getApkVersionName(),
                                ApiUtils.getPhoneSdk() != null ? ApiUtils.getPhoneSdk():""))//如Fitmix/44/2.3.0 (Android 4.0)"
                        .post(requestBody).build();
            }
    
            try {
                OkHttpClient client = new OkHttpClient();
                Response response  = client.newCall(request).execute();
    
                if (response != null && response.isSuccessful()) {
                    String responseUrl;
                    try {
                        responseUrl = response.body().string();
                        if(responseUrl != null){
    //                        Logger.i(Logger.DATA_FLOW_TAG,"uploadToServer responseUrl:"+responseUrl);
                            return responseUrl;
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        if(!TextUtils.isEmpty(e.getMessage())) {
                            Logger.e(Logger.DATA_FLOW_TAG, "uploadToServer error:" + e.getMessage());
                        }
                    }
                    return "{code:" + response.code() + "}";
                }
            }catch (IOException e){
                e.printStackTrace();
            }catch (Exception ex){
                ex.printStackTrace();
            }
            return "{code:" + ApiUtils.HTTP_REQUEST_EXCEPTION + "}";
        }
    View Code
  • 相关阅读:
    SortedList的使用示例
    oracle 查询
    sql group by统计
    删除隐藏盘符的隐藏共享,打开隐藏盘符
    【C#】Entity Framework 增删改查和事务操作
    CSS rem长度单位
    HTML 页面meta标签
    VUE 生成二维码(qrcodejs)
    VUE 密码验证与提示
    JavaScript 加解密库(crypto-js)
  • 原文地址:https://www.cnblogs.com/could-deng/p/6733721.html
Copyright © 2020-2023  润新知