1. 程式人生 > >第一行程式碼——第九章:看看精彩的世界——使用網路技術

第一行程式碼——第九章:看看精彩的世界——使用網路技術

目錄:

9.1 WebView的用法

9.2 使用HTTP協議訪問網路

9.2.1 使用HttpURLConnection

9.2.2 使用OkHttp

9.3 解析XML格式資料

9.3.1 Pull解析方式

9.3.2 SAX解析方式

9.4 解析JSON格式資料

9.4.1 使用JSONObject

9.4.2 使用GSON

9.5 網路程式設計的最佳實踐

9.6 小結與點評


知識點:

9.1 WebView的用法

要求在應用程式裡展示一些網頁,不允許開啟系統瀏覽器,這時候就要藉助WebView控制元件了。

建立WebActivity,最簡單

package com.dak.administrator.firstcode.net;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.dak.administrator.firstcode.R;

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 WebActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web);
        WebView webView = findViewById(R.id.web_view);
        //支援JavaScript指令碼
        webView.getSettings().setJavaScriptEnabled(true);
        webView.setWebViewClient(new WebViewClient());
        webView.loadUrl("http://www.baidu.com");
        

    }

 
}

Xml頁面:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    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>

當然不要忘記加上網路許可權。

9.2 使用HTTP協議訪問網路

HTTP 協議,其工作原理很簡單:客戶端向伺服器發出一條 HTTP 請求,伺服器收到請求後會返回一些資料給客戶端,然後客戶端再對這些資料進行解析和處理。

9.2.1 使用HttpURLConnection

這裡我們使用HttpURLConnection ,對於任何網路請求,我們應該在子執行緒中進行。

這裡可以new Thread 一下,我就不寫了。

 private void HttpUrl(){
        HttpURLConnection connection = null;
        try {
            connection = (HttpURLConnection) new URL("www.baidu.com").openConnection();
            InputStream s=connection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(s));
            String str = "";
            while ((str = reader.readLine()) != null) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {

                    }
                });
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

若是想要提交資料給伺服器只需把請求方法改為 POST,並在獲取輸入流之前把要提交的資料寫出即可。注意每條資料要以鍵值對的形式存在,資料與資料之間用 “&” 隔開,比如向伺服器提交使用者名稱和密碼可寫成:

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

9.2.2 使用OkHttp

Okhttp 是由大名鼎鼎的Square公司開發的,這個公司在開源事業上面貢獻良多,除了Okhttp外,還開發了像Picasso、Retrofit等著名的開源專案。

Okhttp 專案主頁地址是:https://github.com/square/okhttp

在使用 OKHttp 前,需要在專案中新增 OKHttp 庫的依賴,如下:

   implementation 'com.squareup.okhttp3:okhttp:3.11.0'

接下來建立Activity 

package com.dak.administrator.firstcode.net;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.dak.administrator.firstcode.R;

import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;

