《第一行程式碼》閱讀筆記(二十八)——網路技術(OkHttp+JSON/GSON)
網路技術在程式設計中也是非常重要的一環,在android底層是通過HttpURLConnection實現的,後來出現了一款優秀的框架OkHttp,實現了對底層的封裝。然後隨著技術的進步,現在更多的是使用OkHttp+Retrofit+Rxjava網路框架。這裡書中沒有詳細說,後面筆者會對這些部分進行一個補充。
WebView案例
書中以一個內嵌的網頁來開啟網路技術的大門,讓我們來一起看看吧。
第一步:新建一個WebViewTest專案,修改activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
第二步:修改MainActivity
package com.firstcode.webviewtest; import android.os.Bundle; import android.webkit.WebView; import android.webkit.WebViewClient; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView webView = findViewById(R.id.web_view); webView.getSettings().setJavaScriptEnabled(true); webView.setWebViewClient(new WebViewClient()); webView.loadUrl("https://www.baidu.com/"); } }
非常的簡單,就讓我們來看下作者是怎麼解釋的吧
——第一行程式碼
MainActivity中的程式碼也很短,首先使用findViewById( )方法獲取到了WebView的例項,然後呼叫WebView的getSettings()方法可以去設定一些瀏覽器的屬性,這裡我們並不去設定過多的屬性,只是呼叫了setJavaScriptEnabled ()方法來讓WebView支援JavaScript 指令碼。
接下來是非常重要的一個部分,我們呼叫了WebView 的setWebViewClient()方法,並傳人了一個WebViewClient的例項。這段程式碼的作用是,當需要從一個網頁跳轉到另一個網頁時,我們希望目標網頁仍然在當前WebView中顯示,而不是開啟系統瀏覽器。
最後一步就非常簡單了,呼叫WebView的loadUrl()方法,並將網址傳入,即可展示相應網頁的內容,這裡就讓我們看一看百度的首頁長什麼樣吧。
第三步:設定許可權
在AndroidManifest.xml中加入下面這句語句即可
這個非常重要,經常容易忘記。
HttpURLConnection
通過WebView打開了網路世界的大門,之後我們就可以使用網路技術了。這就提到了一個HttpURLConnection,讓我們看看是如何實現的吧。這個並不是很重要,因為後面要講的OkHttp才是關鍵。但是底層的基礎也不能完全不瞭解,所以大家還是需要看看。
第一步:新建專案,修改主頁佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/send_request"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Send Request" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/response_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</ScrollView>
</LinearLayout>
非常簡單,不多說。
第二步:修改主活動程式碼
package com.firstcode.networktest;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
TextView responseText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendRequest = (Button) findViewById(R.id.send_request);
responseText = (TextView) findViewById(R.id.response_text);
sendRequest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
sendRequestWithHttpURLConnection();
}
});
}
private void sendRequestWithHttpURLConnection() {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL url = new URL("https://www.baidu.com/");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(100000);
connection.setReadTimeout(100000);
InputStream in = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
showResponse(response.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
private void showResponse(final String response) {
runOnUiThread(new Runnable() {
@Override
public void run() {
responseText.setText(response);
}
});
}
}
筆者在這裡做了一些修改,沒有抽取點選事件了,怎麼方便怎麼來。其實大家也應該怎麼做都會做,做到想放哪裡就放哪裡就OK了。
這裡主要說一下這個runOnUiThread() 方法,使用它是因為Android是不允許在子執行緒中進行UI操作的,我們需要通過這個方法將執行緒切換到主執行緒,然後再更新UI元素。
注意事項
如果是android版本比較高的手機用來做測試只能使用https的請求,http的請求無法顯示,想讓http的請求顯示需要以下設定。
- 新建xml資料夾,並建立xml檔案,如下圖所示
- 輸入以下內容
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
- 最後在application標籤中,加入android:networkSecurityConfig="@xml/config"
post請求
輸入以下屬性即可
connection.setRequestMethod(" POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
OkHttp的簡單使用
官網資訊
OkHttp官網
Get a URL
This program downloads a URL and prints its contents as a string. Full source.
這段編碼下載一個URL並將其內容列印為字串。更多原始碼
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
Post to a Server
This program posts data to a service. Full source.
這段編碼提交一個data資料到伺服器。更多原始碼
public static final MediaType JSON
= MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
Further examples are on the OkHttp Recipes page.
更多的例子在這裡。
看不懂?沒關係,看看郭神怎麼說。
Get
第一步:匯入依賴
implementation("com.squareup.okhttp3:okhttp:3.4.1")
更新了一下匯入依賴的方式,但是版本保持和作者的一樣。
第二步:修改sendRequestWithHttpURLConnection()為sendRequestWithOkHttp(),程式碼如下
private void sendRequestWithOkHttp() {
new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("http://m.iyuba.cn/jlpt1/getConceptWordOver.jsp?app=primary")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
showResponse(responseData);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
首先需要建立一個OkHttpClient例項。使用Request request = new Request.Builder().build();
建立一個request的空物件,再通過鏈式程式設計.url()來獲取介面。使用newCall回撥一個call,再通過.execute()
傳送請求,並接收資料。最後通過response.body().string();
獲取內容。因為獲取網路請求的執行緒是不能修改UI的所有需要編寫一個方法來重新整理UI,在這裡就是showResponse(responseData);
,它也是一個執行緒。
Post
post請求比get請求多一個RequestBody,具體如下:
OkHttpClient client = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
.add("username", "admin")
.add("password", "123456")
.build();
//需要可以接收RequestBody的url
Request request = new Request.Builder()
.url("***")
.post(requestBody)
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
JSONObject
現在網路上都使用JSON作為前後端互動的基礎。如何使用JSON?
第一步:新增方法parseJSONWithJSONObject,傳入responseData
parseJSONWithJSONObject(responseData);
showResponse(responseData);//
第二步:編寫方法
private void parseJSONWithJSONObject(String jsonData) {
try {
JSONArray jsonArray = new JSONArray(jsonData);
for (int i = 0; i < 5; i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
String voaId = jsonObject.getString("voa_id");
String word = jsonObject.getString("word");
Log.i(TAG, "id is" + voaId);
Log.i(TAG, "word id " + word);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
JSONArray可以例項化並接收json字串,然後使用jsonArray.getJSONObject(i)獲取每一個物件賦值給JSONObject,通過getString方法獲取內容。非常好理解。
Gson
本章節沒有使用書中那麼簡單的案例,正好實習公司有一段需求,拿來練練手,順便記錄下。
第一步:匯入依賴
implementation 'com.google.code.gson:gson:2.8.6'
第二步:安裝外掛
書中也提到了需要為Gson資料匹配實體類,但是那太麻煩了。直接自動生成。外掛名字是GsonFormat
新建一個類,自動生成相應資料。步驟如下:
沒什麼難度。
第三步:修改
因為筆者自己的連結,json資料有個巢狀,這個在公司中也是非常常見的。就像是資料庫的一對多關係。有個size屬性,和data屬性,data中就是資料。如圖
如果使用書上的方法,會報錯。
Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 11 column 2 path $
所以我們再建一個類,進行抽取。很巧妙吧
package com.firstcode.okhttptest;
import java.util.List;
public class Temp {
private String size;
private List<Vocabulary> data;
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public List<Vocabulary> getData() {
return data;
}
public void setData(List<Vocabulary> data) {
this.data = data;
}
}
其實只要把所有的json全部放到gsonformat的文字框裡面就行,但是資料太多,電腦太爛,一執行就卡死。無奈提升了自己水平。
第四步:修改parseJSONWithGson
private void parseJSONWithGson(String jsonData) {
Gson gson = new Gson();
Temp temp = gson.fromJson(jsonData, Temp.class);
List<Vocabulary> vocabularyList = temp.getData();
for (Vocabulary v : vocabularyList) {
Log.i(TAG, "word is" + v.getWord());
}
}
結果
書中還介紹了一種方法,值得注意。
——第一行程式碼
如果需要解析的是一段JSON陣列會稍微麻煩一點,我們需要藉助TypeToken將期望解析成的資料型別傳入到fromJson()方法中,如下所示:
Listpeople = gson.fromJson(jsonData, new TypeToken<List >() {}. getType());
網路程式設計的最佳例項
HttpURLConnection的就不寫了,大家有興趣的看下。
OkHttp的主要實現方法就是
package com.iyuba.primaryenglish.util;
import okhttp3.OkHttpClient;
import okhttp3.Request;
public class HttpUtil {
public static void sendOkHttpRequest(String address, okhttp3.Callback callback) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(address).build();
client.newCall(request).enqueue(callback);
}
}
使用時,編寫如下
HttpUtil.sendOkHttpRequest("http://www.baidu.com", new okhttp3.Callback() {
@Override
public void onFailure(Call call, IOException e) {
//在這裡對異常情況進行處理
}
@Override
public void onResponse(Call call, Response response) throws IOException {
//得到伺服器返回的具體資訊
String reponseData = response.body().string();
}
});