Android实习生 —— 网络请求及数据解析

澳门新葡亰3522平台游戏 11

认识Http协议

Android中发送http网络请求是很常见的,要有GET请求和POST请求。一个完整的http请求需要经历两个过程:客户端发送请求到服务器,然后服务器将结果返回给客户端,如下图所示:

澳门新葡亰3522平台游戏 1

  • 客户端->服务器
    客户端向服务器发送请求主要包含以下信息:请求的Url地址、请求头以及可选的请求体,打开百度首页,客户端向服务器发送的信息如下所示:澳门新葡亰3522平台游戏 2

    • 请求URL(Request URL)
      上图中的Request
      URL就是请求的Url地址,即
    • 请求头(Request Headers)
      上图中Request
      Headers部分就是请求头,请求头其实也是一些键值对,不过这些键值通常都是W3C定义了的一些标准的Http请求头的名称,请求头包含了客户端想告诉服务端的一些元数据信息,注意是元数据,而不是数据,比如请求头User-Agent会告诉服务器这条请求来自于什么浏览器,再比如请求头Accept-Encoding会告诉服务器客户端支持的压缩格式。除了这些标准的请求头,我们还可以添加自定义的请求头。
    • 请求体(Request Body)
      之前我们提到,URL的最大长度就是2048个字符,如果我们发送的数据很大,超过了2KB怎么办?我们可以将很大的数据放到请求体中,GET请求不支持请求体,只有POST请求才能设置请求体。请求体中可以放置任意的字节流,从而可以很方便地发送任意格式的数据,服务端只需要读取该输入流即可。
  • 服务器->客户端
    服务器接收到客户端发来的请求后,会进行相应的处理,并向客户端输出信息,输出的信息包括响应头和响应体。

    • 响应头 (Response Headers)
      响应头也是一些键值对,如下所示:
      澳门新葡亰3522平台游戏 3响应头包含了服务器想要告诉客户端的一些元数据信息,注意不是数据,是元数据,比如通过响应头Content-Encoding告诉客户端服务器所采用的压缩格式,响应头Content-Type告诉客户端响应体是什么格式的数据,再比如服务端可以通过多个Set-Cookie响应头向客户端写入多条Cookie信息,等等。刚刚提到的几个请求头都是W3C规定的标准的请求头名称,我们也可以在服务端向客户端写入自定义的响应头。
    • 响应体 (Response Body)
      响应体是服务端向客户端传输的实际的数据信息,本质就是一堆字节流,可以表示文本,也可以表示图片或者其他格式的信息,如下所示:
      澳门新葡亰3522平台游戏 4

澳门新葡亰3522平台游戏 5

GET vs POST

Http协议支持的操作有GET、POST、HEAD、PUT、TRACE、OPTIONS、DELETE,其中最最常用的还是GET和POST操作,下面我们看一下GET和POST的区别。

GET:

  • GET请求可以被缓存。
  • 我们之前提到,当发送键值对信息时,可以在URL上面直接追加键值对参数。当用GET请求发送键值对时,键值对会随着URL一起发送的。
  • 由于GET请求发送的键值对时随着URL一起发送的,所以一旦该URL被黑客截获,那么就能看到发送的键值对信息,所以GET请求的安全性很低,不能用GET请求发送敏感的信息(比如用户名密码)。
  • 由于URL不能超过2048个字符,所以GET请求发送数据是有长度限制的。
  • 由于GET请求较低的安全性,我们不应该用GET请求去执行增加、删除、修改等的操作,应该只用它获取数据。

POST:

  • POST请求从不会被缓存。
  • POST请求的URL中追加键值对参数,不过这些键值对参数不是随着URL发送的,而是被放入到请求体中发送的,这样安全性稍微好一些。
  • 应该用POST请求发送敏感信息,而不是用GET。
  • 由于可以在请求体中发送任意的数据,所以理论上POST请求不存在发送数据大小的限制。
  • 当执行增减、删除、修改等操作时,应该使用POST请求,而不应该使用GET请求。

目录

前言
    1、http协议定义
    2、客户端连接服务器实现内部的原理
    3、Http请求方式、区别
    4、HTTP返回请求数据的三种方式:
一、Android中的网络请求方式
    1、在Android上发送HTTP请求的方式
    2、关于HttpClient的废除
    3、HttpURLConnection、HttpCient介绍。(HttpCient废除之前)
    4、HttpURLConnection和HttpCient区别(HttpURLConnection优势)
    5、Android配置网络权限
二、HttpURLConnection(Demo)
    1、Get请求的实现(Demo)
    2、Post请求实现(Demo)
三、HttpCient(Demo)
    1、关于 HttpCient废除
    2、HttpCient介绍
    3、HttpCient实现步骤
    4、HttpCient实现实例(GET)(Demo)
    5、HttpCient实现实例(POST)(Demo)
四、HttpURLConnection抽象请求方法(Demo)
    1、JQuery
    2、定义接口HttpCallbackListener,为了实现回调
    3、创建HttpTool类,抽象请求方法(GET)
    4、调用示例
    5、抽象请求方法(POST)
五、文件下载(Demo)
    1、DownLoadManager简介
    2、功能实现
六、JSON数据解析(Demo)
    1、解析单条Json数据
    2、解析多条Json数据
七、图片数据解析(Demo)
【附录】
    Demo

HttpURLConnection vs DefaultHttpClient

在Android API Level 9(Android
2.2)之前之能使用DefaultHttpClient类发送http请求。DefaultHttpClient是Apache用于发送http请求的客户端,其提供了强大的API支持,而且基本没有什么bug,但是由于其太过复杂,Android团队在保持向后兼容的情况下,很难对DefaultHttpClient进行增强。为此,Android团队从Android
API Level
9开始自己实现了一个发送http请求的客户端类——–HttpURLConnection。

相比于DefaultHttpClient,HttpURLConnection比较轻量级,虽然功能没有DefaultHttpClient那么强大,但是能够满足大部分的需求,所以Android推荐使用HttpURLConnection代替DefaultHttpClient,并不强制使用HttpURLConnection。

但从Android API Level 23(Android
6.0)开始,不能再在Android中使用DefaultHttpClient,强制使用HttpURLConnection。

前言

大部分andriod应用需要与服务器进行数据交互,HTTP、FTP、SMTP或者是直接基于SOCKET编程都可以进行数据交互,但是HTTP必然是使用最广泛的协议

