目 录CONTENT

文章目录

全网最详细的OkHttp 使用总结

小王同学
2024-08-06 / 0 评论 / 5 点赞 / 1248 阅读 / 0 字

全网最详细的OkHttp 使用总结

前言

本篇文章只是对okhttp的使用总结

想了解源码的同学可以看我的另一篇文章:终于懂了系列之OKHTTP源码解析

文章较长,建议收藏观看~

觉得有用的同学帮忙点个赞吧,谢谢!

一.OkHttp简介

Okhttp是一个第三方类库,用于android中请求网络。

这是一个开源项目,是安卓端最火热的轻量级框架

由移动支付Square公司贡献(该公司还贡献了Picasso和LeakCanary) 。

用于替代HttpUrlConnection和Apache HttpClient(android API23 里已移除HttpClient)。

okhttp有自己的官网,官网网址:OKHttp官网

如果想了解原码可以在github上下载,地址是:https://github.com/square/okhttp

在AndroidStudio中使用不需要下载jar包,直接添加依赖即可: 

implementation ‘com.squareup.okhttp3:okhttp:4.8.1’

下面对以OKHttp3来详细介绍OKHttp的使用方法

二.get请求的使用方法

使用OKHttp进行网络请求支持两种方式,一种是同步请求,一种是异步请求。下面分情况进行介绍。

1.get的同步请求

对于同步请求在请求时需要开启子线程,请求成功后需要跳转到UI线程修改UI。 
使用示例如下:

public void getDatasync(){
    //此时的代码执行在子线程,修改UI的操作请使用handler跳转到UI线程。
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //创建OkHttpClient对象
                OkHttpClient client = new OkHttpClient();
                Request request = new Request.Builder()
                        //请求接口。如果需要传参拼接到接口后面。
                        .url("http://www.baidu.com")
                        //创建Request 对象
                        .build();
                Response response = null;
                //得到Response 对象
                response = client.newCall(request).execute();
                if (response.isSuccessful()) {
                Log.d("kwwl","response.code()=="+response.code());
                Log.d("kwwl","response.message()=="+response.message());
                Log.d("kwwl","res=="+response.body().string());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }).start();
}

此时打印结果如下: 

response.code()==200; 
response.message()==OK; 
res=={“code”:200,”message”:success};

注意事项: 
1,Response.code是http响应行中的code,如果访问成功则返回200.这个不是服务器设置的,而是http协议中自带的。res中的code才是服务器设置的。注意二者的区别。 
2,response.body().string()本质是输入流的读操作,所以它还是网络请求的一部分,所以这行代码必须放在子线程。 
3,response.body().string()只能调用一次,在第一次时有返回值,第二次再调用时将会返回null。原因是:response.body().string()的本质是输入流的读操作,必须有服务器的输出流的写操作时客户端的读操作才能得到数据。而服务器的写操作只执行一次,所以客户端的读操作也只能执行一次,第二次将返回null。

2.get的异步请求

这种方式不用再次开启子线程,但回调方法是执行在子线程中,所以在更新UI时还要跳转到UI线程中。 
使用示例如下:

private void getDataAsync() {
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
            .url("http://www.baidu.com")
            .build();
    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
        }
        @Override
        public void onResponse(Call call, Response response) throws IOException {
            //回调的方法执行在子线程。
            if(response.isSuccessful()){
                Log.d("kwwl","获取数据成功了");
                Log.d("kwwl","response.code()=="+response.code());
                Log.d("kwwl","response.body().string()=="+response.body().string());
            }
        }
    });
}

异步请求的打印结果与注意事项与同步请求时相同。最大的不同点就是异步请求不需要开启子线程,enqueue方法会自动将网络请求部分放入子线程中执行。

注意事项: 
1,回调接口的onFailure方法和onResponse执行在子线程。 
2,response.body().string()方法也必须放在子线程中。当执行这行代码得到结果后,再跳转到UI线程修改UI。

三.post请求的使用方法

Post请求也分同步和异步两种方式,同步与异步的区别和get方法类似,所以此时只讲解post异步请求的使用方法。 
使用示例如下:

private void postDataWithParame() {
    //创建OkHttpClient对象。
    OkHttpClient client = new OkHttpClient();
    //创建表单请求体
    FormBody.Builder formBody = new FormBody.Builder();
    //传递键值对参数
    formBody.add("username","zhangsan");
    //创建Request 对象。
    Request request = new Request.Builder()
            .url("http://www.baidu.com")
             //传递请求体
            .post(formBody.build())
            .build();
    client.newCall(request).enqueue(new Callback() {。。。});//回调方法的使用与get异步请求相同,此时略。
}

看完代码我们会发现:post请求中并没有设置请求方式为POST,回忆在get请求中也没有设置请求方式为GET,那么是怎么区分请求方式的呢?

重点是Request.Builder类的post方法,在Request.Builder对象创建最初默认是get请求,所以在get请求中不需要设置请求方式,当调用post方法时把请求方式修改为POST。所以此时为POST请求。

四.POST请求传递参数的方法总结

在post请求使用方法中讲了一种传递参数的方法,就是创建表单请求体对象,然后把表单请求体对象作为post方法的参数。post请求传递参数的方法还有很多种,但都是通过post方法传递的。下面我们看一下Request.Builder类的post方法的声明:

public Builder post(RequestBody body)

由方法的声明可以看出,post方法接收的参数是RequestBody 对象,所以只要是RequestBody 类以及子类对象都可以当作参数进行传递。FormBody就是RequestBody 的一个子类对象。

1.使用FormBody传递键值对参数

这种方式用来上传String类型的键值对 

private void postDataWithParame() {
    //创建OkHttpClient对象。
    OkHttpClient client = new OkHttpClient();
    //创建表单请求体
    FormBody.Builder formBody = new FormBody.Builder();
    //传递键值对参数
    formBody.add("username","zhangsan");
    //创建Request 对象。
    Request request = new Request.Builder()
            .url("http://www.baidu.com")
             //传递请求体
            .post(formBody.build())
            .build();
    client.newCall(request).enqueue(new Callback() {。。。});//此处省略回调方法。
}

2.使用RequestBody传递Json或File对象

RequestBody是抽象类,故不能直接使用,但是他有静态方法create,使用这个方法可以得到RequestBody对象。

这种方式可以上传Json对象或File对象。 
上传json对象使用示例如下:

import okhttp3.*;
import org.json.JSONObject;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class JsonUploader {
    // 定义 JSON 媒体类型
    private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
    private final OkHttpClient client;

    // 构造方法,初始化 OkHttpClient
    public JsonUploader() {
        client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS) // 设置连接超时时间为 30 秒
                .readTimeout(30, TimeUnit.SECONDS)    // 设置读取超时时间为 30 秒
                .writeTimeout(30, TimeUnit.SECONDS)   // 设置写入超时时间为 30 秒
                .retryOnConnectionFailure(true)       // 设置连接失败时重试
                .build();
    }

    // 上传 JSON 数据的方法
    public void uploadJson(String url, JSONObject json, UploadCallback callback) {
        // 创建请求体,将 JSON 对象转换为字符串并创建 RequestBody
        RequestBody body = RequestBody.create(JSON, json.toString());

        // 构建请求对象
        Request request = new Request.Builder()
                .url(url)  // 设置请求 URL
                .post(body)  // 设置请求方法为 POST,并附带请求体
                .build();

        // 异步执行请求
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 请求失败时调用 callback 的 onFailure 方法
                if (callback != null) {
                    callback.onFailure(e);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // 请求成功时调用 callback 的 onSuccess 方法
                if (callback != null) {
                    if (response.isSuccessful()) {
                        callback.onSuccess(response);
                    } else {
                        // 请求失败时调用 callback 的 onFailure 方法,并传递错误信息
                        callback.onFailure(new IOException("Unexpected code " + response));
                    }
                }
            }
        });
    }

    // 上传回调接口
    public interface UploadCallback {
        void onSuccess(Response response);  // 上传成功时调用

        void onFailure(IOException e);  // 上传失败时调用
    }

    // 主方法,用于测试上传功能
    public static void main(String[] args) {
        JsonUploader uploader = new JsonUploader();
        String uploadUrl = "https://example.com/upload";  // 设置上传 URL

        // 创建一个 JSON 对象
        JSONObject json = new JSONObject();
        try {
            json.put("client", "Android");
            json.put("uid", "1061");
            json.put("token", "1911173227afe098143caf4d315a436d");
            json.put("uuid", "A000005566DA77");
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 上传 JSON 数据,并定义上传回调
        uploader.uploadJson(uploadUrl, json, new UploadCallback() {
            @Override
            public void onSuccess(Response response) {
                // 上传成功时处理逻辑
                System.out.println("JSON uploaded successfully: " + response.message());
            }

            @Override
            public void onFailure(IOException e) {
                // 上传失败时处理逻辑
                e.printStackTrace();
            }
        });
    }
}

