Learning notes of the first line of code: Android

This article and the following articles are the learning notes for reading the first line of code: Android (2nd Edition) written by Mr. Guo Lin. they are recorded in the order of the contents in the book. I have done all the demos in the book.
I have made detailed records of each chapter. The following are my learning records (including the sorting of a large number of books and various bug s and solutions encountered in my study), which are convenient for future reading and reference. Finally, thank Mr. Guo Lin for providing such good books.

Chapter 9 look at the wonderful world - using network technology

Now is no longer the era of playing stand-alone. Almost all PC s, mobile phones, tablets and TVs will have the function of surfing the Internet. In the foreseeable future, watches, glasses, cars and other devices will also join this ranks one by one. The 21st century is indeed the era of the Internet.

Of course, Android phones can also access the Internet, so as developers, we need to consider how to use the network to write better applications. Common applications such as QQ, microblog and wechat will use a lot of network technology.

This chapter will mainly describe how to use HTTP protocol to interact with the server on the mobile phone and analyze the data returned by the server. This is also the most commonly used network technology in Android. Let's learn it together.

9.1 usage of WebView

For example, ask to display some web pages in the application. Loading and displaying web pages are usually the task of the browser, but the requirements clearly point out that it is not allowed to open the system browser, and of course we can't write a browser ourselves. What should we do at this time?

Android has long considered and provided a WebView control. With the help of it, you can embed a browser in your own application, so as to display all kinds of web pages very easily. The usage of WebView is also quite simple. Let's learn from an example. Create a new WebViewTest project and modify the activity_ main. The code in XML is as follows:

<?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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/web_view"/>
</LinearLayout>

A new control is used in the layout file: WebView. This control is used to display the of the web page, set it with an id, and let it fill the whole screen. Then modify the code in MainActivity as follows:

package com.zhouzhou.webviewtest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        WebView webView =(WebView) findViewById(R.id.web_view);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient());
        webView.loadUrl("https://www.cnblogs.com/1693977889zz/");
    }
}

In MainActivity, first use the findViewById() method to get the instance of WebView, and then call the getSettings() method of WebView to set the properties of some browsers. Here, just call the setJavaScriptEnabled() method to make WebView support JavaScript scripts.

Next, the setWebViewClient() method of WebView is called and an instance of WebViewClient is passed in. The purpose of this code is that when we need to jump from one page to another, we want the target page to still be displayed in the current WebView instead of opening the system browser.

The last step is to call the loadUrl() method of WebView and pass in the web address to display the content of the corresponding web page.

In addition, it should be noted that since this program uses the network function, and the permission to access the network needs to be declared, we have to modify androidmanifest XML file and add a permission statement, as shown below:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zhouzhou.webviewtest">
    <uses-permission android:name="android.permission.INTERNET"/>
    ...
</manifest>

Before starting to run, you need to ensure that your mobile phone or simulator is connected to the Internet. If you use the simulator, just ensure that the computer can connect to the Internet normally. Then you can run the following program. The effect is shown in the figure:

It can be seen that WebViewTest has the function of a simple browser. It can not only successfully display my blog, but also browse more web pages by clicking the link.

Of course, WebView has many more advanced usage skills, so we won't continue to explore them. Here we first introduce the usage of WebView. I just hope you can have a basic understanding of the use of HTTP protocol. Next, we need to use this protocol to do some real network development work.

9.2 accessing the network using HTTP protocol

If you really want to deeply analyze the HTTP protocol, it may take a whole book. Here you only need to know a little about it. Its working principle is particularly simple. That is, the client sends an HTTP request to the server. After receiving the request, the server will return some data to the client, and then the client will parse and process these data.

For example, the WebView control used in the previous section actually means that we send an HTTP request to the server, and then the server analyzes the page we want to visit, so it will return the HTML code of the page, and then WebView calls the kernel of the mobile browser to analyze the returned HTML code, and finally display the page.

In short, WebView has helped us handle the steps of sending HTTP request, receiving service response, parsing returned data and final page display in the background. However, because it is encapsulated so well, we can't see how the HTTP protocol works so intuitively. Therefore, let's have a deeper understanding of this process by manually sending HTTP requests.

9.2.1 using HttpURLConnection

In the past, there were generally two ways to send HTTP requests on Android: HttpURLConnection and HttpClient. However, due to the shortcomings of too many API s and difficult expansion of HttpClient, the Android team increasingly does not recommend us to use this method.

Finally, in Android 6.0 system, the function of HttpClient has been completely removed, marking that this function has been officially abandoned. Therefore, in this section, we will learn the usage of HttpURLConnection officially recommended.

First, you need to get an instance of HttpURLConnection. Generally, you only need to new out a URL object, pass in the network address of the target, and then call the openConnection() method, as shown below:

URL url = new URL("http://baidu.com");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();

After obtaining the instance of HttpURLConnection, you can set the method used for HTTP request. There are two common methods: GET and POST. GET indicates that you want to GET data from the server, while POST indicates that you want to submit data to the server. It is written as follows:

connection.setRequestMethod("GET");

Next, you can make some free customizations, such as setting the connection timeout, the number of milliseconds of reading timeout, and some message headers that the server wants to get. This part is written according to your actual situation. The example is as follows:

connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);

Then call getInputStream() method to get the input stream returned by the server. The remaining task is to read the input stream, as shown below:

InputStream in = connection.getInputStream();

Finally, you can call the disconnect() method to close the HTTP connection, as shown below:

connection.disconnect();

Let's really experience the usage of HttpURLConnection through a specific example. To create a new NetworkTest project, first modify the activity_ main. The code in XML is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/send_request"
        android:text="Send Request"/>
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/response_text"/>
    </ScrollView>
</LinearLayout>

A new control is used: ScrollView. What is it used for?

Because the space of mobile phone screen is generally small, sometimes too much content cannot be displayed on one screen. With the help of ScrollView control, we can view the part of content outside the screen in the form of scrolling. In addition, a Button and a TextView are placed in the layout. The Button is used to send HTTP requests, and the TextView is used to display the data returned by the server. Then modify the code in MainActivity as follows:

package com.zhouzhou.networktest;

import androidx.appcompat.app.AppCompatActivity;

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;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    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(this);
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.send_request) {
            sendRequestWithHttpURLConnection();
        }
    }

    private void sendRequestWithHttpURLConnection() {
        //Start a thread to initiate a network request
        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(8000);
                    connection.setReadTimeout(8000);
                    InputStream inputStream = connection.getInputStream();
                    //Next, read the obtained input stream
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                    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() {
                //Conduct Ui operation here and display the results to the interface
                responseText.setText(response);
            }
        });
    }
}

You can see that the sendRequestWithHttpURLConnection() method is called in the click event of the Send Request button. In this method, a sub thread is started first, and then an HTTP request is sent by using HttpURLConnection in the sub thread. The target address of the request is Baidu's home page.

Then, BufferedReader is used to read the stream returned by the server, and the result is passed into the showResponse() method. In the showResponse() method, a runOnUiThread() method is called, and then the anonymous class parameters of this method are operated to display the returned data on the interface.

So why use this runOnUiThread() method here? This is because Android does not allow UI operations in child threads. We need to switch the thread to the main thread through this method, and then update the UI elements.

We will explain this part in detail in the next chapter. Now you just need to remember to write it like this. The whole process is like this, but before you start running, don't forget to declare network permissions. Modify androidmanifest The code in XML is as follows:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zhouzhou.networktest">
    <uses-permission android:name="android.permission.INTERNET"/>
    ...
</manifest>

OK, now run the program and click the Send Request button. The results are as follows:

What the server returns to us is this kind of HTML code, but usually the browser will parse these codes into beautiful web pages and then display them.

So what if you want to submit data to the server? Just change the HTTP request method to POST and write out the data to be submitted before obtaining the input stream. Note that each data must exist in the form of key value pairs, and the data is separated by "&". For ex amp le, if we want to submit the user name and password to the server, we can write as follows:

connection.setRequestMethod("POST");
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()) ;
outputStream.writeBytes("username=admin&&password=123456");

9.2.2 using OkHttp

Of course, we don't have to use HttpURLConnection. We have no other choice at all. In fact, today, when open source is popular, there are many excellent network communication libraries that can replace the original HttpURLConnection, and OkHttp is undoubtedly the best one.

OkHttp is developed by the famous Square company. This company has made a lot of contributions to the open source cause. In addition to OkHttp, it has also developed famous open source projects such as Picasso and Retrofit.

OkHttp is not only simple and easy to use in the interface encapsulation, but also in the underlying implementation. Compared with the native HttpURLConnection, OkHttp has become the preferred network communication library for Android developers.

In this section, let's learn the usage of OkHttp. The project home page address of OkHttp is: https://github.com/square/okhttp.

See the project website for documentation and APIs.

HTTP is the way modern applications network. It's how we exchange data & media. Doing HTTP efficiently makes your stuff load faster and saves bandwidth.

