Android Learning Notes-Network Technology

Keywords: Android network xml JSON

HTTP protocol and server side are used to interact with each other on the mobile phone, and the data returned from the server side is parsed. This is the most commonly used network technology for Android.

1,WebView

With the help of WebView control, a browser can be embedded in the application program.
Create a new WebViewTest project and modify the layout file:

<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>

Modify the main program:

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);//GetSettings () is used to set some browser properties, where WebView supports JavaScript scripts
        webView.setWebViewClient(new WebViewClient());//When you need to jump from one page to another, you want the target page to still be displayed in the current WebView instead of opening the browser.
        webView.loadUrl("http://www.baidu.com");
    }
}

You also need to declare permissions to access the network:

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

Before starting the operation, it is necessary to ensure that the simulator (computer access to the Internet) or the mobile phone are connected to the Internet.

Click on the link to browse more pages.

2. Accessing the Network Using HTTP Protocol

Principle: The client sends an HTTP request to the server. When the server receives the request, it will return some data to the client. Then the client can parse and process the data.

WebView has handled the following steps in the background: sending HTTP requests, receiving service responses, parsing returned data and displaying pages, which are well encapsulated. You need to understand the way HTTP requests are sent manually.

(1) Using HttpURLConnection

Before Android 6.0, there were two ways to send HTTP requests: HttpURLConnection and HttpClient. In Android 6.0, the HttpClient function was completely removed.

Usage of HttpURLConnection:
a. Get an instance of HttpURLConnection. a new URL object, passing in the destination network address, calls the openConnection() method:

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

b. The method used to set up HTTP requests. There are two common types: GET (which wants to get data from the server) and POST (which wants to submit data to the server). Writing:

connection.setRequestMethod("GET");

c. Customize freely, such as setting connection timeouts, reading supermarket milliseconds, and some headers the server wants. Such as:

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

d. Call getInputStream() to get the input stream returned by the server and read the input stream:

InputStream in = connection.getInputStream();

e. Call the disconnect() method to close the HTTP connection. Such as:

connection.disconnect();

For example, create a new NetworkTest project and modify the layout file:

<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: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>

Modify the main program:

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 v) {
        if (v.getId() == R.id.send_request){
            sendRequestWithHttpURLConnection();
        }
    }
    private void sendRequestWithHttpURLConnection(){
        //Open threads to initiate network requests
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                BufferedReader reader = null;
                try {
                    URL url = new URL("https://hao.360.cn/");
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    InputStream in = connection.getInputStream();
                    //Read the acquired input stream
                    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){
        //Android does not allow UI operations in sub-threads. It is necessary to switch threads to main threads and update UI elements through this method.
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                //UI operations are performed here, and the results are displayed on the interface.
                responseText.setText(response);
            }
        });
    }
}

You also need to add a declaration of access to the network. Run the program and click the button:

Note: If the address is incorrect, the prompt of Network Security Config: No Network Security Config specified, using platform default will appear.

This is the HTML code that the server returns, but usually the browser parses the code into a beautiful web page before showing it.

If you want to submit data to the server, you just need to change the HTTP request method to POST and write out the data to be submitted before getting the input stream. Each data must exist as a key-value pair, separated by a "&" symbol, such as submitting a username and password:

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

(2) Using OkHttp

Network Communication Database (Square Corporation Development), OkHttp Project Home Page Address: https://github.com/square/okhttp

Before using, you need to add OkHttp library dependencies, open app/buid.gradle, and add the following in dependencies closure:

compile 'com.squareup.okhttp3:okhttp:3.8.1'

Adding this dependency will automatically download two libraries: OkHttp and Okio libraries (which are the communication basis of the former).

Usage:
a. Create an OkHttpClient instance as follows:

OkHttpClient client = new OkHttpClient();

b. If you want to initiate an HTTP request, you need to create a Request object:

Request request = new Request.Builder().url("http://www.baidu.com").build();//The build() method can be preceded by many other methods to enrich the Request object

c. Call OkHttpCilent's newCall() method to create a Call object and call its execute() method to send the request and get the data returned by the server:

Response response = client.newCall(request).execute();//Response object is the data returned by the server
String responseData = response.body().string();//Get the specific content of the return

d. If a POST request is initiated, the RequestBody object needs to be constructed to store the parameters to be submitted:

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

Then call the post() method in Request.Builder and pass in the RequestBody object:

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

Next, call the execute() method to send the request and get the data returned by the server.