上传File对象使用示例如下:

import okhttp3.*;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class FileUploader {
    // 定义文件媒体类型
    private static final MediaType MEDIA_TYPE = MediaType.parse("application/octet-stream");
    private final OkHttpClient client;

    // 构造方法,初始化 OkHttpClient
    public FileUploader() {
        client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS) // 设置连接超时时间为 30 秒
                .readTimeout(30, TimeUnit.SECONDS)    // 设置读取超时时间为 30 秒
                .writeTimeout(30, TimeUnit.SECONDS)   // 设置写入超时时间为 30 秒
                .retryOnConnectionFailure(true)       // 设置连接失败时重试
                .build();
    }

    // 上传文件的方法
    public void uploadFile(String url, File file, UploadCallback callback) {
        // 创建请求体,将文件转换为 RequestBody
        RequestBody body = RequestBody.create(MEDIA_TYPE, file);

        // 构建请求对象
        Request request = new Request.Builder()
                .url(url)  // 设置请求 URL
                .post(body)  // 设置请求方法为 POST,并附带请求体
                .build();

        // 异步执行请求
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 请求失败时调用 callback 的 onFailure 方法
                if (callback != null) {
                    callback.onFailure(e);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                // 请求成功时调用 callback 的 onSuccess 方法
                if (callback != null) {
                    if (response.isSuccessful()) {
                        callback.onSuccess(response);
                    } else {
                        // 请求失败时调用 callback 的 onFailure 方法,并传递错误信息
                        callback.onFailure(new IOException("Unexpected code " + response));
                    }
                }
            }
        });
    }

    // 上传回调接口
    public interface UploadCallback {
        void onSuccess(Response response);  // 上传成功时调用

        void onFailure(IOException e);  // 上传失败时调用
    }

    // 主方法,用于测试上传功能
    public static void main(String[] args) {
        FileUploader uploader = new FileUploader();
        String uploadUrl = "https://example.com/upload";  // 设置上传 URL
        File file = new File("path/to/your/file.txt");    // 设置待上传的文件路径

        // 上传文件,并定义上传回调
        uploader.uploadFile(uploadUrl, file, new UploadCallback() {
            @Override
            public void onSuccess(Response response) {
                // 上传成功时处理逻辑
                System.out.println("File uploaded successfully: " + response.message());
            }

            @Override
            public void onFailure(IOException e) {
                // 上传失败时处理逻辑
                e.printStackTrace();
            }
        });
    }
}

3.使用MultipartBody同时传递键值对参数和File对象

这个字面意思是多重的body。我们知道FromBody传递的是字符串型的键值对,RequestBody传递的是多媒体,那么如果我们想二者都传递怎么办?此时就需要使用MultipartBody类。 
使用示例如下:

public class FileUploader {
    private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
    private final OkHttpClient client;

    public FileUploader() {
        client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .build();
    }

    public void uploadFile(Context context, String fileName, String url, HashMap<String, String> params, UploadCallback callback) {
        File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
        if (!file.exists()) {
            if (callback != null) {
                callback.onFailure(new IOException("File not found: " + file.getAbsolutePath()));
            }
            return;
        }

        RequestBody fileBody = MultipartBody.create(MEDIA_TYPE_PNG, file);
        MultipartBody.Builder multiBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(), fileBody);

