第一行代码——第九章:看看精彩的世界——使用网络技术

目录:

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




    

当然不要忘记加上网络权限。

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


        
            1
            Google maps
            1.0
        
        
            2
            Chrome
            2.1
        
        
            3
            Google Play
            2.3
        
    

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 people = gson.fromJson(jsonData,new TypeToken>(){}.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();
        ListstudentList = gson.fromJson(jsonData,new TypeToken>(){}.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 服务的学习旅程之中。

我的总结:

网络技术是必不可缺的一块内容,在工作中,我们最常用的就是网络了,对于刚刚入职的小伙伴们,或许公司中有封装好的框架,但是对于一些技术要点,一定要弄得明明白白的。

你可能感兴趣的:(第一行代码,第一行代码知识点)