import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class OkHttpActivity extends AppCompatActivity {

    private static final String TAG = "OkHttpActivity===";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ok_http);

        getRequest();
    }

    private void getRequest() {
        try {
            OkHttpClient client = new OkHttpClient();
            Request request = new Request.Builder()
                    .url("http://www.baidu.com")
                    .build();
            Response response = client.newCall(request).execute();
            String responseData = response.body().string();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void postRequest() {
        try {
            OkHttpClient client = new OkHttpClient();
            RequestBody requestBody = new FormBody.Builder()
                    .add("username", "admin")
                    .add("password", "123456")
                    .build();
            Request request = new Request.Builder()
                    .url("www.baidu.com")
                    .post(requestBody)
                    .build();

            Response response = client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

相比 HttpURLConnection,OKHttp 簡單易用,若是發起一條 POST 請求,會比 GET 請求稍微複雜點,需要構建一個 RequestBody 物件來存放待提交的引數:

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

然後在 Request.Builder 中呼叫一下 post() 方法,並將 RequestBody 物件傳入:

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

 

9.3 解析XML格式資料

在網路傳輸資料時,最常用的格式有兩種:XMl和JSON,下面我們就來一個個地學習吧。

首先建立一個xml檔案

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

9.3.1 Pull解析方式

  private void xmlWithPull(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) {
                    //解析某個節點
                    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;
                        //完成解析某個節點
                    case XmlPullParser.END_TAG:
                        if ("app".equals(nodeName)) {
                            Log.d(TAG, "xmlWithPull: id"+id);
                            Log.d(TAG, "xmlWithPull: name"+name);
                            Log.d(TAG, "xmlWithPull: version"+version);
                        }
                        break;
                    default:
                        break;
                }
                eventType = xmlPullParser.next();
            }
        } catch (XmlPullParserException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

9.3.2 SAX解析方式

 private void xmlWithSax(String xmlData){
        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            XMLReader xmlReader = factory.newSAXParser().getXMLReader();
            MyHandler handler=new MyHandler();
            xmlReader.setContentHandler(handler);
            xmlReader.parse(new InputSource(xmlData));
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

建立所需要的DefaultHandler

package com.dak.administrator.firstcode.net;

import android.util.Log;

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

/**
 * Created by Administrator on 2018/10/29.
 */

public class MyHandler extends DefaultHandler {

    private static final String TAG = "MyHandler===";
    String nodeName;
    StringBuilder id,name, version;

    /**
     * 開始解析時呼叫
     *
     * @throws SAXException
     */
    @Override
    public void startDocument() throws SAXException {
        id = new StringBuilder();
        name = new StringBuilder();
        version = new StringBuilder();
    }

    /**
     * 完成整個xml解析後使用
     *
     * @throws SAXException
     */
    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
    }

    /**
     * 開始解析 某個節點時時呼叫
     *
     * @param uri
     * @param localName
     * @param qName
     * @param attributes
     * @throws SAXException
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        nodeName = localName;//記錄當前節點名
    }

    /**
     * 完成解析某個節點呼叫
     *
     * @param uri
     * @param localName
     * @param qName
     * @throws SAXException
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        if ("app".equals(localName)) {
            Log.d(TAG, "xmlWithPull: id"+id.toString().trim());
            Log.d(TAG, "xmlWithPull: name"+name.toString().trim());
            Log.d(TAG, "xmlWithPull: version"+version.toString().trim());
            id.setLength(0);
            name.setLength(0);
            version.setLength(0);
        }

    }

    /**
     * 獲取某個節點內容時呼叫
     * @param ch
     * @param start
     * @param length
     * @throws SAXException
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        super.characters(ch, start, length);
        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);
        }

    }
}

9.4 解析JSON格式資料

9.4.1 使用JSONObject

/**
     * 用 JSONObject 解析
     * @param jsonData 需要解析的資料
     */
    private void parseJSONWithJSONObject(String jsonData) {
        try {
            // 把需要解析的資料傳入到 JSONArray 物件中
            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 sex = jsonObject.getString("sex");
                Log.d("JSONObject解析", "id is "+id);
                Log.d("JSONObject解析", "name is "+name);
                Log.d("JSONObject解析", "sex is "+sex);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

9.4.2 使用GSON

新增相關依賴

  implementation 'com.google.code.gson:gson:2.8.2'

 GSON 可以將一段 JSON 格式的字串自動對映成一個物件,從而不需要手動去編寫程式碼進行解析了。

  比如解析一段 JSON 格式資料:

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

  就可以定義一個 Person 類,並加入 name 和 age 兩欄位,然後只需呼叫如下程式碼就可以將 JSON 資料自動解析成一個 Person 物件:

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

  若解析一段 JSON 陣列會麻煩些,需要藉助 TypeToken 把期望解析成的資料型別傳入到 fromJson() 方法中:

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

  GSON 的基本用法就是這樣。下面來解析上面的 JSON 文字,首先新增一個 Student 類:

public class Student {
    
    private String id;
    private String name;
    private String sex;

    // Getter and Setter
    . . .
}

  接下來就非常簡單了,程式碼如下:

   /**
     *  用 GSON 解析
     * @param jsonData
     */
    private void parseJSONWithGSON(String jsonData){
        Gson gson = new Gson();
        List<Student>studentList = gson.fromJson(jsonData,new TypeToken<List<Student>>(){}.getType());
        for (Student student:studentList){
            Log.d("GSON解析", "id is "+student.getId());
            Log.d("GSON解析", "name is "+student.getName());
            Log.d("GSON解析", "sex is "+student.getSex());
        }
    }

 

9.5 網路程式設計的最佳實踐

這裡涉及到兩個問題:首先是網路連線的程式碼比較長,所以可以把它封裝在一個類裡面,然後設定一個靜態的方法,每次要進行網路連線的時候呼叫它就可以了;還有一個問題是由於網路連線需要開啟子執行緒,然而子執行緒又不能返回資料,所以需要設定回撥函式。 
  這裡首先建立一個介面,意思就是回撥函式:

public interface HttpCallbackListener {

    void finish(String response);

    void onError(Exception e);

}

然後定義HttpUtil,作為網路連線的通用類。

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.setDoInput(true);
                    connection.setDoOutput(true);
                    InputStream in = connection.getInputStream();
                    BufferedReader br = new BufferedReader(new InputStreamReader(in));
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line);
                    }
                    listener.finish(sb.toString());
                }
                catch (Exception e) {
                    e.printStackTrace();
                    listener.onError(e);
                }
                finally {
                    if (connection != null) connection.disconnect();
                }
            }
        }).start();
    }
}