        // Add additional parameters
        if (params != null && !params.isEmpty()) {
            for (String key : params.keySet()) {
                multiBuilder.addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"" + key + "\""),
                        RequestBody.create(null, params.get(key)));
            }
        }

        RequestBody multiBody = multiBuilder.build();
        Request request = new Request.Builder().url(url).post(multiBody).build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                if (callback != null) {
                    callback.onFailure(e);
                }
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (callback != null) {
                    if (response.isSuccessful()) {
                        callback.onSuccess(response);
                    } else {
                        callback.onFailure(new IOException("Unexpected code " + response));
                    }
                }
            }
        });
    }

    public interface UploadCallback {
        void onSuccess(Response response);

        void onFailure(IOException e);
    }
}

4.自定义RequestBody实现流的上传

在上面的分析中我们知道,只要是RequestBody类以及子类都可以作为post方法的参数,下面我们就自定义一个类,继承RequestBody,实现流的上传。 
使用示例如下: 
首先创建一个RequestBody类的子类对象:

RequestBody body = new RequestBody() {
    @Override
    public MediaType contentType() {
        return null;
    }
  
    @Override
    public void writeTo(BufferedSink sink) throws IOException {//重写writeTo方法
        FileInputStream fio= new FileInputStream(new File("fileName"));
        byte[] buffer = new byte[1024*8];
        if(fio.read(buffer) != -1){
             sink.write(buffer);
        }
    }
};

然后使用body对象:

OkHttpClient client = new OkHttpClient();//创建OkHttpClient对象。
Request request = new Request.Builder()
        .url("http://www.baidu.com")
        .post(body)
        .build();
client.newCall(request).enqueue(new Callback() {。。。});

以上代码的与众不同就是body对象,这个body对象重写了write方法,里面有个sink对象。这个是OKio包中的输出流,有write方法。使用这个方法我们可以实现上传流的功能。

使用RequestBody上传文件时,并没有实现断点续传的功能。我可以使用这种方法结合RandomAccessFile类实现断点续传的功能。

五,设置请求头

OKHttp中设置请求头特别简单,在创建request对象时调用一个方法即可。 
使用示例如下:

Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .header("User-Agent", "OkHttp Headers.java")
                .addHeader("token", "myToken")
                .build();

其他部分代码略。

六,下载文件

在OKHttp中并没有提供下载文件的功能,但是在Response中可以获取流对象,有了流对象我们就可以自己实现文件的下载。代码如下: 
这段代码写在回调接口CallBack的onResponse方法中:

import okhttp3.*;
import okio.BufferedSink;
import okio.Okio;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class FileDownloader {
    private static final int MAX_RETRY = 3; // 最大重试次数为 3 次
    private final OkHttpClient client; // OkHttpClient 客户端
    private final ExecutorService executorService; // 线程池,用于执行下载任务

    // 构造方法,初始化 OkHttpClient 和线程池
    public FileDownloader() {
        client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS) // 设置连接超时时间为 30 秒
                .readTimeout(30, TimeUnit.SECONDS)    // 设置读取超时时间为 30 秒
                .writeTimeout(30, TimeUnit.SECONDS)   // 设置写入超时时间为 30 秒
                .retryOnConnectionFailure(true)       // 设置连接失败时重试
                .build();
        executorService = Executors.newSingleThreadExecutor(); // 初始化单线程的线程池
    }

    // 下载文件的方法
    public void download(String url, File destFile, DownloadCallback callback) {
        // 提交下载任务到线程池执行
        executorService.submit(() -> {
            long downloadedBytes = 0; // 已下载的字节数
            if (destFile.exists()) {  // 如果目标文件已经存在,获取其大小
                downloadedBytes = destFile.length();
            }

            // 构建请求对象,添加断点续传的请求头
            Request request = new Request.Builder()
                    .url(url) 
                    .addHeader("Range", "bytes=" + downloadedBytes + "-")
                    .build();

            int retryCount = 0; // 当前重试次数
            boolean success = false; // 下载是否成功

            // 重试循环,直到达到最大重试次数或成功下载
            while (retryCount < MAX_RETRY && !success) {
                Call call = client.newCall(request);
                try (Response response = call.execute()) {
                    // 如果响应成功或支持断点续传
                    if (response.isSuccessful() || response.code() == 206) {
                        try (BufferedSink sink = Okio.buffer(Okio.appendingSink(destFile))) {
                            sink.writeAll(response.body().source()); // 将响应内容写入文件
                            success = true; // 设置下载成功标志
                            if (callback != null) {
                                callback.onSuccess(); // 调用成功回调
                            }
                        }
                    } else {
                        retryCount++; // 增加重试次数
                        if (retryCount >= MAX_RETRY) {
                            if (callback != null) {
                                callback.onFailure(new IOException("Failed to download file: " + response.message())); // 调用失败回调
                            }
                        }
                    }
                } catch (IOException e) {
                    retryCount++; // 增加重试次数
                    if (retryCount >= MAX_RETRY) {
                        if (callback != null) {
                            callback.onFailure(e); // 调用失败回调
                        }
                    }
                }
            }
        });
    }

    // 下载回调接口
    public interface DownloadCallback {
        void onSuccess(); // 下载成功时调用

        void onFailure(IOException e); // 下载失败时调用
    }

    // 主方法,用于测试下载功能
    public static void main(String[] args) {
        FileDownloader downloader = new FileDownloader();
        String fileUrl = "https://example.com/file.zip"; // 设置下载文件的 URL
        File destFile = new File("path/to/destination/file.zip"); // 设置目标文件路径

        // 调用下载方法,并定义下载回调
        downloader.download(fileUrl, destFile, new DownloadCallback() {
            @Override
            public void onSuccess() {
                // 下载成功时处理逻辑
                System.out.println("File downloaded successfully.");
            }

            @Override
            public void onFailure(IOException e) {
                // 下载失败时处理逻辑
                e.printStackTrace();
            }
        });
    }
}

