《第一行代码》阅读笔记(二十八)——网络技术(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中加入下面这句语句即可
<uses-permission android:name="android.permission.INTERNET" />
这个非常重要,经常容易忘记。
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();
}
});