Android 訪問網(wǎng)絡(luò):方案一 OkHttp

本文使用登錄場景來簡單介紹 Android 應(yīng)用中使用 OkHttp 訪問網(wǎng)絡(luò)的用法鸟妙。

  • 數(shù)據(jù)交換協(xié)議 HTTP
  • 數(shù)據(jù)交換格式 JSON
  • HTTP 請求方法 POST

訪問網(wǎng)絡(luò)的準(zhǔn)備工作


聲明使用網(wǎng)絡(luò)訪問權(quán)限

AndroidManifest.xml

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

啟用明文通信

AndroidManifest.xml

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

出于安全性的考慮勃痴,在 Android 9 (API 級別 28)及以上版本的系統(tǒng)上,默認(rèn)禁止應(yīng)用明文通信,即禁止使用 HTTP 交換數(shù)據(jù)嫡纠,需要使用 HTTPS 闰歪。

如果沒有啟用明文通信肚逸,應(yīng)用在使用 HTTP 訪問網(wǎng)絡(luò)時會引發(fā)異常。

HTTP FAILED : java.net.UnknownServiceException : CLEARTEXT communication to 192.168.43.218 not permitted by network security policy
譯:網(wǎng)絡(luò)安全策略不允許和 192.168.43.218 進行明文通信

關(guān)于 Android 9 中默認(rèn)禁用 HTTP 通信的詳細(xì)信息聘萨,可以參閱行為變更:以 API 級別 28 及更高級別為目標(biāo)的應(yīng)用一文的 框架安全性變更 部分竹椒。

使用 OkHttp 訪問網(wǎng)絡(luò)


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. 創(chuàng)建 OkHttpClient 。添加 Http 日志攔截器米辐,以便調(diào)試胸完。

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

3. 將請求參數(shù)轉(zhuǎn)換為 JSON 字符串,創(chuàng)建 Request翘贮。

  • 將請求參數(shù)轉(zhuǎn)換為 JSON 格式的字符串
JsonObject requestParamJsonObject = new JsonObject();
requestParamJsonObject.addProperty("userName", userName);
requestParamJsonObject.addProperty("password", password);
String requestParam = requestParamJsonObject.toString();
  • 創(chuàng)建 RequestBody赊窥,并將其作為請求體創(chuàng)建 Request
    Request 用來描述一個請求的相關(guān)信息,包括 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. 創(chuàng)建 Call芍耘。

然后可以使用同步execute()或異步enqueue(Callback)方式執(zhí)行請求址遇。本文中使用的是異步方式。

Call call = client.newCall(request);

由于這一步比較簡單斋竞,通常的寫法是和下一步連起來倔约,將 Call 的實例作為匿名對象來使用。

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

5. 將 Call 加入隊列坝初,創(chuàng)建 Callback 用來處理網(wǎng)絡(luò)訪問的結(jié)果浸剩。

  • 將 Call 加入隊列,使用 Callback 實例作為 enqueue() 方法的參數(shù)
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 {
        
    }
});
  • 為了方便對響應(yīng)數(shù)據(jù)進行預(yù)處理鳄袍,通常會定義統(tǒng)一的接口響應(yīng)數(shù)據(jù)格式绢要。
    響應(yīng)數(shù)據(jù)示例
// 登錄失敗的接口響應(yīng)數(shù)據(jù)
{
    "code": 701,
    "message": "密碼錯誤",
    "data": ""
}
// 登錄成功的接口響應(yīng)數(shù)據(jù)
{
    "code": 600,
    "message": "登錄成功",
    "data": "token"
}

我們可以定義一個 Result 類型來描述響應(yīng)數(shù)據(jù)。

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() 中將響應(yīng)體的 JSON 字符串轉(zhuǎn)換為期望的類型拗小。
@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 的回調(diào)方法中切換到主線程重罪,進行后續(xù)處理。

比較常用的線程切換方案如下。

  • Handler
  • LiveData
  • Retrofit

后兩種的實現(xiàn)還是使用了 Handler 蛆封,由于進行了封裝唇礁,所以用起來相對簡單。本文 采用 LiveData 方案進行線程切換惨篱。

使用 LiveData 切換線程


1. 引入依賴

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

2. 定義 MutableLiveData 變量

定義 loginResult 變量盏筐,用于描述登錄結(jié)果。

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

3. 為 MutableLiveData 變量添加觀察者

調(diào)用 loginResult 的 observe() 方法砸讳,添加觀察者琢融。當(dāng) loginResult 的值改變后,此觀察者會在主線程進行后續(xù)處理簿寂。

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. 在任務(wù)線程中調(diào)用 postValue() 方法漾抬,更改該變量的值

在 Callback 的 onResponse() 方法中調(diào)用 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);

}


測試設(shè)備參數(shù)

  1. 測試設(shè)備1:
  • 型號:Mi 10 Lite Zoom
  • 操作系統(tǒng):MIUI 12.0.6 穩(wěn)定版 (Android 10)
  1. 測試設(shè)備2:
  • 型號:vivo Y66L
  • 操作系統(tǒng):Funtouch OS 3.0(Android 6.0.1)

參考資料

  1. 怪盜kidou : 你真的會用Gson嗎?Gson使用指南(一)
  2. Square : OkHttp
  3. Android 應(yīng)用開發(fā)指南 : LiveData 概覽

代碼

XuMeng-0 / android-study

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末常遂,一起剝皮案震驚了整個濱河市纳令,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌克胳,老刑警劉巖平绩,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異漠另,居然都是意外死亡捏雌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門笆搓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來性湿,“玉大人,你說我怎么就攤上這事满败》羝担” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵算墨,是天一觀的道長着裹。 經(jīng)常有香客問我,道長米同,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任摔竿,我火速辦了婚禮面粮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘继低。我一直安慰自己熬苍,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著柴底,像睡著了一般婿脸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柄驻,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天狐树,我揣著相機與錄音,去河邊找鬼鸿脓。 笑死抑钟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的野哭。 我是一名探鬼主播在塔,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拨黔!你這毒婦竟也來了蛔溃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤篱蝇,失蹤者是張志新(化名)和其女友劉穎贺待,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體态兴,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡狠持,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瞻润。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喘垂。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绍撞,靈堂內(nèi)的尸體忽然破棺而出正勒,到底是詐尸還是另有隱情,我是刑警寧澤傻铣,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布章贞,位于F島的核電站,受9級特大地震影響非洲,放射性物質(zhì)發(fā)生泄漏鸭限。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一两踏、第九天 我趴在偏房一處隱蔽的房頂上張望败京。 院中可真熱鬧,春花似錦梦染、人聲如沸赡麦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泛粹。三九已至遂铡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晶姊,已是汗流浹背扒接。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留帽借,地道東北人珠增。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像砍艾,于是被迫代替她去往敵國和親蒂教。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容