在总结之前先来了解一下Http协议,也是对技术支持的一些补充。

Demo介绍

为了演示HttpURLConnection的常见用法,我做了一个App,界面如下所示:

澳门新葡亰3522平台游戏 6

主界面MainActivity有四个按钮,分别表示用GET发送请求、用POST发送键值对数据、用POST发送XML数据以及用POST发送JSON数据,点击对应的按钮会启动NetworkActivity并执行相应的操作。

NetworkActivity的源码如下所示,此处先贴出代码,后面会详细说明。

package com.ispring.httpurlconnection;

import android.content.Intent;
import android.content.res.AssetManager;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class NetworkActivity extends AppCompatActivity {

    private NetworkAsyncTask networkAsyncTask = new NetworkAsyncTask();

    private TextView tvUrl = null;
    private TextView tvRequestHeader = null;
    private TextView tvRequestBody = null;
    private TextView tvResponseHeader = null;
    private TextView tvResponseBody = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_network);
        tvUrl = (TextView) findViewById(R.id.tvUrl);
        tvRequestHeader = (TextView) findViewById(R.id.tvRequestHeader);
        tvRequestBody = (TextView) findViewById(R.id.tvRequestBody);
        tvResponseHeader = (TextView) findViewById(R.id.tvResponseHeader);
        tvResponseBody = (TextView) findViewById(R.id.tvResponseBody);
        Intent intent = getIntent();
        if (intent != null && intent.getExtras() != null) {
            String networkAction = intent.getStringExtra("action");
            networkAsyncTask.execute(networkAction);
        }
    }

    //用于进行网络请求的AsyncTask
    class NetworkAsyncTask extends AsyncTask<String, Integer, Map<String, Object>> {
        //NETWORK_GET表示发送GET请求
        public static final String NETWORK_GET = "NETWORK_GET";
        //NETWORK_POST_KEY_VALUE表示用POST发送键值对数据
        public static final String NETWORK_POST_KEY_VALUE = "NETWORK_POST_KEY_VALUE";
        //NETWORK_POST_XML表示用POST发送XML数据
        public static final String NETWORK_POST_XML = "NETWORK_POST_XML";
        //NETWORK_POST_JSON表示用POST发送JSON数据
        public static final String NETWORK_POST_JSON = "NETWORK_POST_JSON";

        @Override
        protected Map<String, Object> doInBackground(String... params) {
            Map<String,Object> result = new HashMap<>();
            URL url = null;//请求的URL地址
            HttpURLConnection conn = null;
            String requestHeader = null;//请求头
            byte[] requestBody = null;//请求体
            String responseHeader = null;//响应头
            byte[] responseBody = null;//响应体
            String action = params[0];//http请求的操作类型

            try {
                if (NETWORK_GET.equals(action)) {
                    //发送GET请求
                    url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet?name=孙群&age=27");
                    conn = (HttpURLConnection) url.openConnection();
                    //HttpURLConnection默认就是用GET发送请求,所以下面的setRequestMethod可以省略
                    conn.setRequestMethod("GET");
                    //HttpURLConnection默认也支持从服务端读取结果流,所以下面的setDoInput也可以省略
                    conn.setDoInput(true);
                    //用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
                    conn.setRequestProperty("action", NETWORK_GET);
                    //禁用网络缓存
                    conn.setUseCaches(false);
                    //获取请求头
                    requestHeader = getReqeustHeader(conn);
                    //在对各种参数配置完成后,通过调用connect方法建立TCP连接,但是并未真正获取数据
                    //conn.connect()方法不必显式调用,当调用conn.getInputStream()方法时内部也会自动调用connect方法
                    conn.connect();
                    //调用getInputStream方法后,服务端才会收到请求,并阻塞式地接收服务端返回的数据
                    InputStream is = conn.getInputStream();
                    //将InputStream转换成byte数组,getBytesByInputStream会关闭输入流
                    responseBody = getBytesByInputStream(is);
                    //获取响应头
                    responseHeader = getResponseHeader(conn);
                } else if (NETWORK_POST_KEY_VALUE.equals(action)) {
                    //用POST发送键值对数据
                    url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
                    conn = (HttpURLConnection) url.openConnection();
                    //通过setRequestMethod将conn设置成POST方法
                    conn.setRequestMethod("POST");
                    //调用conn.setDoOutput()方法以显式开启请求体
                    conn.setDoOutput(true);
                    //用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
                    conn.setRequestProperty("action", NETWORK_POST_KEY_VALUE);
                    //获取请求头
                    requestHeader = getReqeustHeader(conn);
                    //获取conn的输出流
                    OutputStream os = conn.getOutputStream();
                    //获取两个键值对name=孙群和age=27的字节数组,将该字节数组作为请求体
                    requestBody = new String("name=孙群&age=27").getBytes("UTF-8");
                    //将请求体写入到conn的输出流中
                    os.write(requestBody);
                    //记得调用输出流的flush方法
                    os.flush();
                    //关闭输出流
                    os.close();
                    //当调用getInputStream方法时才真正将请求体数据上传至服务器
                    InputStream is = conn.getInputStream();
                    //获得响应体的字节数组
                    responseBody = getBytesByInputStream(is);
                    //获得响应头
                    responseHeader = getResponseHeader(conn);
                } else if (NETWORK_POST_XML.equals(action)) {
                    //用POST发送XML数据
                    url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
                    conn = (HttpURLConnection) url.openConnection();
                    //通过setRequestMethod将conn设置成POST方法
                    conn.setRequestMethod("POST");
                    //调用conn.setDoOutput()方法以显式开启请求体
                    conn.setDoOutput(true);
                    //用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
                    conn.setRequestProperty("action", NETWORK_POST_XML);
                    //获取请求头
                    requestHeader = getReqeustHeader(conn);
                    //获取conn的输出流
                    OutputStream os = conn.getOutputStream();
                    //读取assets目录下的person.xml文件,将其字节数组作为请求体
                    requestBody = getBytesFromAssets("person.xml");
                    //将请求体写入到conn的输出流中
                    os.write(requestBody);
                    //记得调用输出流的flush方法
                    os.flush();
                    //关闭输出流
                    os.close();
                    //当调用getInputStream方法时才真正将请求体数据上传至服务器
                    InputStream is = conn.getInputStream();
                    //获得响应体的字节数组
                    responseBody = getBytesByInputStream(is);
                    //获得响应头
                    responseHeader = getResponseHeader(conn);
                } else if (NETWORK_POST_JSON.equals(action)) {
                    //用POST发送JSON数据
                    url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
                    conn = (HttpURLConnection) url.openConnection();
                    //通过setRequestMethod将conn设置成POST方法
                    conn.setRequestMethod("POST");
                    //调用conn.setDoOutput()方法以显式开启请求体
                    conn.setDoOutput(true);
                    //用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
                    conn.setRequestProperty("action", NETWORK_POST_JSON);
                    //获取请求头
                    requestHeader = getReqeustHeader(conn);
                    //获取conn的输出流
                    OutputStream os = conn.getOutputStream();
                    //读取assets目录下的person.json文件,将其字节数组作为请求体
                    requestBody = getBytesFromAssets("person.json");
                    //将请求体写入到conn的输出流中
                    os.write(requestBody);
                    //记得调用输出流的flush方法
                    os.flush();
                    //关闭输出流
                    os.close();
                    //当调用getInputStream方法时才真正将请求体数据上传至服务器
                    InputStream is = conn.getInputStream();
                    //获得响应体的字节数组
                    responseBody = getBytesByInputStream(is);
                    //获得响应头
                    responseHeader = getResponseHeader(conn);
                }
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //最后将conn断开连接
                if (conn != null) {
                    conn.disconnect();
                }
            }

            result.put("url", url.toString());
            result.put("action", action);
            result.put("requestHeader", requestHeader);
            result.put("requestBody", requestBody);
            result.put("responseHeader", responseHeader);
            result.put("responseBody", responseBody);
            return result;
        }

        @Override
        protected void onPostExecute(Map<String, Object> result) {
            super.onPostExecute(result);
            String url = (String)result.get("url");//请求的URL地址
            String action = (String) result.get("action");//http请求的操作类型
            String requestHeader = (String) result.get("requestHeader");//请求头
            byte[] requestBody = (byte[]) result.get("requestBody");//请求体
            String responseHeader = (String) result.get("responseHeader");//响应头
            byte[] responseBody = (byte[]) result.get("responseBody");//响应体

            //更新tvUrl,显示Url
            tvUrl.setText(url);

            //更新tvRequestHeader,显示请求头
            if (requestHeader != null) {
                tvRequestHeader.setText(requestHeader);
            }

            //更新tvRequestBody,显示请求体
            if(requestBody != null){
                try{
                    String request = new String(requestBody, "UTF-8");
                    tvRequestBody.setText(request);
                }catch (UnsupportedEncodingException e){
                    e.printStackTrace();
                }
            }

            //更新tvResponseHeader,显示响应头
            if (responseHeader != null) {
                tvResponseHeader.setText(responseHeader);
            }

            //更新tvResponseBody,显示响应体
            if (NETWORK_GET.equals(action)) {
                String response = getStringByBytes(responseBody);
                tvResponseBody.setText(response);
            } else if (NETWORK_POST_KEY_VALUE.equals(action)) {
                String response = getStringByBytes(responseBody);
                tvResponseBody.setText(response);
            } else if (NETWORK_POST_XML.equals(action)) {
                //将表示xml的字节数组进行解析
                String response = parseXmlResultByBytes(responseBody);
                tvResponseBody.setText(response);
            } else if (NETWORK_POST_JSON.equals(action)) {
                //将表示json的字节数组进行解析
                String response = parseJsonResultByBytes(responseBody);
                tvResponseBody.setText(response);
            }
        }

        //读取请求头
        private String getReqeustHeader(HttpURLConnection conn) {
            //https://github.com/square/okhttp/blob/master/okhttp-urlconnection/src/main/java/okhttp3/internal/huc/HttpURLConnectionImpl.java#L236
            Map<String, List<String>> requestHeaderMap = conn.getRequestProperties();
            Iterator<String> requestHeaderIterator = requestHeaderMap.keySet().iterator();
            StringBuilder sbRequestHeader = new StringBuilder();
            while (requestHeaderIterator.hasNext()) {
                String requestHeaderKey = requestHeaderIterator.next();
                String requestHeaderValue = conn.getRequestProperty(requestHeaderKey);
                sbRequestHeader.append(requestHeaderKey);
                sbRequestHeader.append(":");
                sbRequestHeader.append(requestHeaderValue);
                sbRequestHeader.append("n");
            }
            return sbRequestHeader.toString();
        }

        //读取响应头
        private String getResponseHeader(HttpURLConnection conn) {
            Map<String, List<String>> responseHeaderMap = conn.getHeaderFields();
            int size = responseHeaderMap.size();
            StringBuilder sbResponseHeader = new StringBuilder();
            for(int i = 0; i < size; i++){
                String responseHeaderKey = conn.getHeaderFieldKey(i);
                String responseHeaderValue = conn.getHeaderField(i);
                sbResponseHeader.append(responseHeaderKey);
                sbResponseHeader.append(":");
                sbResponseHeader.append(responseHeaderValue);
                sbResponseHeader.append("n");
            }
            return sbResponseHeader.toString();
        }

        //根据字节数组构建UTF-8字符串
        private String getStringByBytes(byte[] bytes) {
            String str = "";
            try {
                str = new String(bytes, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return str;
        }

        //从InputStream中读取数据,转换成byte数组,最后关闭InputStream
        private byte[] getBytesByInputStream(InputStream is) {
            byte[] bytes = null;
            BufferedInputStream bis = new BufferedInputStream(is);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            BufferedOutputStream bos = new BufferedOutputStream(baos);
            byte[] buffer = new byte[1024 * 8];
            int length = 0;
            try {
                while ((length = bis.read(buffer)) > 0) {
                    bos.write(buffer, 0, length);
                }
                bos.flush();
                bytes = baos.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return bytes;
        }

        //根据文件名,从asserts目录中读取文件的字节数组
        private byte[] getBytesFromAssets(String fileName){
            byte[] bytes = null;
            AssetManager assetManager = getAssets();
            InputStream is = null;
            try{
                is = assetManager.open(fileName);
                bytes = getBytesByInputStream(is);
            }catch (IOException e){
                e.printStackTrace();
            }
            return bytes;
        }

        //将表示xml的字节数组进行解析
        private String parseXmlResultByBytes(byte[] bytes) {
            InputStream is = new ByteArrayInputStream(bytes);
            StringBuilder sb = new StringBuilder();
            List<Person> persons = XmlParser.parse(is);
            for (Person person : persons) {
                sb.append(person.toString()).append("n");
            }
            return sb.toString();
        }

        //将表示json的字节数组进行解析
        private String parseJsonResultByBytes(byte[] bytes){
            String jsonString = getStringByBytes(bytes);
            List<Person> persons = JsonParser.parse(jsonString);
            StringBuilder sb = new StringBuilder();
            for (Person person : persons) {
                sb.append(person.toString()).append("n");
            }
            return sb.toString();
        }

    }
}

这个App是用来发送http请求的客户端,除此之外,我还创建了一个JSP的WebProject作为服务端,用Servlet对客户端发来的请求进行处理,Servlet的代码如下所示:

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Enumeration;

@WebServlet(name = "MyServlet")
public class MyServlet extends HttpServlet {
    //GET请求
    private static final String NETWORK_GET = "NETWORK_GET";
    //用POST发送键值对
    private static final String NETWORK_POST_KEY_VALUE = "NETWORK_POST_KEY_VALUE";
    //用POST发送XML数据
    private static final String NETWORK_POST_XML = "NETWORK_POST_XML";
    //用POST发送JSON数据
    private static final String NETWORK_POST_JSON = "NETWORK_POST_JSON";

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String action = request.getHeader("action");
        //将输入与输出都设置为UTF-8编码
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/plain;charset=UTF-8");
        //response.setHeader("content-type","text/plain;charset=UTF-8");
        if(NETWORK_GET.equals(action) || NETWORK_POST_KEY_VALUE.equals(action)){
            //对于NETWORK_GET和NETWORK_POST_KEY_VALUE,遍历键值对,并将键值对重新写回到输出结果中
            Enumeration<String> parameterNames = request.getParameterNames();
            PrintWriter writer = response.getWriter();
            while(parameterNames.hasMoreElements()){
                String name = parameterNames.nextElement();
                String value = request.getParameter(name);
                if(request.getMethod().toUpperCase().equals("GET")){
                    //GET请求需要进行编码转换,POST不需要
                    value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
                }
                writer.write(name + "=" + value + "n");
            }
            writer.flush();
            writer.close();
        }else if(NETWORK_POST_XML.equals(action) || NETWORK_POST_JSON.equals(action)){
            //对于NETWORK_POST_XML和NETWORK_POST_JSON,将请求体重新写入到响应体的输出流中
            //通过request.getInputStream()得到http请求的请求体
            BufferedInputStream bis  = new BufferedInputStream(request.getInputStream());
            //通过response.getOutputStream()得到http请求的响应体
            BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
            byte[] buffer = new byte[1024 * 8];
            int length = 0;
            while ( (length = bis.read(buffer)) > 0){
                bos.write(buffer, 0, length);
            }
            bos.flush();
            bos.close();
            bis.close();
        }else{
            PrintWriter writer = response.getWriter();
            writer.write("非法的请求头: action");
            writer.flush();
            writer.close();
        }
    }
}
1、http协议定义

HTTP协议是基于TCP/IP协议之上的协议,是客户端服务器之间的应用层的协议,是通用的、无状态的面向对象的协议。

发送GET请求

由于网络请求耗时而且会阻塞当前线程,所以我们将发送http请求的操作都放到NetworkAsyncTask中,NetworkAsyncTask是继承自AsyncTask。

点击”GET”按钮后,界面如下所示:

澳门新葡亰3522平台游戏 7

GET请求是最简单的http请求,其发送请求的代码如下所示:

if (NETWORK_GET.equals(action)) {
    //发送GET请求
    url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet?name=孙群&age=27");
    conn = (HttpURLConnection) url.openConnection();
    //HttpURLConnection默认就是用GET发送请求,所以下面的setRequestMethod可以省略
    conn.setRequestMethod("GET");
    //HttpURLConnection默认也支持从服务端读取结果流,所以下面的setDoInput也可以省略
    conn.setDoInput(true);
    //用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
    conn.setRequestProperty("action", NETWORK_GET);
    //禁用网络缓存
    conn.setUseCaches(false);
    //获取请求头
    requestHeader = getReqeustHeader(conn);
    //在对各种参数配置完成后,通过调用connect方法建立TCP连接,但是并未真正获取数据
    //conn.connect()方法不必显式调用,当调用conn.getInputStream()方法时内部也会自动调用connect方法
    conn.connect();
    //调用getInputStream方法后,服务端才会收到请求,并阻塞式地接收服务端返回的数据
    InputStream is = conn.getInputStream();
    //将InputStream转换成byte数组,getBytesByInputStream会关闭输入流
    responseBody = getBytesByInputStream(is);
    //获取响应头
    responseHeader = getResponseHeader(conn);
}

上面的注释写的比较详细了,此处对代码进行一下简单说明。

  • 我们在URL的后面添加了?name=孙群&age=27,这样就相当于添加了两个键值对,其形式如?key1=value1&key2=value2&key3=value3,在?后面添加键值对,键和值之间用=相连,键值对之间用&分隔,服务端可以读取这些键值对信息。
  • HttpURLConnection默认就是用GET发送请求,当然也可以用conn.setRequestMethod(“GET”)将其显式地设置为GET请求。
  • 通过setRequestProperty方法可以设置请求头,既可以是标准的请求头,也可以是自定义的请求头,此处我们设置了自定义的请求头action,用于服务端判断请求的类型。
  • GET请求容易被缓存,我们可以用conn.setUseCaches(false)禁用缓存。
  • 通过调用connect方法可以让客户端和服务器之间建立TCP连接,建立连接之后不会立即传输数据,只是表示处于connected状态了。该方法不必显式调用,因为在之后的getInputStream方法中会隐式地调用该方法。
  • 调用HttpURLConnection的getInputStream()方法可以获得响应结果的输入流,即服务器向客户端输出的信息,需要注意的是,getInputStream()方法的调用必须在一系列的set方法之后进行。
  • 然后在方法getBytesByInputStream中,通过输入流的read方法得到字节数组,read方法是阻塞式的,每read一次,其实就是从服务器上下载一部分数据,直到将服务器的输出全部下载完成,这样就得到响应体responseBody了。
  • 我们可以通过getReqeustHeader方法读取请求头,代码如下所示:

    //读取请求头
    private String getReqeustHeader(HttpURLConnection conn) {            
        Map<String, List<String>> requestHeaderMap = conn.getRequestProperties();
        Iterator<String> requestHeaderIterator = requestHeaderMap.keySet().iterator();
        StringBuilder sbRequestHeader = new StringBuilder();
        while (requestHeaderIterator.hasNext()) {
            String requestHeaderKey = requestHeaderIterator.next();
            String requestHeaderValue = conn.getRequestProperty(requestHeaderKey);
            sbRequestHeader.append(requestHeaderKey);
            sbRequestHeader.append(":");
            sbRequestHeader.append(requestHeaderValue);
            sbRequestHeader.append("n");
        }
        return sbRequestHeader.toString();
    }
  • 由上可以看出,以上方法主要还是调用了HttpURLConnection的方法getRequestProperty获取请求头,需要注意的是getRequestProperty方法执行时,客户端和服务器之间必须还未建立TCP连接,即还没有调用connect方法,在connected之后执行getRequestProperty会抛出异常,详见

我们还可以通过getResponseHeader方法获取响应头,代码如下所示:

    //读取响应头
    private String getResponseHeader(HttpURLConnection conn) {
        Map<String, List<String>> responseHeaderMap = conn.getHeaderFields();
        int size = responseHeaderMap.size();
        StringBuilder sbResponseHeader = new StringBuilder();
        for(int i = 0; i < size; i++){
            String responseHeaderKey = conn.getHeaderFieldKey(i);
            String responseHeaderValue = conn.getHeaderField(i);
            sbResponseHeader.append(responseHeaderKey);
            sbResponseHeader.append(":");
            sbResponseHeader.append(responseHeaderValue);
            sbResponseHeader.append("n");
        }
        return sbResponseHeader.toString();
    }

通过方法getHeaderFieldKey可以获得响应头的key值,通过方法getHeaderField可以获得响应头的value值。

在后台的Servelt中将键值对信息重新原样写入到客户端,服务端的代码如下所示:

if(NETWORK_GET.equals(action) || NETWORK_POST_KEY_VALUE.equals(action)){
            //对于NETWORK_GET和NETWORK_POST_KEY_VALUE,遍历键值对,并将键值对重新写回到输出结果中
            Enumeration<String> parameterNames = request.getParameterNames();
            PrintWriter writer = response.getWriter();
            while(parameterNames.hasMoreElements()){
                String name = parameterNames.nextElement();
                String value = request.getParameter(name);
                if(request.getMethod().toUpperCase().equals("GET")){
                    //GET请求需要进行编码转换,POST不需要
                    value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
                }
                writer.write(name + "=" + value + "n");
            }
            writer.flush();
            writer.close();
        }

在接收到服务端返回的数据后,在AsyncTask的onPostExecute方法中会将Url、请求头、响应头、响应体的值展示在UI上。

2、客户端连接服务器实现内部的原理

澳门新葡亰3522平台游戏 8

分析上图,步骤如下:

  • 第一步:在浏览器客户端中得到用户输入的内容。

  • 第二步:浏览器得到这个网址之后,内部会将这个域名发送到DNS上,进行域名解析。得到它的IP之后就会链接到指定的服务器上,假如服务器的地址是:221.104.13.32:80,从浏览器到服务器端口它使用到最底层的TCP/IP协议。

  • 第三步:实现TCP/IP协议用Socket来完成,使用了Socket的套接字。

  • 第四步:服务器端的80端口监听客户端的链接,这样客户端到服务器就链接上了。

通俗一点讲,用户在浏览器输入网址,通过http协议发出去,网址经过DNS域名解析,解析成指定的ip地址,并在80端口上监听用户的请求。服务器监听到请求之后,会以三种方式返回给客户端:HTML、XML、JASON。

用POST发送键值对数据

点击”POST KEY VALUE”按钮,可以用POST发送键值对数据,界面如下所示:

澳门新葡亰3522平台游戏 9

代码如下所示:

if (NETWORK_POST_KEY_VALUE.equals(action)) {
    //用POST发送键值对数据
    url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
    conn = (HttpURLConnection) url.openConnection();
    //通过setRequestMethod将conn设置成POST方法
    conn.setRequestMethod("POST");
    //调用conn.setDoOutput()方法以显式开启请求体
    conn.setDoOutput(true);
    //用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
    conn.setRequestProperty("action", NETWORK_POST_KEY_VALUE);
    //获取请求头
    requestHeader = getReqeustHeader(conn);
    //获取conn的输出流
    OutputStream os = conn.getOutputStream();
    //获取两个键值对name=孙群和age=27的字节数组,将该字节数组作为请求体
    requestBody = new String("name=孙群&age=27").getBytes("UTF-8");
    //将请求体写入到conn的输出流中
    os.write(requestBody);
    //记得调用输出流的flush方法
    os.flush();
    //关闭输出流
    os.close();
    //当调用getInputStream方法时才真正将请求体数据上传至服务器
    InputStream is = conn.getInputStream();
    //获得响应体的字节数组
    responseBody = getBytesByInputStream(is);
    //获得响应头
    responseHeader = getResponseHeader(conn);
}

使用POST发送请求的代码与用GET发送请求的代码大部分类似,我们只对其中不同的地方做下说明。

  • 澳门新葡亰3522平台游戏,需要通过setRequestMethod将conn设置成POST方法。
  • 如果想用POST发送请求体,那么需要调用setDoOutput方法,将其设置为true。
  • 通过conn.getOutputStream()获得输出流,可以向输出流中写入请求体,最后记得调用输出流的flush方法,注意此时并没有真正将请求体发送到服务器端。
  • 当调用getInputStream方法后,才真正将请求体的内容发送到服务器。

在我们的服务器端的Servlet中,在接收到POST请求发送的键值对数据后,也只是简单地将键值对数据原样写入给客户端,具体代码参见上文,不再赘述。

3、Http请求方式、区别

根据HTTP标准,HTTP请求可以使用多种请求方法。例如:HTTP1.1支持7种请求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TARCE。在Internet应用中,最常用的方法是GET和POST。

区别:

  • 在客户端,GET方式在通过URL提交数据,数据在URL中可以看到;POST方式,数据放在HTML
    HEADER内提交。

  • 对于GET方式,服务器端用Request.QueryString获取变量的值,对于POST方式,服务器用Request.Form获取提交的数据。

  • GET方式提交的数据不能大于2KB(主要是URL长度限制),而POST则没有此限制。

  • 安全性问题。使用GET的时候,参数会显示在地址栏上,而POST不会。所以,如果这些数据是中文数据而且是非敏感数据,那么使用GET;如果用户输入的数据不是中文字符而且包含敏感数据,那么还是使用POST为好。

用POST发送XML数据

点击”POST XML”按钮,可以用POST发送XML数据,界面如下所示:

澳门新葡亰3522平台游戏 10

代码如下所示:

if (NETWORK_POST_XML.equals(action)) {
    //用POST发送XML数据
    url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
    conn = (HttpURLConnection) url.openConnection();
    //通过setRequestMethod将conn设置成POST方法
    conn.setRequestMethod("POST");
    //调用conn.setDoOutput()方法以显式开启请求体
    conn.setDoOutput(true);
    //用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
    conn.setRequestProperty("action", NETWORK_POST_XML);
    //获取请求头
    requestHeader = getReqeustHeader(conn);
    //获取conn的输出流
    OutputStream os = conn.getOutputStream();
    //读取assets目录下的person.xml文件,将其字节数组作为请求体
    requestBody = getBytesFromAssets("person.xml");
    //将请求体写入到conn的输出流中
    os.write(requestBody);
    //记得调用输出流的flush方法
    os.flush();
    //关闭输出流
    os.close();
    //当调用getInputStream方法时才真正将请求体数据上传至服务器
    InputStream is = conn.getInputStream();
    //获得响应体的字节数组
    responseBody = getBytesByInputStream(is);
    //获得响应头
    responseHeader = getResponseHeader(conn);
}

上面的代码与用POST发送键值对的代码很相似,对其进行简单说明。

  • 上述代码通过getBytesFromAssets方法读取了assets目录下的person.xml文件,将xml文件的字节流作为请求体requestBody,然后将该请求体发送到服务器。
  • person.xml文件如下所示:

<?xml version="1.0" encoding="utf-8"?>
<persons>
    <person id="101">
        <name>张三</name>
        <age>27</age>
    </person>
    <person id="102">
        <name>李四</name>
        <age>28</age>
    </person>
</persons>

<person>标签对应着Person类,Person类代码如下所示:

package com.ispring.httpurlconnection;

public class Person {
    private String id = "";
    private String name = "";
    private int age = 0;

    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 int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return new StringBuilder().append("name:").append(getName()).append(", age:").append(getAge()).toString();
    }
}