OkHttp is an HTTP client that's efficient by default:

  • HTTP/2 support allows all requests to the same host to share a socket.
  • Connection pooling reduces request latency (if HTTP/2 isn't available).
  • Transparent GZIP shrinks download sizes.
  • Response caching avoids the network completely for repeat requests.

OkHttp perseveres when the network is troublesome: it will silently recover from common connection problems. If your service has multiple IP addresses, OkHttp will attempt alternate addresses if the first connect fails. This is necessary for IPv4+IPv6 and services hosted in redundant data centers. OkHttp supports modern TLS features (TLS 1.3, ALPN, certificate pinning). It can be configured to fall back for broad connectivity.

Using OkHttp is easy. Its request/response API is designed with fluent builders and immutability. It supports both synchronous blocking calls and async calls with callbacks.

Before using OkHttp, we need to add OkHttp library dependencies in the project. Edit app / build Gradle file, add the following contents to the dependencies closure:

    dependencies {
       implementation("com.squareup.okhttp3:okhttp:4.9.3")
    }

Adding the above dependencies will automatically download two libraries, one is OkHttp library and the other is Okio library, which is the communication basis of the former.

Let's take a look at the specific usage of OkHttp. First, we need to create an instance of OkHttpClient, as shown below:

OkHttpClient client = new OkHttpClient();

Next, if you want to initiate an HTTP Request, you need to create a Request object:

Request request = new Request.Builder().build();

Of course, the above code just creates an empty Request object, which has no practical effect. We can concatenate many other methods before the final build() method to enrich the Request object. For example, you can set the network address of the target through the url() method, as shown below:

Request request = new Request.Builder()
      .url("https://www.baidu.com")
      .build();

Then, Call the newCall() method of OkHttpClient to create a Call object, and Call its execute() method to send the request and obtain the data returned by the server. The writing method is as follows:

Response response = client.newCall(request).execute();

Among them, the Response object is the data returned by the server. We can use the following method to get the specific content of the return:

String responseData = response.body().string();

If you initiate a POST request, it will be a little more complicated than a GET request. We need to build a RequestBody object to store the parameters to be submitted, as shown below:

RequestBody requestBody = new FormBody.Builder()
      .add("username","admin")
      .add("password","123456")
      .build();

Then, in request Call the post() method in the builder and pass in the RequestBody object:

Request request = new Request.Builder()
      .url("https://www.baidu.com")
      .post(requestBody)
      .build();

The next operation is the same as the GET request. Just call the execute() method to send the request and GET the data returned by the server.

We will use OkHttp to implement all the network related functions in the back of the book, and we will further study at that time. Now let's change the NetworkTest project to OkHttp and implement it again. Since the layout part does not need to be changed at all, now directly modify the code in MainActivity, as shown below:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    ...
    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.send_request) {
            //sendRequestWithHttpURLConnection();
            sendRequestWithOkHttp();
        }
    }

    private void sendRequestWithOkHttp() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder()
                            .url("https://www.baidu.com")
                            .build();
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    showResponse(responseData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
	...
}

There are not many changes here, but a sendRequestWithOkHttp() method is added and called in the click event of the Send Request button. In this method, a sub thread is also started first, and then OkHttp is used in the sub thread to send an HTTP request. The target address of the request is still Baidu's home page, and the usage of OkHttp is the same as described above.

Finally, the showResponse() method is still called to display the data returned by the server on the interface. Run the program again. After clicking the send request button, the test result is OK, which proves that the function of sending HTTP requests using OkHttp has also been successfully realized.

9.3 parsing XML format data

Usually, each application that needs to access the network will have its own server. We can submit data to the server or obtain data from the server.

However, a problem arises at this time. What format should these data be transmitted on the network? It's definitely impossible to pass a text casually, because the other party will never know what the purpose of this text is. Therefore, generally, we will transmit some formatted data on the network. This data will have certain structural specifications and semantics. When the other party receives the data message, it can analyze it according to the same structural specifications, so as to take out the part of the content he wants. There are two most commonly used formats for transmitting data on the network: XML and JSON. Let's learn one by one. In this section, first learn how to parse data in XML format.

Before we start, we need to solve a problem, that is, where can we get a piece of data in XML format?

Here I'm going to teach you to build the simplest Web server, provide an XML text on this server, and then we access this server in the program, and then parse the XML text.

Building a Web server is actually very simple. There are many server types to choose from. Here I'm going to use Apache server. First, you need to download an Apache server installation package. The official download address is: http://httpd.apache.org/download.cgi . If you can't find the installation package for Windows in this website, you can also search "Apache server download" directly on Baidu, and you will find many download links.

