HttpURLConnection,OKHttp以及xml解析

来源:互联网 发布:知乎周刊pdf 编辑:程序博客网 时间:2024/06/05 23:41


本篇文章主要介绍以几下个知识点:

  • 使用 HTTP 协议访问网络:
    使用 HttpURLConnection 和 OKHttp;
  • 解析 XML 格式数据:
    Pull 和 SAX 解析;

9.1 使用 HTTP 协议访问网络

  HTTP 协议,其工作原理很简单:客户端向服务器发出一条 HTTP 请求,服务器收到请求后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理。

9.1.1 使用 HttpURLConnection

       下面学习 HttpURLConnection 的用法,其请求步骤代码如下:

    /**     *  HttpURLConnection 发送请求     */    private void sendRequestWithHttpURLConnection() {        // 开启线程来发送网络请求        new Thread(new Runnable() {            @Override            public void run() {                HttpURLConnection connection = null;                BufferedReader reader = null;                try{                    URL url = new URL("http://www.baidu.com");                    // 1. 获取 HttpURLConnection 实例                    connection = (HttpURLConnection) url.openConnection();                    // 2. 设置请求方法                    connection.setRequestMethod("GET");                    // 3. 自由定制,如设置连接超时、读取超时等                    connection.setConnectTimeout(8000);                    connection.setReadTimeout(8000);                    // 4. 获取服务器返回的输入流                    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.printStackTrace();                }finally {                    if (reader != null){                        try{                            reader.close();                        }catch (IOException e){                            e.printStackTrace();                        }                    }                    if (connection != null){                        // 5.把 HTTP 连接关掉                        connection.disconnect();                    }                }            }        }).start();    }

      若是想要提交数据给服务器只需把请求方法改为 POST,并在获取输入流之前把要提交的数据写出即可。注意每条数据要以键值对的形式存在,数据与数据之间用 “&” 隔开,比如向服务器提交用户名和密码可写成:

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

1.1.2 使用 OKHttp

        接下来学习下网络请求开源项目 OKHttp,其项目主页地址是:https://github.com/square/okhttp

        在使用 OKHttp 前,需要在项目中添加 OKHttp 库的依赖,如下:

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

        下面学习 OKHttp 请求步骤,如下:

    /**     *  OKHttp 发送请求     */    private void sendRequestWithOKHttp() {        new Thread(new Runnable() {            @Override            public void run() {                try{                    // 1. 创建 OkHttpClient 实例                    OkHttpClient client = new OkHttpClient();                    // 2. 创建 Request 对象                    Request request = new Request.Builder().url("http://www.baidu.com").build();                    // 3. 调用 OkHttpClient 的 newCall() 方法来创建 Call 对象                    Response response = client.newCall(request).execute();                    // 4. 获取返回的内容                    String responseData = response.body().string();                    showResponse(responseData);// 显示请求结果                }catch (Exception e){                    e.printStackTrace();                }            }        }).start();    }

        相比 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();

1.1.3 网络编程的最佳实践

       在实际开发中,我们通常将这些通用的网络操作提取到一个公共类里,接下来就简单封装下网络操作。

       首先针对 HttpURLConnection 定义一个回调接口:

public interface HttpCallbackListener {    void onFinish(String response);// 请求成功时调用    void onError(Exception e);// 请求失败时调用}

       接着编写工具类 HttpUtil:

public class HttpUtil {    /**     * 用 HttpURLConnection 发送请求     * @param address     * @param listener     */    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);                    // 1. 获取 HttpURLConnection 实例                    connection = (HttpURLConnection) url.openConnection();                    // 2. 设置请求方法                    connection.setRequestMethod("GET");                    // 3. 自由定制,如设置连接超时、读取超时等                    connection.setConnectTimeout(8000);                    connection.setReadTimeout(8000);                    connection.setDoInput(true);                    connection.setDoOutput(true);                    // 4. 获取服务器返回的输入流                    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){                        // 5.把 HTTP 连接关掉                        connection.disconnect();                    }                }            }        }).start();    }    /**     * 用 OKHttp 发送请求     * @param address     * @param callback     */    public static void sendOKHttpRequest(String address, Callback callback){        OkHttpClient client = new OkHttpClient();        Request request = new Request.Builder().url(address).build();        client.newCall(request).enqueue(callback);    }}

        这时候用 HttpURLConnection 发送请求就可以写成:

        HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {            @Override            public void onFinish(String response) {                // 在这里根据返回内容执行具体的逻辑            }            @Override            public void onError(Exception e) {                // 在这里对异常情况进行处理            }        });

       用 OKHttp 发送请求就可以写成:

       HttpUtil.sendOKHttpRequest("http://www.baidu.com", new Callback() {            @Override            public void onFailure(Call call, IOException e) {                 // 在这里对异常情况进行处理            }            @Override            public void onResponse(Call call, Response response) throws IOException {                // 得到服务器返回的具体内容                String responseData = response.body().string();               }        });

       下面举个例子巩固下,主要代码如下:

public class HttpActivity extends AppCompatActivity implements View.OnClickListener {    private TextView response_text;    /**     *  OKHttp 发送请求     */    private void sendRequestWithOKHttp() {        HttpUtil.sendOKHttpRequest("http://www.baidu.com", new Callback() {            @Override            public void onFailure(Call call, IOException e) {                 // 异常情况处理            }            @Override            public void onResponse(Call call, Response response) throws IOException {                // 得到服务器返回内容                String responseData = response.body().string();                showResponse(responseData);            }        });    }    /**     *  HttpURLConnection 发送请求     */    private void sendRequestWithHttpURLConnection() {        HttpUtil.sendHttpRequest("http://www.baidu.com", new HttpCallbackListener() {            @Override            public void onFinish(String response) {                // 在根据返回内容执行具体的逻辑                showResponse(response);            }            @Override            public void onError(Exception e) {                // 异常情况处理            }        });    }    /**     * 显示请求结果     * @param response     */    private void showResponse(final String response) {        runOnUiThread(new Runnable() {            @Override            public void run() {                System.out.println(response);            }        });    }}

1.2 解析 XML 格式数据

       在网络上传输数据时最常用的格式有两种:XML 和 JSON。本节来学习下如何解析 XML 格式的数据。

       解析 XML 格式的数据有多种方式,这里主要介绍 Pull 解析和 SAX 解析。解析前先来看看等下要解析的 XML 文本:


xml 格式的内容

1.2.1 Pull 解析方式

       Pull 解析整个过程比较简单,具体看代码注释:

   /**     * pull 解析     * @param xmlData 要解析的xml数据     */    private void parseXMLWithPull(String xmlData) {        try {            // 1. 获取 XmlPullParserFactory 实例            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();            // 2. 借助 XmlPullParserFactory 实例得到 XmlPullParser 对象            XmlPullParser xmlPullParser = factory.newPullParser();            // 3. 调用 setInput() 方法设置xml数据            xmlPullParser.setInput(new StringReader(xmlData));            // 4. 获取当前的解析事件            int eventType = xmlPullParser.getEventType();            String id = "";            String name = "";            String sex = "";            // 5. 通过 while 循环不断地进行解析            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 ("sex".equals(nodeName)){                            sex = xmlPullParser.nextText();                        }                        break;                    // 完成解析某个节点                    case  XmlPullParser.END_TAG:                        if ("student".equals(nodeName)){                            Log.d("pull解析:", "id is" + id);                            Log.d("pull解析:", "name is" + name);                            Log.d("pull解析:", "sex is" + sex);                        }                        break;                    default:                        break;                }                // 获取下一个解析事件                eventType = xmlPullParser.next();            }        }catch (Exception e){            e.printStackTrace();        }    }

1.2.2 SAX 解析方式

       SAX 解析的用法比 Pull 解析要复杂些,但在语义方面会更加清楚。

       用 SAX 解析需要建一个类继承 DefaultHandler,并重写父类的5个方法。为实现上面同样的功能,新建一个 ContentHandler 类,如下所示:

public class ContentHandler extends DefaultHandler {    private String nodeName;    private StringBuilder id;    private StringBuilder name;    private StringBuilder sex;    /**     * 开始 XML 解析时调用     * @throws SAXException     */    @Override    public void startDocument() throws SAXException {        id = new StringBuilder();        name = new StringBuilder();        sex = new StringBuilder();    }    /**     * 开始解析某个节点时调用     * @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 ch     * @param start     * @param length     * @throws SAXException     */    @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 ("sex".equals(nodeName)){            sex.append(ch,start,length);        }    }    /**     * 完成解析某个节点时调用     * @param uri     * @param localName     * @param qName     * @throws SAXException     */    @Override    public void endElement(String uri, String localName, String qName) throws SAXException {        if ("student".equals(localName)){            Log.d("sax解析:", "id is" + id.toString().trim());            Log.d("sax解析:", "name is" + name.toString().trim());            Log.d("sax解析:", "sex is" + sex.toString().trim());            // 最后要将 StringBuilder 清空掉            id.setLength(0);            name.setLength(0);            sex.setLength(0);        }    }    /**     * 完成整个 XML 解析时调用     * @throws SAXException     */    @Override    public void endDocument() throws SAXException {        super.endDocument();    }}

       接下来就非常简单了,代码如下:

    /**     * sax 解析     * @param xmlData     */    private void parseXMLWithSAX(String xmlData){        try {            // 创建 SAXParserFactory 对象            SAXParserFactory factory = SAXParserFactory.newInstance();            // 获取 XMLReader 对象            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();        }    }

1.2.3 举个例子实在点

       下面在布局中放置两个按钮,分别进行pull解析和sax解析:

public class ParseXMLActivity extends AppCompatActivity implements View.OnClickListener {    private Button btn_pull,btn_sax;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_parse_xml);        btn_pull = (Button) findViewById(R.id.btn_pull);        btn_sax = (Button) findViewById(R.id.btn_sax);        btn_pull.setOnClickListener(this);        btn_sax.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.btn_pull:                HttpUtil.sendOKHttpRequest("http://10.0.2.2/get_data.xml", new Callback() {                    @Override                    public void onFailure(Call call, IOException e) {                        //ToastUtils.showShort("请求失败");                    }                    @Override                    public void onResponse(Call call, Response response) throws IOException {                        String responseData = response.body().string();                        parseXMLWithPull(responseData);  // pull 解析                    }                });                break;            case R.id.btn_sax:                HttpUtil.sendOKHttpRequest("http://10.0.2.2/get_data.xml", new Callback() {                    @Override                    public void onFailure(Call call, IOException e) {                        //ToastUtils.showShort("请求失败");                    }                    @Override                    public void onResponse(Call call, Response response) throws IOException {                        String responseData = response.body().string();                        parseXMLWithSAX(responseData);  // sax 解析                    }                });                break;        }    }    /**     * pull 解析     * @param xmlData 要解析的xml数据     */    private void parseXMLWithPull(String xmlData) {  . . .  }    /**     * sax 解析     * @param xmlData     */    private void parseXMLWithSAX(String xmlData){  . . .  }}

       运行程序,打印的日志分别如下:


pull 解析

sax 解析

       可以看到,已经将 XML 数据成功解析出来了。

原创粉丝点击