聊聊 Android 的網絡請求框架 Retrofit 2 + okhttp 3

工做中跟客戶端溝通時產生了點問題, 記錄一下.java

原由

後端有一個接口是這樣的android

@RequestMapping("android")
public BaseResult android(@RequestBody RequestData requestData) {
    
}

RequestData.javagit

package com.junbaor.network.model;

public class RequestData {
    private Integer id;
    private String name;
    // 省略 get set
}

若是使用 http://localhost:8080/android?id=1&name=張三 是調不通的github

clipboard.png

只能處理 Content-Type 爲 application/json 的請求, 須要把參數放在 post 請求體內json

{
    "id":1,
    "name":"張三"
}

clipboard.png

問題

客戶端使用第一種方式調不通, 告知需把參數轉成 json 對象放到請求體內,
反饋說以前都是按照第一種方式調用的接口, 作不到第二種。後端

解決

後端沒有找到優雅的解決方式, 被迫修改接口實現, 之因此繼續保留 requestData 是爲了兼容其它調用方.網絡

@RequestMapping(value = "android")
public BaseResult android(@RequestBody(required = false) RequestData requestData,
                          @RequestParam(required = false) Integer id,
                          @RequestParam(required = false) String name) {
    // 處理邏輯時先判斷 requestParam 是否有值, 沒有的話再從 requestData 取
}

研究 Android

long long ago 寫過一點 Android , 不太相信想本身試試。
打聽後得知客戶端的網絡框架是 Retrofit 配合 OkHttp。
開始研究 Android, 安裝環境就不說了。app

引包

先引入客戶端使用的網絡框架, 使用的都是最新版, 因爲數據是 json 格式再引入 gson 庫(雖然不知道他們用的是什麼解析庫). converter-gson 是 retrofit2 的工具包, 用來把 json 封裝成對象.框架

compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.okhttp3:okhttp:3.9.1'
compile 'com.google.code.gson:gson:2.8.2'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'

編碼

// OkHttp 請求日誌攔截器
HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
    @Override
    public void log(String message) {
        System.out.println(message);
    }
});
logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

// OkHttp 客戶端
OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
        .connectTimeout(2, TimeUnit.MILLISECONDS)
        .addNetworkInterceptor(logInterceptor)
        .build();
        
// 把後臺返回的時間戳轉成 java.util.Date
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, new GsonDateTypeAdapter()).create();

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://127.0.0.1:8080")
        .client(okHttpClient)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();
        
 //請求參數
RequestData requestData = new RequestData();
requestData.setId(1);
requestData.setName("張三");

TestService testService = retrofit.create(TestService.class);
Call testServiceReslut = testService.getTestResult(requestData);

testServiceReslut.enqueue(new Callback<ResponseData>() {
    @Override
    public void onResponse(Call<ResponseData> call, Response<ResponseData> response) {
        System.out.println("響應成功, 數據:" + response.body());
    }

    @Override
    public void onFailure(Call<ResponseData> call, Throwable t) {
        System.out.println("響應失敗, 緣由:" + t.getMessage());
    }
});

源碼

參見:https://github.com/junbaor/an...
Android 端重點關注:com.junbaor.network.NetworkTest
Server 端重點關注:com.junbaor.network.NetworkApplicationide

花絮

Gson 時間戳轉 Date

GsonDateTypeAdapter.java

package com.junbaor.network.extend;

import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.util.Date;

/**
 * Created by junbaor on 2017/12/13.
 *
 * 將後臺返回的時間戳轉成 java.util.Date
 * 參見: https://stackoverflow.com/questions/41348055/gson-dateformat-to-parse-output-unix-timestamps
 */
public class GsonDateTypeAdapter extends TypeAdapter<Date> {

    @Override
    public void write(JsonWriter out, Date value) throws IOException {
        if (value == null) {
            out.nullValue();
        } else {
            out.value(value.getTime() / 1000);
        }
    }

    @Override
    public Date read(JsonReader in) throws IOException {
        if (in != null) {
            return new Date(in.nextLong() / 1000);
        } else {
            return null;
        }
    }
}

okhttp 日誌打印

在網上找到了這個 https://github.com/square/okh...
因爲沒有 logger 實現, 打印的比較凌亂, 索性把代碼拷到項目中以便修改, 用控制檯輸出實現了 logger 接口

// OkHttp 日誌攔截器
HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
    @Override
    public void log(String message) {
        System.out.println(message);
    }
});
logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

日誌效果:

--> POST http://127.0.0.1:8080/android http/1.1
Content-Type: application/json; charset=UTF-8
Content-Length: 24
Host: 127.0.0.1:8080
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.9.1

{"id":1,"name":"張三"}
--> END POST (24-byte body)
<-- 200 http://127.0.0.1:8080/android (15ms)
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 13 Dec 2017 16:05:49 GMT

{"code":200,"message":"成功","data":{"id":1,"name":"張三","birthday":1513181149729}}
<-- END HTTP (88-byte body)
響應成功, 數據:BaseResult{code=200, message='成功', data=ResponseData{id=1, name='張三', birthday=Sun Jan 18 20:19:41 CST 1970}}
相關文章
相關標籤/搜索