服务端接收到客户端发送来的XML数据之后,只是简单的将其原样写回到客户端,即客户端接收到的响应体还是原来的XML数据,然后客户端通过parseXmlResultByBytes方法对该XML数据进行解析,将字节数组转换成List<Person>,parseXmlResultByBytes代码如下所示:

    //将表示xml的字节数组进行解析
    private String parseXmlResultByBytes(byte[] bytes) {
        InputStream is = new ByteArrayInputStream(bytes);
        StringBuilder sb = new StringBuilder();
        List<Person> persons = XmlParser.parse(is);
        for (Person person : persons) {
            sb.append(person.toString()).append("n");
        }
        return sb.toString();
    }

该方法使用了自定义的XmlParser类对XML数据进行解析,其源码如下所示:

package com.ispring.httpurlconnection;

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

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

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

public class XmlParser {

    public static List<Person> parse(InputStream is) {
        List<Person> persons = new ArrayList<>();
        try{
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser parser = factory.newSAXParser();
            PersonHandler personHandler = new PersonHandler();
            parser.parse(is, personHandler);
            persons = personHandler.getPersons();
        }catch (Exception e){
            e.printStackTrace();
        }
        return persons;
    }

    static class PersonHandler extends DefaultHandler {
        private List<Person> persons;
        private Person temp;
        private StringBuilder sb;