Modify MainActivity:

...
 public void onClick(View v) {
        if (v.getId() == R.id.send_request){
            sendRequestWithOkHttp();
        }
    }
    private void sendRequestWithOkHttp(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder().url("https://hao.360.cn/").build();
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    showResponse(responseData);//Display the data returned by the server to the interface
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    ...

Rerun the program and click the button:

It is the same function as HttpURLConnection.

3. Parsing XML Format Data

Usually, every application that needs to access the network has its own server. There are two most commonly used formats for transferring data over the network: XML and JSON.

Parsing data in XML format:
First, you need to have data in XML format. Build the simplest Web server (many for choice, here using Apache server, official download address: http://httpd.apache.org/download.cgi)
Detailed installation steps are as follows: http://blog.csdn.net/lanjingling09/article/details/77034964

Enter ____________. \ Under Apache HtDocs directory, create a new file named get_data.xml and edit it:

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

Access in Browser http://127.0.0.1/get_data.xml:

(1) Pull analysis

There are many ways to parse data in XML format. Pull and SAX parsing are two commonly used methods.

Modify MainActivity:

    ...
    public void onClick(View v) {
        if (v.getId() == R.id.send_request){
            sendRequestWithOkHttp();
        }
    }
    private void sendRequestWithOkHttp(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    **Request request = new Request.Builder().url("http://10.0.2.2/get_data.xml").build();//http://10.0.2.2/ For the simulator, the local IP address of the computer**
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    **parseXMLWithPull(responseData);//Resolve the data returned by the server**
                } 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();//Get the current parse event
            String id = "";
            String name = "";
            String version = "";
            while (eventType != XmlPullParser.END_DOCUMENT){
                //The parsing event is not XmlPullParser.END_DOCUMENT, indicating that the parsing work has not been completed.
                String nodeName = xmlPullParser.getName();//Get the current node name
                switch (eventType){
                    //Start parsing a node
                    case XmlPullParser.START_TAG:
                        if ("id".equals(nodeName)){
                            id = xmlPullParser.nextText();//Get the specific content of the node
                        }else if ("name".equals(nodeName)){
                            name = xmlPullParser.nextText();
                        }else if ("version".equals(nodeName)){
                            version = xmlPullParser.nextText();
                        }
                        break;
                    //Complete parsing of a node
                    case XmlPullParser.END_TAG:
                        if ("app".equals(nodeName)){
                            //Print the captured content after parsing an app node
                            Log.d("MainActivity", "id is " + id);
                            Log.d("MainActivity", "name is " + name);
                            Log.d("MainActivity", "version is " + version);
                        }
                        break;
                    default:
                        break;
                }
                eventType = xmlPullParser.next();//Get the next parse event
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    ...

Run the program, click the Send button, and view the log:

Successful analysis.

(2) SAX parsing

It's a little more complicated than Pull's analysis, but it's clearer.

Usually a new class inheritor DefaultHandler is created and five methods of the parent class are overridden.

The new ContentHandler class inherits from DefaultHandler (org.xml.sax.helpers):

public class ContentHandler extends DefaultHandler {
    private String nodeName;
    private StringBuilder id;
    private StringBuilder name;
    private StringBuilder version;

    //Called at the beginning of XML parsing
    @Override
    public void startDocument() throws SAXException {
        id = new StringBuilder();
        name = new StringBuilder();
        version = new StringBuilder();
    }

    //Called when starting to parse a node
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        //Record the current node name
        nodeName = localName;
    }

    //Called when getting the content of the node, it will be called many times
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        //Determine which StringBuilder object to add content to based on 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);
        }
    }

    //Called upon completion of parsing a node
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if ("app".equals(localName)){
            Log.d("ContentHandler", "id is " + id.toString().trim());//The id name version may contain carriage return or line break characters, which need to be removed by calling the trim() method.
            Log.d("ContentHandler", "name is " + name.toString().trim());
            Log.d("ContentHandler", "version is " + version.toString().trim());
            //Finally, clear the StringBuilder to avoid affecting the next content reading
            id.setLength(0);
            name.setLength(0);
            version.setLength(0);
        }
    }

    //Called when the entire XML parsing is completed
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }
}

Modify MainActivity:

...
private void sendRequestWithOkHttp(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder().url("http://10.0.2.2/get_data.xml").build();//http://10.0.2.2/ For the simulator, the local IP address of the computer
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    **parseXMLWithSAX(responseData);//Resolve the data returned by the server**
                } 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();
            //Setting an instance of ContentHandler in XMLReader
            xmlReader.setContentHandler(handler);
            //Start parsing
            xmlReader.parse(new InputSource(new StringReader(xmlData)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    ...

Run the program, click the button, and view the log:

The results are the same. DOM parsing is also available.

4. Parsing JSON data

JSON has smaller volume and less traffic than XML, but its semantics is not as intuitive as XML.

In... \ Create a new get_data.json file in the Apache HtDocs directory:

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

(1) Using JSONObject

There are also many ways to parse JSON data, using official JSONObject, Google's open source library GSON, or third-party open source libraries such as Jackson, FastJSON, etc.

Using JSONObject, modify MainActivity:

...
private void sendRequestWithOkHttp(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    **Request request = new Request.Builder().url("http://10.0.2.2/get_data.json").build();//http://10.0.2.2/ For the simulator, the local IP address of the computer**
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    **parseJSONWithJSONObject(responseData);**
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    private void parseJSONWithJSONObject(String jsonData){
        try {
            JSONArray jsonArray = new JSONArray(jsonData);//Time JSON Array Defined in Server
            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();
        }
    }
    ...

Running the program, you will see the log information.

(2) Using GSON

It's simpler. Since it is not added to the official Android API, it is necessary to add GSON library dependencies to the project to use it:

compile 'com.google.code.gson:gson:2.7'

It can automatically map a JSON-formatted string to an object (defining a class correspondence) without manual code parsing.
If you need to parse a JSON array, you need to use TypeToken to pass the expected parsed data type into the fromJson() method, such as:

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

For example, first create a new App class:

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

Modify MainActivity:

...
private void sendRequestWithOkHttp(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    OkHttpClient client = new OkHttpClient();
                    Request request = new Request.Builder().url("http://10.0.2.2/get_data.json").build();//http://10.0.2.2/ For the simulator, the local IP address of the computer
                    Response response = client.newCall(request).execute();
                    String responseData = response.body().string();
                    **parsJSONWithGSON(responseData);**
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    private void parsJSONWithGSON(String jsonData){
        Gson gson = new Gson();
        List<App> appList = gson.fromJson(jsonData, 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());
        }
    }
    ...

Running the program, the effect is the same.

5. Best Practices of Network Programming

Applications are likely to use network functionality in many places, and the code for sending HTTP requests is basically the same, so generic network operations should usually be extracted into a common class example and provided with a static method. Considering that this time-consuming operation needs to be completed by opening sub-threads and can receive the returned data in real time, the Java callback mechanism is used here to complete the data acquisition:

Define an HttpCallbackListener interface:

public interface HttpCallbackListener {
    void onFinish(String response);//When the server successfully responds to the request, the parameter is the data returned by the server.
    void onError(Exception e);//When an error occurs in network operation, the parameter records the details of the error.
}

Next, create a new class (common class for common network operations):

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 in = connection.getInputStream();
                    //Read the acquired input stream
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    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) {
                    if (listener != null){
                        //Callback onError method
                        listener.onError(e);
                    }
                }finally {
                    if (connection != null){
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
}

In this class, there is a sendHttpRequest() method with two parameters (the second parameter is the defined HttpCallbackListener interface parameter).
The HttpCallbackListener instance needs to be passed in at the time of invocation:

HttpUtil.sendHttpRequest(address, new HtpCallbackListener(){
    @Override
    public void onFinish(String response){
        //Here you execute specific logic based on the returned content
}
    @Override
    public void onError(Exception e){
        //Deal with abnormal situations here
}
});

The response data is successfully returned to the caller using the callback mechanism.

If OkHttp is used, a sendOkHttpRequest() method is added to HttpUtil:

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);//Subthreads are already in place within the enqueue method
        }
    }

Call the sendOkHttpRequest() method:

HttpUtil.sendOkHttpRequest("https://www.baidu.com", new okhttp3.Callback(){
    @Override
    public void onResponse(Call call, Response response) throws IOException{
        //Get the specific data returned by the server
        String reponseData = reponse.body().string();
}
    @Override
    public void onFailure(Call call, IOException e){
        //Deal with abnormal situations here
}
});

Whether using HttpURLConnection or OkHttp, the final callback interface is still running in the sub-thread, so no UI operation can be performed here unless thread conversion is performed by using the runOnUiThread() method.

Posted by Nukedesign on Wed, 05 Jun 2019 15:18:59 -0700