這裡就是傳送請求然後接收返回的資料的程式碼,增加的地方就是呼叫介面的函式。然後我們在MainActivity裡呼叫它,注意介面都是要先例項化之後才可以呼叫。

private void sendHttpRequestWithHttpUtil() {
    String address = "http://www.baidu.com";
    HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {

        @Override
        public void onError(Exception e) {
            Log.d("MainActivity", "連線失敗");
        }

        @Override
        public void finish(String response) {
            Message msg = new Message();
            msg.obj = response;
            handler.sendMessage(msg);
        }
    });
}

需要注意的地方是介面中的程式碼依然是在子執行緒中執行的,所以也不能直接修改UI。

來源:https://blog.csdn.net/xiaoliizi/article/details/50773218?locationNum=4

9.6 小結與點評

郭霖總結:

本章中我們主要學習了在Android 中使用HTTP協議來進行網路互動的知識,雖然Android中支援的網路通訊協議有很多種,但HTTP協議無疑是最常用的一種。 通常我們有兩種方式來發送HTTP請求,分別是HtpURLConnection和OktHtp,相信這兩種方式你都已經很好地掌握了。

接著我們又學習了XML和JSON格式資料的解析方式,因為伺服器響應給我們的資料般都是屬於這兩種格式的。無論是XML還是JSON,它們各自又擁有多種解析方式,這裡我們只是學習了最常用的幾種,如果以後你的工作中還需要用到其他的解析方式,可以自行去學習。

本章的最後同樣是最佳實踐環節,在這次的最佳實踐中,我們主要學習瞭如何利用Java的回撥機制來將伺服器響應的資料進行返回。其實除此之外,還有很多地方都可以使用到Java的回撥機制,希望你能舉一反三, 以後在其他地方需要用到回撥機制時都能夠靈活地使用。

在進行了一章多媒體和一章網路的相關知識學習後,你是否想起來Android四大元件中還剩一個沒有學過呢! 那麼下面就讓我們進人到Android 服務的學習旅程之中。

我的總結:

網路技術是必不可缺的一塊內容,在工作中,我們最常用的就是網路了,對於剛剛入職的小夥伴們,或許公司中有封裝好的框架,但是對於一些技術要點,一定要弄得明明白白的。