        public List<Person> getPersons(){
            return persons;
        }

        @Override
        public void startDocument() throws SAXException {
            super.startDocument();
            persons = new ArrayList<>();
            sb = new StringBuilder();
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            super.startElement(uri, localName, qName, attributes);
            sb.setLength(0);
            if(localName.equals("person")){
                temp = new Person();
                int length = attributes.getLength();
                for(int i = 0; i < length; i++){
                    String name = attributes.getLocalName(i);
                    if(name.equals("id")){
                        String value = attributes.getValue(i);
                        temp.setId(value);
                    }
                }
            }
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            super.characters(ch, start, length);
            sb.append(ch, start, length);
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            super.endElement(uri, localName, qName);
            if(localName.equals("name")){
                String name = sb.toString();
                temp.setName(name);
            }else if(localName.equals("age")){
                int age = Integer.parseInt(sb.toString());
                temp.setAge(age);
            }else if(localName.equals("person")){
                persons.add(temp);
            }
        }
    }
}
4、HTTP返回请求数据的三种方式:
  • 以HTML代码内容返回。
  • 以XML字符串的形式返回,在以后的android开发中这种形式返回数据比较多。
  • 以JSON对象形式返回,在网络流量上考虑JSON要比XML方式要好一些,便于解析。
    在Android当中,一般使用xml和Json数据解析。