The following is a brief introduction to the official download steps:

  1. Click the link a number of third party vendors.:

  1. Find Downloading Apache for Windows and click the Apache Haus link:

  1. Click the icon in the red box to start downloading. x86 is 32-bit and x64 is 64 bit. Choose to download according to your own operating system:

  1. Click and jump to the download page, your download will start short

  1. It often times out ~ so I downloaded [Apache HTTP Server official version v2.4.17] on the Internet and saved it to the location I want to save (I saved it to: F:\ApacheServer), unzip the compressed package and open httpd Conf file (I'm in F:\ApacheServer\conf), modify the Apache installation directory, (where "${SRVROOT}" refers to the defined SRVROOT path variable):

  1. If port 80 is occupied (you can use the command netstat -an -o | findstr 80 under cmd), change port 80 to another one and save it:

  1. Run cmd as an administrator, copy and enter "F:\ApacheServer\bin\httpd.exe" -k install -n apache. This command means to install the Apache service under this directory (F:\ApacheServer\bin \), and name the service "Apache".

    Alternatively, run cmd as an administrator, enter the F:\ApacheServer\bin directory, and enter httpd Exe - K install - N apache install apache services

  1. Double click apachemonitor.com under the run bin directory Exe, which can also be opened through the service:

  1. Double click to open the window interface, as shown in the figure:


10. Click start to test whether the Apache server is available and enter it in the browser http://localhost:8099 , (port 80 is occupied and changed to 8099) the following page appears, indicating that the installation and configuration is successful!

Next, go to the F:\ApacheServer\htdocs directory and create a new one called get_data.xml file, then edit this file and add the following XML format:

<apps>
	<app>
		<id>1</id>
		<name>Google Maps</name>
		<version>1.0</version>
	</app>
	<app>
		<id>2</id>
		<name>Chrome</name>
		<version>2.1</version>
	</app>
	<app>
		<id>3</id>
		<name>Google Play</name>
		<version>2.3</version>
	</app>
</apps>

This is accessed in the browser http://127.0.0.1:8099/get_data.xml This website should appear as shown in the figure:

This is the end of the preparatory work. Next, let's get and parse this XML data in the Android program.

Analysis method of pull

There are many ways to parse data in XML format. In this section, we will learn the two commonly used methods, Pull parsing and SAX parsing.

For the sake of simplicity, the development here is still based on the NetworkTest project, so that we can reuse the code of the previous network communication part, so as to focus on XML data parsing.

Now that the data in XML format has been provided, what we need to do now is to parse the part we want. Modify the code in MainActivity as follows:

package com.zhouzhou.networktest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    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(this);
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.send_request) {
            //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://127.0.0.1:8099/get_data.xml")
                            .build();
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    //showResponse(responseData);
                    parseXMLWithPull(responseData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void parseXMLWithPull(String xmlData) {
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlData));
            int eventType = xmlPullParser.getEventType();
            String id = "";
            String name = "";
            String version = "";
            while (eventType != XmlPullParser.END_DOCUMENT) {
                String nodeName = xmlPullParser.getName();
                switch (eventType) {
                    //Start parsing a node
                    case XmlPullParser.START_TAG: {
                        if ("id".equals(nodeName)) {
                            id = xmlPullParser.nextText();
                        } else if ("name".equals(nodeName)) {
                            name = xmlPullParser.nextText();
                        } else if ("version".equals(nodeName)) {
                            version = xmlPullParser.nextText();
                        }
                        break;
                    }
                    //Finish parsing a node
                        case XmlPullParser.END_TAG: {
                            if ("app".equals(nodeName)) {
                                Log.d("MainActivity","id is " + id);
                                Log.d("MainActivity","name is " + name);
                                Log.d("MainActivity","version is " + version);
                            }
                            break;
                        }
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendRequestWithHttpURLConnection() {
        //Start a thread to initiate a network request
        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(8000);
                    connection.setReadTimeout(8000);
                    InputStream inputStream = connection.getInputStream();
                    //Next, read the obtained input stream
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                    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() {
                //Conduct Ui operation here and display the results to the interface
                responseText.setText(response);
            }
        });
    }
}

As you can see, the first thing here is to change the address of the HTTP request to http://10.0.2.2:8099/get_data.xml .

10.0.2.2 for the simulator, it is the IP address of the computer. If used: http://127.0.0.1:8099/get_data.xml perhaps http://localhost:8099/get_data.xml If yes, the error message is: Java net. ConnectException: failed to connect to /127.0.0.1 (port 8099) after 2500ms: isConnected failed: ECONNREFUSED (Connection refused)

In addition, an error may be reported: w / system err: java. net. UnknownServiceException:
CLEARTEXT communication your domain name is not allowed by network security policy

The reason for the error is that the network security policy does not allow:

In order to ensure the security of user data and devices, Google's applications for the next generation Android system (Android P) will require encrypted connections by default, which means that Android P will prohibit apps from using all unencrypted connections. Therefore, Android devices running Android P system can not transmit traffic in clear code in the future, and need to use the next generation (Transport Layer Security) transport layer security protocol, Android Nougat and Oreo are not affected.

On the device of Android P system, if the application uses the http network request of unencrypted plaintext traffic, the application will not be able to make the network request, and https will not be affected. Similarly, if the application is nested with webview, webview can only use https request.

terms of settlement:

(1) APP uses https request instead

(2) targetSdkVersion is reduced below 27 (this problem occurs in the new project with API > = 27)

(3) Add in the application element:

android:usesCleartextTraffic="true"

A more standardized point is:

Create a new xml directory under res directory and a new network directory under xml directory_ security_ config. xml file, content:

    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <base-config cleartextTrafficPermitted="true" />
    </network-security-config>

And then at androidmanifest New attribute in the Application tag of XML:

android:networkSecurityConfig="@xml/network_security_config" 

All errors are solved, and the program runs OK. continue,

After getting the data returned by the server, we don't show it directly, but call the parsexmlwithpull () method to parse the data returned by the server. The code in the parseXMLWithPull() method first needs to get an instance of XmlPullParserFactory, get the XmlPullParser object with this instance, and then call the setInput() method of XmlPullParser to set the XML data returned by the server to start parsing. The parsing process is also very simple. You can get the current parsing event through getEventType(), and then parse it continuously in a while loop. If the current parsing event is not equal to XmlPullParser END_ Document, indicating that the parsing work has not been completed. After calling the next() method, you can get the next parsing event.

In the while loop, we get the name of the current node through the getName() method. If we find that the node name is equal to id, name or version, we call the nextText() method to obtain the specific content in the node. Every time we parse an app node, we print the obtained content. okay

The whole process is so simple. Let's test it. Run the NetworkTest project, and then click the Send Request button to observe the print log in logcat, as shown in the figure:

9.3.2 SAX parsing method

Although Pull parsing is very easy to use, it is not our only choice. SAX parsing is also a commonly used XML parsing method. Although its usage is more complex than Pull parsing, it will be clearer in semantics. Usually, we will create a new class, inherit from DefaultHandler, and override the five methods of the parent class, as shown below:

public class MyHandler extends DefaultHandler {

    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
    }

    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }
}

The startDocument() method will be called when XML parsing starts;

The startElement() method will be called when parsing a node;

The characters() method will be called when getting the content in the node;

The endElement() method will be called when a node is resolved;

The endDocument() method will be called when the entire XML parsing is completed.

Among them, the three methods of startElement(), characters() and endElement() have parameters, and the data parsed from XML will be passed into these methods in the form of parameters.

It should be noted that when obtaining the content in the node, the characters() method may be called many times, and some line breaks are also parsed as content. We need to control this situation in the code.

Now let's try to use SAX parsing to realize the same functions as in the previous section. Create a new ContentHandler class, inherit from DefaultHandler, and override the five methods of the parent class, as shown below:

package com.zhouzhou.networktest;

import android.util.Log;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class ContentHandler extends DefaultHandler {
    private String nodeName;
    private StringBuilder id;
    private StringBuilder name;
    private StringBuilder version;
    
    // The startDocument() method will be called when XML parsing starts;
    @Override
    public void startDocument() throws SAXException {
        super.startDocument();
        id = new StringBuilder();
        name = new StringBuilder();
        version = new StringBuilder();
    }
    
    // The startElement() method will be called when parsing a node;
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        super.startElement(uri, localName, qName, attributes);
        //Record the current node name
        nodeName = localName;
    }
    
    // The characters() method will be called when getting the content in the node;
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        // Determine which StringBuilder object to add the content to according to the current node name
        if ("id".equals(nodeName)) {
            id.append(ch,start,length);
        } else if ("name".equals(nodeName)) {
            name.append(ch,start,length);
        } else if ("version".equals(nodeName)) {
            version.append(ch,start,length);
        }
    }
    
    // The endElement() method will be called when a node is resolved;
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        super.endElement(uri, localName, qName);
        if ("app".equals(localName)) {
            Log.d("ContentHandler","id is " + id.toString().trim());
            Log.d("ContentHandler","name is " + name.toString().trim());
            Log.d("ContentHandler","version is " + version.toString().trim());
            //Finally, empty the StringBuilder
            id.setLength(0);
            name.setLength(0);
            version.setLength(0);
        }
    }
    
    // The endDocument() method will be called when the entire XML parsing is completed.
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }
}

As you can see, we first define a StringBuilder object for the id, name and version nodes, and initialize them in the startDocument() method.

Whenever you start parsing a node, the startElement() method will be called. The localName parameter records the name of the current node. Here we record it.

Then, when parsing the specific content in the node, the characters() method will be called. We will judge according to the current node name and add the parsed content to which StringBuilder object.

Finally, make a judgment in the endElement() method. If the app node has been parsed, print the contents of id, name and version.

It should be noted that at present, carriage return or line feed may be included in id, name and version. Therefore, we need to call trim() method before printing, and clear the contents of StringBuilder after printing, otherwise it will affect the next content reading. The next work is very simple. Modify the code in MainActivity as follows:

package com.zhouzhou.networktest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.xml.parsers.SAXParserFactory;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    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(this);
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.send_request) {
            //sendRequestWithHttpURLConnection();
            sendRequestWithOkHttp();
        }
    }

    private void sendRequestWithOkHttp() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder()
                            //The server address specified for access is the local computer
                            // The simulator takes 127.0.0.1 and localhost as itself by default. On the simulator, you can use 10.0.2.2 to replace 127.0.0.1 and localhost
                            .url("http://10.0.2.2:8099/get_data.xml")
                            .build();
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    //showResponse(responseData);
                    //parseXMLWithPull(responseData);
                    parseXMLWithSAX(responseData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void parseXMLWithSAX(String xmlData) {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            XMLReader xmlReader = factory.newSAXParser().getXMLReader();
            ContentHandler handler = new ContentHandler();
            //Set the instance of ContentHandler to XMLReader
            xmlReader.setContentHandler(handler);
            //Start parsing
            xmlReader.parse(new InputSource(new StringReader(xmlData)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseXMLWithPull(String xmlData) {
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlData));
            int eventType = xmlPullParser.getEventType();
            String id = "";
            String name = "";
            String version = "";
            while (eventType != XmlPullParser.END_DOCUMENT) {
                String nodeName = xmlPullParser.getName();
                switch (eventType) {
                    //Start parsing a node
                    case XmlPullParser.START_TAG: {
                        if ("id".equals(nodeName)) {
                            id = xmlPullParser.nextText();
                        } else if ("name".equals(nodeName)) {
                            name = xmlPullParser.nextText();
                        } else if ("version".equals(nodeName)) {
                            version = xmlPullParser.nextText();
                        }
                        break;
                    }
                    //Finish parsing a node
                        case XmlPullParser.END_TAG: {
                            if ("app".equals(nodeName)) {
                                Log.d("MainActivity","id is " + id);
                                Log.d("MainActivity","name is " + name);
                                Log.d("MainActivity","version is " + version);
                            }
                            break;
                        }
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendRequestWithHttpURLConnection() {
        //Start a thread to initiate a network request
        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(8000);
                    connection.setReadTimeout(8000);
                    InputStream inputStream = connection.getInputStream();
                    //Next, read the obtained input stream
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                    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() {
                //Conduct Ui operation here and display the results to the interface
                responseText.setText(response);
            }
        });
    }
}

After getting the data returned by the server, we call the parseXMLWithSAX() method to parse the XML data this time. In the parseXMLWithSAX() method, first create an object of SAXParserFactory, then get the XMLReader object, then set the ContentHandler instance we wrote to the XMLReader, and finally call the parse() method to start parsing.

Now run the program again, click the Send Request button and observe the print log in logcat. You will see the same result as above.

In addition to Pull parsing and SAX parsing, there is also a DOM parsing method that is quite commonly used. However, we won't explain it here. If you are interested, you can check the relevant materials yourself.

9.4 parsing JSON format data

How to parse data in JSON format. Compared with XML, the main advantage of JSON is that it is smaller and can save traffic when transmitted on the network. But the disadvantage is that it has poor semantics and looks less intuitive than XML.

Before starting, you also need to create a new get in the F:\ApacheServer\htdocs directory_ data. JSON file, then edit this file and add the following JSON format content:

[{"id":"5","version":"5.5","name":"Clash of Clans"},
 {"id":"6","version":"7.0","name":"Boom Beach"},
 {"id":"7","version":"3.5","name":"Clash Royale"}]

This is accessed in the browser http://127.0.0.1:8099/get_data.json This website should appear as shown in the figure:

Having prepared the data in JSON format, let's learn how to parse these data in Android programs.

9.4.1 using JSONObject

Similarly, there are many ways to parse JSON data. You can use the official JSONObject or Google's open source library GSON. In addition, some third-party open source libraries such as Jackson and FastJSON are also very good. In this section, we will learn the usage of the first two parsing methods. Modify the code in MainActivity as follows:

package com.zhouzhou.networktest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONObject;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;

import javax.xml.parsers.SAXParserFactory;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    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(this);
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.send_request) {
            //sendRequestWithHttpURLConnection();
            sendRequestWithOkHttp();
        }
    }

    private void sendRequestWithOkHttp() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder()
                            //The server address specified for access is the local computer
                            // The simulator takes 127.0.0.1 and localhost as itself by default. On the simulator, you can use 10.0.2.2 to replace 127.0.0.1 and localhost
                            .url("http://10.0.2.2:8099/get_data.json")
                            .build();
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    //showResponse(responseData);
                    //parseXMLWithPull(responseData);
                    //parseXMLWithSAX(responseData);
                    parseJSONWithJSONObject(responseData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void parseJSONWithJSONObject(String jsonData) {
        try {
            JSONArray jsonArray = new JSONArray(jsonData);
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String id = jsonObject.getString("id");
                String name = jsonObject.getString("name");
                String version = jsonObject.getString("version");
                Log.d("MainActivity","id is " + id);
                Log.d("MainActivity","name is " + name);
                Log.d("MainActivity","version is " + version);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseXMLWithSAX(String xmlData) {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            XMLReader xmlReader = factory.newSAXParser().getXMLReader();
            ContentHandler handler = new ContentHandler();
            //Set the instance of ContentHandler to XMLReader
            xmlReader.setContentHandler(handler);
            //Start parsing
            xmlReader.parse(new InputSource(new StringReader(xmlData)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseXMLWithPull(String xmlData) {
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlData));
            int eventType = xmlPullParser.getEventType();
            String id = "";
            String name = "";
            String version = "";
            while (eventType != XmlPullParser.END_DOCUMENT) {
                String nodeName = xmlPullParser.getName();
                switch (eventType) {
                    //Start parsing a node
                    case XmlPullParser.START_TAG: {
                        if ("id".equals(nodeName)) {
                            id = xmlPullParser.nextText();
                        } else if ("name".equals(nodeName)) {
                            name = xmlPullParser.nextText();
                        } else if ("version".equals(nodeName)) {
                            version = xmlPullParser.nextText();
                        }
                        break;
                    }
                    //Finish parsing a node
                        case XmlPullParser.END_TAG: {
                            if ("app".equals(nodeName)) {
                                Log.d("MainActivity","id is " + id);
                                Log.d("MainActivity","name is " + name);
                                Log.d("MainActivity","version is " + version);
                            }
                            break;
                        }
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendRequestWithHttpURLConnection() {
        //Start a thread to initiate a network request
        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(8000);
                    connection.setReadTimeout(8000);
                    InputStream inputStream = connection.getInputStream();
                    //Next, read the obtained input stream
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                    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() {
                //Conduct Ui operation here and display the results to the interface
                responseText.setText(response);
            }
        });
    }
}

Now run the program again and click the Send Request button. The results are as follows:

9.4.2 using GSON

If you think it is very simple to use JSONObject to parse JSON data, you will be too satisfied. GSON open source library provided by Google( https://github.com/google/gson/ )It can make the work of parsing JSON data so simple that you can't imagine. Then we must not miss this learning opportunity.

However, GSON has not been added to the official Android API, so if you want to use this function, you must add the dependency of GSON Library in the project. Edit app / build Gradle file, add the following contents to the dependencies closure:

dependencies {
  implementation 'com.google.code.gson:gson:2.9.0'
}

So, where is the magic of GSON library? In fact, it can automatically map a string in JSON format into an object, so we don't need to write code manually for parsing. For example, a piece of data in JSON format is as follows:

{"name":"Tom","age":20}

Then we can define a Person class and add the fields name and age. Then we can automatically parse JSON data into a Person object by simply calling the following code:

Gson gson = new Gson();
Person person = gson.fromJson(jsonData,Person.class);

If a JSON array needs to be parsed, it will be a little troublesome. We need to pass the expected data type into the fromjason () method with the help of TypeToken, as shown below:

List<Person> appList = gson.fromJson(gsonData,new TypeToken<List<Person>>(){}.getType());

This is the basic usage. Let's really try it. First, add an App class and add three fields: id, name and version, as shown below:

package com.zhouzhou.networktest;

public class App {
    private String id;
    private String name;
    private String version;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }
}

Then modify the code in MainActivity as follows:

package com.zhouzhou.networktest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import org.json.JSONArray;
import org.json.JSONObject;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;

import javax.xml.parsers.SAXParserFactory;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    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(this);
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.send_request) {
            //sendRequestWithHttpURLConnection();
            sendRequestWithOkHttp();
        }
    }

    private void sendRequestWithOkHttp() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder()
                            //The server address specified for access is the local computer
                            // The simulator takes 127.0.0.1 and localhost as itself by default. On the simulator, you can use 10.0.2.2 to replace 127.0.0.1 and localhost
                            .url("http://10.0.2.2:8099/get_data.json")
                            .build();
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    //showResponse(responseData);
                    //parseXMLWithPull(responseData);
                    //parseXMLWithSAX(responseData);
                    //parseJSONWithJSONObject(responseData);
                    parseJSONWithGSON(responseData);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    private void parseJSONWithGSON(String gsonData) {
        Gson gson = new Gson();
        List<App> appList = gson.fromJson(gsonData,new TypeToken<List<App>>(){}.getType());
        for (App app : appList) {
            Log.d("MainActivity","id is " + app.getId());
            Log.d("MainActivity","name is " + app.getName());
            Log.d("MainActivity","version is " + app.getVersion());
        }
    }

    private void parseJSONWithJSONObject(String jsonData) {
        try {
            JSONArray jsonArray = new JSONArray(jsonData);
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String id = jsonObject.getString("id");
                String name = jsonObject.getString("name");
                String version = jsonObject.getString("version");
                Log.d("MainActivity","id is " + id);
                Log.d("MainActivity","name is " + name);
                Log.d("MainActivity","version is " + version);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseXMLWithSAX(String xmlData) {
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            XMLReader xmlReader = factory.newSAXParser().getXMLReader();
            ContentHandler handler = new ContentHandler();
            //Set the instance of ContentHandler to XMLReader
            xmlReader.setContentHandler(handler);
            //Start parsing
            xmlReader.parse(new InputSource(new StringReader(xmlData)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parseXMLWithPull(String xmlData) {
        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            XmlPullParser xmlPullParser = factory.newPullParser();
            xmlPullParser.setInput(new StringReader(xmlData));
            int eventType = xmlPullParser.getEventType();
            String id = "";
            String name = "";
            String version = "";
            while (eventType != XmlPullParser.END_DOCUMENT) {
                String nodeName = xmlPullParser.getName();
                switch (eventType) {
                    //Start parsing a node
                    case XmlPullParser.START_TAG: {
                        if ("id".equals(nodeName)) {
                            id = xmlPullParser.nextText();
                        } else if ("name".equals(nodeName)) {
                            name = xmlPullParser.nextText();
                        } else if ("version".equals(nodeName)) {
                            version = xmlPullParser.nextText();
                        }
                        break;
                    }
                    //Finish parsing a node
                        case XmlPullParser.END_TAG: {
                            if ("app".equals(nodeName)) {
                                Log.d("MainActivity","id is " + id);
                                Log.d("MainActivity","name is " + name);
                                Log.d("MainActivity","version is " + version);
                            }
                            break;
                        }
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void sendRequestWithHttpURLConnection() {
        //Start a thread to initiate a network request
        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(8000);
                    connection.setReadTimeout(8000);
                    InputStream inputStream = connection.getInputStream();
                    //Next, read the obtained input stream
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                    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() {
                //Conduct Ui operation here and display the results to the interface
                responseText.setText(response);
            }
        });
    }
}

Now re run the program, click the Send Request button, observe the print log in logcat, and test OK!

9.5 best practices in network programming

Maybe you haven't found that our writing method is actually very problematic before. Because an application may use network functions in many places, and the code for sending HTTP requests is basically the same. If we write the code for sending HTTP requests every time, it is obviously a very bad practice.

Yes, usually, we should extract these general network operations into a public class and provide a static method. When we want to initiate a network request, we just need to simply call this method. For example, use the following wording:

package com.zhouzhou.networktest;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpUtil {
    public static String sendHttpRequest(String address) {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(address);
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(8000);
            connection.setReadTimeout(8000);
            connection.setDoInput(true);
            connection.setDoOutput(true);
            InputStream inputStream = connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
}

In the future, whenever an HTTP request needs to be initiated, it can be written as follows:

String address = "http://www.baidu.com"
String response = HttpUtil.sendHttpRequest(address);

After obtaining the data of the server response, we can analyze and process it. However, it should be noted that network requests are usually time-consuming operations, and the thread is not opened inside the sendHttpRequest() method, which may cause the main thread to be blocked when calling the sendHttpRequest() method.

You might say, it's very simple. Don't you solve this problem by opening a thread inside the sendhttprequest () method? In fact, it's not as easy as you think, because if we open a thread in the sendHttpRequest() method to initiate an HTTP request, the data of the server response cannot be returned. All the time-consuming logic is carried out in the sub thread. The sendHttpRequest() method will finish the execution before the server has time to respond, and of course, it can't return the response data.

So what should I do in this situation? In fact, the solution is not difficult. Just use the callback mechanism of Java. Let's learn how to use the callback mechanism. First, you need to define an interface. For example, name it HttpCallbackListener. The code is as follows:

package com.zhouzhou.networktest;

public interface HttpCallbackListener {
    void onFinish(String response);
    void onError(Exception e);
}

We can see that we have defined two methods in the interface. onFinish() means to call when the server successfully responds to our request, and onError() means to call when there is an error in network operation. Both methods have parameters. The parameters in onFinish() method represent the data returned by the server, while the parameters in onError() method record the details of the error. Then modify the code in HttpUtil as follows:

package com.zhouzhou.networktest;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpUtil {
    public static void sendHttpRequest(final String address,final HttpCallbackListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                try {
                    URL url = new URL(address);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    connection.setDoInput(true);
                    connection.setDoOutput(true);
                    InputStream inputStream = connection.getInputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    if (listener != null) {
                        // Callback onFinish() method
                        listener.onFinish(response.toString());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    // Callback onError() method
                    listener.onError(e);
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

First, add an HttpCallbackListener parameter to the sendHttpRequest() method, and open a sub thread inside the method, and then perform specific network operations in the sub thread.

Note that data cannot be returned through the return statement in the child thread, so here we pass the data responded by the server into the onFinish() method of HttpCallbackListener. If an exception occurs, we pass the cause of the exception into the onError() method.

Now the sendHttpRequest() method receives two parameters, so we need to pass in the instance of HttpCallbackListener when calling it, as shown below:

HttpUtil.sendHttpRequest(address,new HttpCallbackListener(){
    @Override
    public void onFinish(String response) {
        //Here, the specific logic is executed according to the returned content
    }
    @Override
    public void onError(Exception e) {
        //The exception is handled here
    }
});

In this way, when the server responds successfully, we can process the response data in the onFinish() method. Similarly, if an exception occurs, you can handle the exception in the onError() method.

In this way, we skillfully use the callback mechanism to successfully return the response data to the caller. However, you will find that the above writing method of using HttpURLConnection is still relatively complex, so will using OkHttp become easier? The answer is yes, and it's much simpler. Let's take a look at it in detail. Add a sendOkHttpRequest() method to HttpUtil, as follows:

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);
    }
}

As you can see, there is an OkHttp3 in the sendOkHttpRequest() method Callback parameter, which is a callback interface built in the OkHttp library, similar to the HttpCallbackListener we just wrote.

Then in the client Instead of calling the execute() method as before, newcall () called an enqueue() method and put OkHttp3 Callback parameter passed in. OkHttp has opened the sub thread for us inside the enqueue() method. Then it will execute the HTTP request in the sub thread and call back the final request result to OkHttp3 In the callback. Then we can write this when calling sendOkHttpRequest() method:

HttpUtil.sendOkHttpRequest("http://www.baidu.com",new okhttp3.Callback(){
    @Override
    public void onResponse(Call call,Response response) throws IOException {
        //Get the specific content returned by the server
        String responseData = response.body().string();
    }
    @Override
    public void onFailure(Call call,IOException e) {
        //The exception is handled here
    }
});

It can be seen from this that the interface design of OkHttp is indeed very humanized. It encapsulates some common functions well, so that we can complete more complex network operations with only a small amount of code. Of course, this is not the whole of OkHttp. We will continue to learn other related knowledge later.

In addition, it should be noted that whether HttpURLConnection or OkHttp is used, the final callback interface still runs in the sub thread. Therefore, we can't perform any UI operations here unless we use the runOnUiThread() method to perform thread conversion. As for the specific reasons, we will soon learn in the next chapter.

9.6 summary and comments

This chapter mainly studies the knowledge of using HTTP protocol for network interaction in Android. Although there are many kinds of network communication protocols supported in Android, HTTP protocol is undoubtedly the most commonly used one. Generally, we have two ways to send HTTP requests: HttpURLConnection and OkHttp.

Then I learned the parsing methods of XML and JSON format data. Whether XML or JSON, they have a variety of parsing methods. If other parsing methods need to be used in future work, I can learn by myself.

Finally, I mainly learned how to use the callback mechanism of Java to return the data responded by the server. In fact, in addition, there are many places where you can use Java's callback mechanism. I hope you can draw inferences from one example.

Posted by dartcol on Wed, 11 May 2022 12:59:11 +0300