《第一行代码》阅读笔记(二十八)——网络技术(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的请求显示需要以下设置。

  1. 新建xml文件夹,并创建xml文件,如下图所示

  1. 输入以下内容
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>
  1. 最后在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()方法中,如下所示:
List people = 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();
            }
        });
原文地址:https://www.cnblogs.com/zllk/p/13416378.html