用POST发送JSON数据

点击”POST JSON”按钮,可以用POST发送JSON数据,界面如下所示:

澳门新葡亰3522平台游戏 11

代码如下所示:

if (NETWORK_POST_JSON.equals(action)) {
     //用POST发送JSON数据
     url = new URL("http://192.168.31.200:8080/HttpServer/MyServlet");
     conn = (HttpURLConnection) url.openConnection();
     //通过setRequestMethod将conn设置成POST方法
     conn.setRequestMethod("POST");
     //调用conn.setDoOutput()方法以显式开启请求体
     conn.setDoOutput(true);
     //用setRequestProperty方法设置一个自定义的请求头:action,由于后端判断
     conn.setRequestProperty("action", NETWORK_POST_JSON);
     //获取请求头
     requestHeader = getReqeustHeader(conn);
     //获取conn的输出流
     OutputStream os = conn.getOutputStream();
     //读取assets目录下的person.json文件,将其字节数组作为请求体
     requestBody = getBytesFromAssets("person.json");
     //将请求体写入到conn的输出流中
     os.write(requestBody);
     //记得调用输出流的flush方法
     os.flush();
     //关闭输出流
     os.close();
     //当调用getInputStream方法时才真正将请求体数据上传至服务器
     InputStream is = conn.getInputStream();
     //获得响应体的字节数组
     responseBody = getBytesByInputStream(is);
     //获得响应头
     responseHeader = getResponseHeader(conn);
}