七.RxJava + OkHttp + Retrofit 网络请求

  1. 添加依赖

首先,在你的 build.gradle 文件中添加所需的依赖项:

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation 'com.squareup.okhttp3:okhttp:4.9.1'
    implementation 'io.reactivex.rxjava3:rxjava:3.0.13'
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
}
  1. 创建 API 服务接口
public interface ApiService {
    @GET("endpoint")
    Observable<ResponseBody> getData(@Query("param") String param);

    @POST("endpoint")
    @Multipart
    Observable<ResponseBody> uploadFile(
        @Part MultipartBody.Part file, 
        @PartMap Map<String, RequestBody> params
    );
}
  1. 创建网络客户端生成器
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;

import java.util.concurrent.TimeUnit;

public class NetworkClient {
    private static final String BASE_URL = "https://your.api.url/";
    private static Retrofit retrofit;

    public static Retrofit getRetrofit() {
        if (retrofit == null) {
            HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);

            OkHttpClient client = new OkHttpClient.Builder()
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .retryOnConnectionFailure(true)
                    .addInterceptor(logging)
                    .build();

            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .client(client)
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
                    .build();
        }
        return retrofit;
    }

    public static ApiService getApiService() {
        return getRetrofit().create(ApiService.class);
    }
}
  1. 创建 ViewModel 示例
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class MyViewModel extends ViewModel {
    private final CompositeDisposable disposable = new CompositeDisposable();
    private final MutableLiveData<ResponseBody> dataLiveData = new MutableLiveData<>();
    private final MutableLiveData<String> errorLiveData = new MutableLiveData<>();
    private final ApiService apiService = NetworkClient.getApiService();
    private final MutableLiveData<RequestState> requestStateLiveData = new MutableLiveData<>();

    public MutableLiveData<ResponseBody> getDataLiveData() {
        return dataLiveData;
    }

    public MutableLiveData<String> getErrorLiveData() {
        return errorLiveData;
    }

    public MutableLiveData<RequestState> getRequestStateLiveData() {
        return requestStateLiveData;
    }

    public void fetchData(String param, RequestCallback<ResponseBody> callback) {
        requestStateLiveData.setValue(RequestState.LOADING);
        if (callback != null) {
            callback.onRequestPre();
        }
  
        disposable.add(apiService.getData(param)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(responseBody -> {
                    dataLiveData.setValue(responseBody);
                    requestStateLiveData.setValue(RequestState.SUCCESS);
                    if (callback != null) {
                        callback.onRequestSuccess(responseBody);
                        callback.onRequestComplete();
                    }
                }, throwable -> {
                    errorLiveData.setValue(throwable.getMessage());
                    requestStateLiveData.setValue(RequestState.FAILURE);
                    KoolearnException exception = new KoolearnException(throwable.getMessage());
                    if (callback != null) {
                        callback.onRequestError(exception);
                        callback.requestError(exception);
                        callback.onRequestComplete();
                    }
                }));
    }

    public void uploadFile(File file, Map<String, String> params, RequestCallback<ResponseBody> callback) {
        requestStateLiveData.setValue(RequestState.LOADING);
        if (callback != null) {
            callback.onRequestPre();
        }
  
        RequestBody requestFile = RequestBody.create(file, MultipartBody.FORM);
        MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);

        Map<String, RequestBody> requestBodyMap = new HashMap<>();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            requestBodyMap.put(entry.getKey(), RequestBody.create(entry.getValue(), MultipartBody.FORM));
        }

        disposable.add(apiService.uploadFile(body, requestBodyMap)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(responseBody -> {
                    dataLiveData.setValue(responseBody);
                    requestStateLiveData.setValue(RequestState.SUCCESS);
                    if (callback != null) {
                        callback.onRequestSuccess(responseBody);
                        callback.onRequestComplete();
                    }
                }, throwable -> {
                    errorLiveData.setValue(throwable.getMessage());
                    requestStateLiveData.setValue(RequestState.FAILURE);
                    KoolearnException exception = new KoolearnException(throwable.getMessage());
                    if (callback != null) {
                        callback.onRequestError(exception);
                        callback.requestError(exception);
                        callback.onRequestComplete();
                    }
                }));
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        disposable.clear();
    }
}

