Android學習筆記(7)

myxs發表於2017-04-02

網路

標籤: Android


1 WebView

在應用內部顯示各種網頁,WebView控制元件用法類似其它控制元件,需要許可權宣告如下

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

<WebView
    android:id="@+id/web_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

WebView webView = (WebView) findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("http://www.baidu.com");

2 HTTP協議

流程:傳送HTTP請求、接收服務響應、解析返回資料、顯示結果

2.1使用HttpURLConnection傳送HTTP請求

通過URL物件的openConnection方法獲取HttpURLConnection例項

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

設定conncetion的請求方法,主要是GET和POST

connection.setRequestMethod("GET");

自定義請求,如設定連線超時、讀取超時的毫秒數

connection.setConnectionTimeout(8000);
connection.setReadTimeout(8000);

呼叫connection的getInputStream方法獲取從伺服器返回的輸入流

InputStream in = connection.getInputStream();

關閉HTTP連線

connection.disconnect();

發起網路請求,需要在子執行緒中處理

new Thread(new Runnable(){
    @Override
    public void run(){
        HttpURLConnection connection = null;
        BufferedReader reader = null;
        try{
            URL url = new URL("http://www.baidu.com");
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectionTimeout(8000);
            connection.setReadTimeout(8000);
            InputStream in = connection.getInputStream();
            //對伺服器返回的輸入流進行讀取
            reader = new BufferedReader(new InputStreamReader(in));
            StringBuilder response = new StringBuilder();
            String line;
            while((line=reader.readline()) != null){
                response.append(line);
                }
                showResponse(response.toString());
            }catch(Exception e){
                e.printStackTracee();
            }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(){
        @Onverride
        public void run(){
            responseText.setText(response);
        }
    });

Android不允許在子執行緒中進行UI操作,runOnUiThread可以將執行緒切換到主執行緒,更新UI元素

提交資料到伺服器,如使用者名稱和密碼

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

2.2網路通訊庫OkHttp

替代原生HttpURLConnection

新增OkHttp庫依賴

compile 'com.squareup.okhttp3:3.7.0'

建立OkHttpClient例項

OkHttpClient client = new OkHttpClient();

建立Request物件

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

連綴其它方法豐富Request物件

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

呼叫OkHttpClient的newCall方法建立Call物件,並呼叫execute方法傳送請求以及獲取伺服器返回的資料

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

獲取返回的具體內容

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

傳送POST請求,需要先建立RequestBody物件存放待提交的引數

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

呼叫Request.Builder的post方法

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

和HttpURLConnection傳送請求稍有不同

new Thread(new Runnable(){
    @Override
    public void run(){
        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();
            showResponse(responseData);
            ...
            }
        }
    }

3 解析XML格式資料

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

3.1 Pull解析方式

本機可以按照Apache伺服器,在Request中url設定為本機伺服器地址

.url("http://10.0.2.2/get_data.xml")
.build();
Response response = client.newCall(request).execute();
String responseData = response.body().string();
parseXMLWithPull(responseData);


private void parseXMLWithPull(String xmlData) {
    try {
        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();//獲取XmlPullParserFactory例項
        XmlPullParser xmlPullParser = factory.newPullParser();//XmlPullParser物件
        xmlPullParser.setInput(new StringReader(xmlData));//XML資料
        int eventType = xmlPullParser.getEventType();//獲得當前解析事件
        String id = "";
        String name = "";
        String version = "";
        //迴圈中判斷解析事件是否完成,否則呼叫next方法獲取下一個解析事件
        while (eventType != XmlPullParser.END_DOCUMENT) {
            String nodeName = xmlPullParser.getName();//獲取當前節點名字
            switch (eventType) {
                // 開始解析某個結點
                case XmlPullParser.START_TAG: {
                    if ("id".equals(nodeName)) {
                        id = xmlPullParser.nextText();//獲取id節點的內容
                    } 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("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();
    }
}

3.2 SAX解析方式

建立一個類繼承DefaultHandler,從寫父類的5個方法

public class MyHandler extends DefaultHandler {
    //XML解析
    @Override
    public void startDocument() throws SAXException {
    }

    //解析節點
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
    }

    //解析節點內容
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
    }

    //完成解析節點
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
    }

    //完成整個XML解析
    @Override
     public void endDocument() throws SAXException {
     }

針對前面的XML資料的類

public class ContentHandler extends DefaultHandler {

    private String nodeName;

    private StringBuilder id;

    private StringBuilder name;

    private StringBuilder version;

    @Override
    public void startDocument() throws SAXException {
        id = new StringBuilder();
        name = new StringBuilder();
        version = new StringBuilder();
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        // 記錄當前結點名
        nodeName = localName;
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        // 根據當前的結點名判斷將內容新增到哪一個StringBuilder物件中
        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);
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        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());
            // 最後要將StringBuilder清空掉
            id.setLength(0);
            name.setLength(0);
            version.setLength(0);
        }
    }

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

id,name和version用來儲存具體解析的內容。解析節點的時候nodeName儲存當前節點的名字,在具體解析節點內容的時候,根據節點名字,把解析的內容放在不同的StringBuilder物件中

在傳送請求後,通過SAX方式解析XML資料

private void parseXMLWithSAX(String xmlData) {
    try {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        XMLReader xmlReader = factory.newSAXParser().getXMLReader();
        ContentHandler handler = new ContentHandler();
        // 將ContentHandler的例項設定到XMLReader中
        xmlReader.setContentHandler(handler);
        // 開始執行解析
        xmlReader.parse(new InputSource(new StringReader(xmlData)));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

4 解析JSON資料

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

解析JSON資料有多種方法,如官方JSONObject,Google的GSON,第三方開源庫Jackson\FastJSON

4.1 使用JSONObject

引數為
Response resonse = client.newCall(request).execute();
String responseData = response.body().string();

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

伺服器上定義是JSON陣列,解析時將資料傳入到JSONArray物件中,再迴圈遍歷,取出每個元素JSONObject,每個JSONObject物件包含id、name和version資料,呼叫getString取出資料

4.2 GSON

新增庫依賴

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

GSON可以將一段JSON格式的字串自動對映成一個物件 JSON資料: {"name":"Tom","age":20} 定義Person類,新增name和age,如下會解析成一個Person物件

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

JSON陣列需要藉助TypeToken將資料型別如Person傳入到fromJson方法中

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

具體針對前面的JSON資料 定義一個App類,Java Bean

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

}

解析獲取到JSON資料

private void parseJSONWithGSON(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());
    }
}

5 總結

網路操作作為工具類,提供一個靜態方法發起網路請求。為了避免呼叫方法開啟新執行緒後,在伺服器可能還未執行響應執行結束,通過使用回撥機制。

介面

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

伺服器成功響應後呼叫onFinish方法,其中引數為返回的資料;網路操作錯誤時呼叫onError方法

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();
                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                StringBuilder response = new StringBuilder();
                String line;
                while ((line = reader.readLine()) != null) {
                    response.append(line);
                }
                if (listener != null) {
                    // 回撥onFinish()方法
                    listener.onFinish(response.toString());
                }
            } catch (Exception e) {
                if (listener != null) {
                    // 回撥onError()方法
                    listener.onError(e);
                }
            } finally {
                if (connection != null) {
                    connection.disconnect();
                }
            }
        }
    }).start();
}

在具體使用的時候,需要傳遞一個HttpCallbackListener

HttpUtil.sendHttpRequest(address, new HttpCallbackListener(){
    @Override
    public void onFinsh(String response){
        //對返回資料操作
    }
    @Override
    public void onError(Exceptio e){
    }
}

回撥機制將響應資料返回給呼叫者 使用OkHttp傳送請求的方式

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

OkHttp在enqueue方法內部開啟子執行緒,執行HTTP請求,最終結果回撥到okhttp3.Callback中 具體用法

HttpUtil.sendOkHttpRequest("http://www.baidu.com", new okhttp3.Callback() {
    @Override
    public void onResponse(Call call, Response response) throws IOException{
        String responseData = response.body().string();
    }
    @Override
    public void onFailure(Call call , IOException e){
    }
};

相關文章