上面的代码与用POST发送XML的代码很相似,对其进行简单说明。

  • 上述代码通过getBytesFromAssets方法读取了assets目录下的person.json文件,将json文件的字节流作为请求体requestBody,然后将该请求体发送到服务器。
  • person.json文件如下所示:

{
  "persons": [{
    "id": "101",
    "name":"张三",
    "age":27
  }, {
    "id": "102",
    "name":"李四",
    "age":28
  }]
}
  • persons数组中的每个元素都对应着一个Person对象。
  • 服务端接收到客户端发送来的JSON数据之后,只是简单的将其原样写回到客户端,即客户端接收到的响应体还是原来的JSON数据,然后客户端通过parseJsonResultByBytes方法对该XML数据进行解析,将字节数组转换成List,parseXmlResultByBytes代码如下所示:

    //将表示json的字节数组进行解析
    private String parseJsonResultByBytes(byte[] bytes){
        String jsonString = getStringByBytes(bytes);
        List<Person> persons = JsonParser.parse(jsonString);
        StringBuilder sb = new StringBuilder();
        for (Person person : persons) {
            sb.append(person.toString()).append("n");
        }
        return sb.toString();
    }

该方法又使用了自定义的JsonParset类,源码如下所示:

package com.ispring.httpurlconnection;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