5.RequestState.java

定义一个回调接口 RequestCallback.java:

public interface RequestCallback<T> {
    void onRequestPre();
    void onRequestSuccess(T result);
    void onRequestError(KoolearnException e);
    void requestError(KoolearnException e);
    void onRequestComplete();
}
  1. 在 Activity 或 Fragment 中使用 ViewModel
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import okhttp3.ResponseBody;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

public class MainActivity extends AppCompatActivity {
    private MyViewModel myViewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myViewModel = new ViewModelProvider(this).get(MyViewModel.class);


        myViewModel.fetchData("some_param", new RequestCallback<ResponseBody>() {
            @Override
            public void onRequestPre() {
                // 请求前的处理
            }

            @Override
            public void onRequestSuccess(ResponseBody result) {
                // 请求成功的处理
            }

            @Override
            public void onRequestError(KoolearnException e) {
                // 请求错误的处理
            }

            @Override
            public void requestError(KoolearnException e) {
                // 处理其他请求错误
            }

            @Override
            public void onRequestComplete() {
                // 请求完成后的处理
            }
        });

        File file = new File("path/to/your/file");
        Map<String, String> params = new HashMap<>();
        params.put("client", "Android");
        params.put("uid", "1061");
        params.put("token", "1911173227afe098143caf4d315a436d");
        params.put("uuid", "A000005566DA77");

        myViewModel.uploadFile(file, params, new RequestCallback<ResponseBody>() {
            @Override
            public void onRequestPre() {
                // 请求前的处理
            }

            @Override
            public void onRequestSuccess(ResponseBody result) {
                // 请求成功的处理
            }

            @Override
            public void onRequestError(KoolearnException e) {
                // 请求错误的处理
            }

            @Override
            public void requestError(KoolearnException e) {
                // 处理其他请求错误
            }

            @Override
            public void onRequestComplete() {
                // 请求完成后的处理
            }
        });
    }
}

八.Okhttp3与旧版本okhttp的区别分析

1、包名改变

包名改了由之前的 com.squareup.http.改为 okhttp3.
我们需要将导包名直接换掉,另外如果代码混淆的话记得修改 progurad-rules.pro 文件中将对应包名.

2、OkHttpClient参数配置

之前参数可以直接 mOkHttpClient.setCache(cache)设置,现在 OkHttpClient使用创建者模式.

3、OkHttpClient创建方式不同

okhttp直接 new OkHttpClient,而okhttp3中提供了 Builder,很好的使用了创建者设计模式.

4、cookie的设置方式不同:

okhttp调用OkHttpClient的 setCookieHandler方法,CookieHandler是Android SDK提供的标注的cookie管理,CookieHandler的之类 CookieManager实现了cookie的具体管理方法,构建CookieManager需要实现CookieStore接口,系统提供了一个默认的实现 CookieStoreImpl,只负责把cookie存储在内存中。okhttp3中已经没有 setCookieHandler方法了,而改成了cookieJar,新增了Builder,用Builder构建okhttp,设置cookie在Builder的cookieJar方法中设置。
如下:
okhttp3 之前用CookieHandler管理cookie,如下:

/* cookie管理 */

mCookieHandler = new CookieManager(null, CookiePolicy.ACCEPT_ALL);

mOkHttpClient.setCookieHandler(mCookieHandler);

3.0 之后新增了两个类Cookiejar、Cookie两个类,开放接口,需要用户自己去实现cookie的配管理。用户可以直接在CookieJar中简单设置Cookie的管理,如下:

import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MyCookieJar implements CookieJar {

    private final Map<HttpUrl, List<Cookie>> cookieStore = new ConcurrentHashMap<>();

    @Override
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
        // 将 cookies 存储到 cookieStore 中
        cookieStore.put(url, cookies);
    }

    @Override
    public List<Cookie> loadForRequest(HttpUrl url) {
        // 从 cookieStore 中加载与给定 URL 相关的 cookies
        List<Cookie> cookies = cookieStore.get(url);
        return cookies != null ? cookies : new ArrayList<>();
    }
}

5、post消息体构建方式不同

okhttp使用 MultipartBuilderFormEncodingBuilder构建post消息体,最终构建出来的都是 RequestBody,而okhttp3增加了RequestBody的子类,构造器放到了RequestBody的子类中,MultipartBody.Builder既可以添加表单数据,也可以添加文件等二进制数据。

6、Call和Callback不同

okhttp的callback方法是

void onFailure(Request request, IOException e);

void onResponse(Response response) throws IOException;

okhttp3 的Callback方法有

void onFailure(Call call, IOException e);

void onResponse(Call call, Response response) throws IOException;

okhttp3对Call做了更简洁的封装,okhttp3 Call是个接口,okhttp的call是个普通class,一定要注意,无论哪个版本,call都不能执行多次,多次执行需要重新创建。

7、OkHttpClient的Cancel方法去掉

3.0之前我们去掉call 可以直接使用 mOkHttpClient.cancel(tag);
3.0之后直接阉割掉此方法我们可以采用如下方法:

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

public class OkHttpCancelExample {

    public static void main(String[] args) {
        // 创建 OkHttpClient 实例
        OkHttpClient client = new OkHttpClient();

        // 创建请求
        Request request = new Request.Builder()
                .url("https://example.com")
                .build();

        // 创建 Call 对象
        Call call = client.newCall(request);

        // 启动一个新的线程执行请求
        new Thread(() -> {
            try {
                // 执行请求并获取响应
                Response response = call.execute();
                if (response.isSuccessful()) {
                    // 处理成功的响应
                    System.out.println("Response: " + response.body().string());
                } else {
                    // 处理失败的响应
                    System.out.println("Request failed with code: " + response.code());
                }
            } catch (IOException e) {
                // 处理请求失败的情况
                if (call.isCanceled()) {
                    System.out.println("Request was canceled");
                } else {
                    e.printStackTrace();
                }
            }
        }).start();

        // 在某个条件下取消请求,例如超时或用户取消操作
        // 模拟等待一段时间后取消请求
        try {
            Thread.sleep(2000); // 等待 2 秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 取消请求
        call.cancel();
        System.out.println("Request has been canceled");
    }
}

8、对https支持的不同

okhttp默认调用了 getDefaultSSLSocketFactory 方法,该方法提供了默认的 SSLSocketFactory,就算不设置 SSLSocketFactory 也可以支持 https,setSslSocketFactory没有做非空判断,如果设置为空,则使用默认的 SSLSocketFactory
okhttp3设置https的方法 sslSocketFactory,对 SSLSocketFactory做了非空判断,为空会抛出异常。如果不主动设置 SSLSocketFactory,okhttp3也提供了默认的http3支持。

公众号:【新时代程序员】

我的公众号

5
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区