Android:使用 OkHttp 访问网络

本文使用登录场景来简单介绍 Android 应用中使用 OkHttp 访问网络的用法。

  • 数据交换协议 HTTP
  • 数据交换格式 JSON
  • HTTP 请求方法 Post

访问网络的准备工作


声明使用网络访问权限

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />

启用明文通信

AndroidManifest.xml

<application
        ...
        android:usesCleartextTraffic="true">
        ...
</application>

出于安全性的考虑,在 Android 9 (API 级别 28)及以上版本的系统上,默认禁止应用明文通信,即禁止使用 HTTP 交换数据,需要使用 HTTPS 。

如果没有启用明文通信,应用在使用 HTTP 访问网络时会引发异常。

HTTP FAILED : java.net.UnknownServiceException : CLEARTEXT communication to 192.168.43.218 not permitted by network security policy
译:网络安全策略不允许和 192.168.43.218 进行明文通信

使用 OkHttp 访问网络


1.引入依赖

build.gradle(:app)

dependencies {
    
    
    ...
    implementation 'com.squareup.okhttp3:okhttp:4.10.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:4.10.0'
    implementation 'com.google.code.gson:gson:2.10.1'
}

2. 创建 OkHttpClient 。添加 Http 日志拦截器,以便调试。

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
    .build();

3. 将请求参数转换为 JSON 字符串,创建 Request。

  • 将请求参数转换为 JSON 格式的字符串
JsonObject requestParamJsonObject = new JsonObject();
requestParamJsonObject.addProperty("userName", userName);
requestParamJsonObject.addProperty("password", password);
String requestParam = requestParamJsonObject.toString();

  • 创建 RequestBody,并将其作为请求体创建 Request
    Request 用来描述一个请求的相关信息,包括 url,请求方法Post/Get, 请求头,请求体等信息。
MediaType mediaTypeJson = MediaType.parse("application/json; charset=utf-8");
Request request = new Request.Builder()
    .url("http://192.168.43.218:8080/user/login")
    .post(RequestBody.create(requestParam, mediaTypeJson))
    .build();

4. 创建 Call。

然后可以使用同步execute()或异步enqueue(Callback)方式执行请求。本文中使用的是异步方式。

Call call = client.newCall(request);

由于这一步比较简单,通常的写法是和下一步连起来,将 Call 的实例作为匿名对象来使用。

client.newCall(request).enqueue(new Callback(){...});

5. 将 Call 加入队列,创建 Callback 用来处理网络访问的结果。

  • 将 Call 加入队列,使用 Callback 实例作为 enqueue() 方法的参数
call.enqueue(new Callback() {
    
    
    @Override
    public void onFailure(@NonNull Call call, @NonNull IOException e) {
    
    
    
    }

    @Override
    public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
    
    
        
    }
});

  • 为了方便对响应数据进行预处理,通常会定义统一的接口响应数据格式。
    响应数据示例
// 登录失败的接口响应数据
{
    
    
    "code": 701,
    "message": "密码错误",
    "data": ""
}

// 登录成功的接口响应数据
{
    
    
    "code": 600,
    "message": "登录成功",
    "data": "token"
}

我们可以定义一个 Result 类型来描述响应数据。

public class Result<DATA> {
    
    

    private static final int HTTP_REQUEST_SUCCESS_CODE = 600;

    public int code;
    public String message;
    public DATA data;


    public boolean isSuccessful() {
    
    
        return code == HTTP_REQUEST_SUCCESS_CODE;
    }

    @Override
    public String toString() {
    
    
        return "Result{" +
            "code=" + code +
            ", message='" + message + '\'' +
            ", data=" + data +
        '}';
    }

}

  • 在 Callback 的 onResponse() 中将响应体的 JSON 字符串转换为期望的类型。
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
    
    
    ResponseBody body = response.body();
    if (body == null) {
    
    
        onFailure(call, new IOException("body is null"));
        return;
    }
    Gson gson = new Gson();
    Result<String> result = gson.fromJson(body.string(), new TypeToken<Result<String>>() {
    
    
    }.getType());
}

6. 在 Callback 的回调方法中切换到主线程,进行后续处理。

比较常用的线程切换方案如下。

  • Handler
  • LiveData
  • Retrofit

后两种的实现还是使用了 Handler ,由于进行了封装,所以用起来相对简单。本文 采用 LiveData 方案进行线程切换。

使用 LiveData 切换线程


1. 引入依赖

dependencies {
    
    
    ...
    implementation "androidx.lifecycle:lifecycle-livedata:2.5.1"
}

2. 定义 MutableLiveData 变量

定义 loginResult 变量,用于描述登录结果。

private final MutableLiveData<Result<String>> loginResult = new MutableLiveData<>();

3. 为 MutableLiveData 变量添加观察者

调用 loginResult 的 observe() 方法,添加观察者。当 loginResult 的值改变后,此观察者会在主线程进行后续处理。

protected void onCreate(Bundle savedInstanceState) {
    
    
    super.onCreate(savedInstanceState);
    ...
    subscribeToLiveData();
}

private void subscribeToLiveData() {
    
    
    loginResult.observe(this, loginResult -> {
    
    
        Toast.makeText(LoginActivity.this, loginResult.message, Toast.LENGTH_SHORT).show();        
        if (loginResult.isSuccessful()) {
    
    
            handleLoginResultSuccess(loginResult);
        }
    });
}

private void handleLoginResultSuccess(Result<String> loginResult) {
    
    
    Log.i(TAG, "token : " + loginResult.data);
    // save token
    // start activity
}

4. 在任务线程中调用 postValue() 方法,更改该变量的值

在 Callback 的 onResponse() 方法中调用 loginResult 的 postValue() 方法。

@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
    
    
    ResponseBody body = response.body();
    if (body == null) {
    
    
        onFailure(call, new IOException("body is null"));
        return;
    }
    Gson gson = new Gson();
    Result<String> result = gson.fromJson(body.string(), new TypeToken<Result<String>>() {
    
    
    }.getType());

    loginResult.postValue(result);

}

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

猜你喜欢

转载自blog.csdn.net/weixin_43440181/article/details/130515662