public class JsonParser {
    public static List<Person> parse(String jsonString){
        List<Person> persons = new ArrayList<>();

        try{
            JSONObject jsonObject = new JSONObject(jsonString);
            JSONArray jsonArray = jsonObject.getJSONArray("persons");
            int length = jsonArray.length();
            for(int i = 0; i < length; i++){
                JSONObject personObject = jsonArray.getJSONObject(i);
                String id = personObject.getString("id");
                String name = personObject.getString("name");
                int age = personObject.getInt("age");
                Person person = new Person();
                person.setId(id);
                person.setName(name);
                person.setAge(age);
                persons.add(person);
            }
        }catch (JSONException e){
            e.printStackTrace();
        }

        return persons;
    }
}

一、Android中的网络请求方式

Android中的WebView控件已经在后台帮我们处理好了发送HTTP请求、接收服务响应、解析返回数据,以及最终的页面展示这几步工作,不过由于它封装得太好了,反而不能直观地看出HTTP协议是如何工作的。因此接下来我们通过手动发送HTTP请求的方式,来更加深入的了解这一过程。

其他

  • 如果Http请求体的数据很大,就可以认为该请求主要是完成数据上传的作用;如果响应体的数据很大,就可以认为该请求主要完成数据下载的作用。
  • 上面我们通过demo演示了如何上传XML文件和JSON文件,并对二者进行解析。在上传的过程中,Android要写入Content-Length这个请求头,Content-Length就是请求体的字节长度,注意是字节长度,而不是字符长度(汉字等会占用两个字节)。默认情况下,Android为了得到Content-Length的长度,Android会把请求体放到内存中的,直到输出流调用了close方法后,才会读取内存中请求体的字节长度,将其作为请求头Content-Length。当要上传的请求体很大时,这会非常占用内存,为此Android提供了两个方法来解决这个问题。
    • setFixedLengthStreamingMode (int contentLength)
      如果请求体的大小是知道的,那么可以调用HttpURLConnection的setFixedLengthStreamingMode
      (int contentLength)
      方法,该方法会告诉Android要传输的请求头Content-Length的大小,这样Android就无需读取整个请求体的大小,从而不必一下将请求体全部放到内存中,这样就避免了请求体占用巨大内存的问题。
    • setChunkedStreamingMode (int chunkLength)
      如果请求体的大小不知道,那么可以调用setChunkedStreamingMode (int
      chunkLength)方法。该方法将传输的请求体分块传输,即将原始的数据分成多个数据块,chunkLength表示每块传输的字节大小。比如我们要传输的请求体大小是10M,我们将chunkLength设置为1024 *
      1024
      byte,即1M,那么Android会将请求体分10次传输,每次传输1M,具体的传输规则是:每次传输一个数据块时,首先在一行中写明该数据块的长度,比如1024 *
      1024,然后在后面的一行中写入要传输的数据块的字节数组,再然后是一个空白行,这样第一数据块就这样传输,在空白行之后就是第二个数据块的传输,与第一个数据块的格式一样,直到最后没有数据块要传输了,就在用一行写明要传输的字节为0,这样在服务器端就知道读取完了整个请求体了。如果设置的chunkLength的值为0,那么表示Android会使用默认的一个值作为实际的chunkLength。使用setChunkedStreamingMode方法的前提是服务器支持分块数据传输,分块数据传输是从HTTP
      1.1开始支持的,所以如果你的服务器只支持HTTP
      1.0的话,那么不能使用setChunkedStreamingMode方法。

希望本文对大家使用HttpURLConnection有所帮助!

1、在Android上发送HTTP请求的方式

一般有两种:HttpURLConnection、HttpCient

2、关于HttpClient的废除
  • 在android 6.0(api 23)
    SDK,不再提供org.apache.http.*(只保留几个类),HttpClient相关类移除,推荐使用HTTPURLConnection。
  • 废除原因:之前一直使用HttClient是由于HttpURLConnection不稳定导致,那么现在谷歌虽然修复了HttpURLConnection之前存在的一些问题。
  • 若还需使用该类,点击查看解决办法。
3、HttpURLConnection、HttpCient介绍。(HttpCient废除之前)
  • HttpClient是apache的开源框架,封装了访问http的请求头,参数,内容体,响应等等,使用起来比较方便,而HttpURLConnection是java的标准类,什么都没封装,用起来太原始,不方便,比如重访问的自定义,以及一些高级功能等。

  • 从稳定性方面来说的话,HttpClient很稳定,功能强,BUG少,容易控制细节,而之前的HttpURLConnection一直存在着版本兼容的问题,不过在后续的版本中已经相继修复掉了。

4、HttpURLConnection和HttpCient区别(HttpURLConnection优势)
  • 1、HttpUrlConnection是Android
    SDK的标准实现,而HttpClient是apache的开源实现;

  • 2、HttpUrlConnection直接支持GZIP压缩;HttpClient也支持,但要自己写代码处理;

  • 3、HttpUrlConnection直接在系统层面做了缓存策略处理,加快重复请求的速度。

  • 4、HttpUrlConnection直接支持系统级连接池,即打开的连接不会直接关闭,在一段时间内所有程序可共用;HttpClient当然也能做到,但毕竟不如官方直接系统底层支持好;

5、Android配置网络权限

因为需要访问网络,需在AndroidManifest.xml中添加如下权限

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

二、HttpURLConnection(Demo)

1、Get请求实现(Demo)

由于网络请求可能造成耗时操作(网络环境差的情况下)对ui线程的阻塞,我们开启子线程去操作网络请求。
【关于UI线程,请看《Android实习生 ——
异步处理之Handler》】

private void connectWithHttpURLConnection() {
        new Thread( new Runnable() {
            @Override
            public void run() {
                Message msg =new Message();
                HttpURLConnection connection = null;
                try {
                    // 调用URL对象的openConnection方法获取HttpURLConnection的实例
                    URL url = new URL("http://www.baidu.com");
                    connection = (HttpURLConnection) url.openConnection();
                    // 设置请求方式,GET或POST
                    connection.setRequestMethod("GET");
                    // 设置连接超时、读取超时的时间,单位为毫秒(ms)
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    // 设置是否使用缓存  默认是true
                    connection.setUseCaches(true);
                    //设置请求头里面的属性
                    //connection.setRequestProperty();
                    // 开始连接
                    Log.i("HttpURLConnection.GET","开始连接");
                    connection.connect();
                    if (connection.getResponseCode() == 200) {
                        Log.i("HttpURLConnection.GET", "请求成功");
                        InputStream in = connection.getInputStream();
                        // 使用BufferedReader对象读取返回的数据流
                        // 按行读取,存储在StringBuider对象response中
                        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                        StringBuilder response = new StringBuilder();
                        String line;
                        while ((line = reader.readLine()) != null) {
                            response.append(line);
                        }
                        // 此处省略处理数据的代码,通过handler直接将返回的结果消息发送给UI线程列队
                        Bundle bundle =new Bundle();
                        bundle.putString("data", String.valueOf(response));
                        msg.setData(bundle);
                        handler.sendMessage(msg);
                    }else{
                        Log.i("HttpURLConnection.GET", "请求失败");
                    }
                } catch (Exception e){
                    e.printStackTrace();
                } finally {
                    if (connection != null){
                        // 结束后,关闭连接
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
  • 效果
![](https://upload-images.jianshu.io/upload_images/4821697-02f7676c93afa928.png)

Get请求效果
  • 打印消息
![](https://upload-images.jianshu.io/upload_images/4821697-90e380713a9f13b1.png)

打印消息
2、Post请求实现(Demo)

正如前言中第3条所述,在客户端,GET方式在通过URL提交数据,数据在URL中可以看到;POST方式,数据放在HTML
HEADER内提交。

在Get方法中请求参数可以直接写到地址栏中,如:

//用“&”隔开不同参数
 String path = "https://reg.163.com/logins.jsp?id=helloworld&pwd=android";

但在Post方式中要把“请求的参数”转换为byte数组,然后通过DataOutputStream(urlConn.getOutputStream())把参数写入。

private void connectWithHttpURLConnectionPOST() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = new Message();
                String path = "https://reg.163.com/logins.jsp";
                // 请求的参数转换为byte数组
                String params = null;
                HttpURLConnection urlConn = null;
                try {
                    params = "id=" + URLEncoder.encode("helloworld", "UTF-8")
                            + "&pwd=" + URLEncoder.encode("android", "UTF-8");

                    byte[] postData = params.getBytes();
                    // 新建一个URL对象
                    URL url = new URL(path);
                    // 打开一个HttpURLConnection连接
                    urlConn = (HttpURLConnection) url.openConnection();
                    // 设置连接超时时间
                    urlConn.setConnectTimeout(8 * 1000);
                    // Post请求必须设置允许输出
                    urlConn.setDoOutput(true);
                    // Post请求不能使用缓存
                    urlConn.setUseCaches(false);
                    // 设置为Post请求
                    urlConn.setRequestMethod("POST");
                    urlConn.setInstanceFollowRedirects(true);
                    // 配置请求Content-Type
                    urlConn.setRequestProperty("Content-Type",
                            "application/x-www-form-urlencode");
                    // 开始连接
                    urlConn.connect();
                    Log.i("HttpURLConnection.POST", "开始连接");
                    // 发送请求参数
                    DataOutputStream dos = new DataOutputStream(urlConn.getOutputStream());
                    dos.write(postData);
                    dos.flush();
                    dos.close();
                    // 判断请求是否成功
                    if (urlConn.getResponseCode() == 200) {
                        // 获取返回的数据
                        InputStream in = urlConn.getInputStream();
                        // 使用BufferedReader对象读取返回的数据流
                        // 按行读取,存储在StringBuider对象response中
                        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                        StringBuilder response = new StringBuilder();
                        String line;
                        while ((line = reader.readLine()) != null) {
                            response.append(line);
                        }
                        Log.i("HttpURLConnection.POST", "请求成功");
                        // 此处省略处理数据的代码,直接将返回的结果消息发送给UI线程列队
                        Bundle bundle = new Bundle();
                        bundle.putString("data", String.valueOf(response));
                        msg.setData(bundle);
                        handler.sendMessage(msg);
                    } else {
                        Log.i("HttpURLConnection.POST", "请求失败");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (urlConn != null) {
                        // 结束后,关闭连接
                        urlConn.disconnect();
                    }
                }
            }
        }).start();
    }
  • 效果
![](https://upload-images.jianshu.io/upload_images/4821697-fbe0c0aeb7172bb3.png)

Post请求效果

三、HttpCient(Demo)

1、关于 HttpCient废除
  • 在android 6.0(api 23)
    SDK,不再提供org.apache.http.*(只保留几个类),HttpClient相关类移除,推荐使用HTTPURLConnection。
  • 废除原因:之前一直使用HttClient是由于HttpURLConnection不稳定导致,那么现在谷歌虽然修复了HttpURLConnection之前存在的一些问题。
  • 若还需使用该类,点击查看解决办法。
2、HttpCient介绍

HttpClient其实是一个interface类型,HttpClient封装了对象需要执行的Http请求、身份验证、连接管理和其它特性。既然HttpClient是一个接口,因此无法创建它的实例。从文档上看,HttpClient有三个已知的实现类分别是:AbstractHttpClient,
AndroidHttpClient,
DefaultHttpClient
,会发现有一个专门为Android应用准备的实现类AndroidHttpClient,当然使用常规的DefaultHttpClient也可以实现功能。