android网络请求全面解析

android中发起网络请求主要的有get和post两种:
get:

public String get(String url) {
        HttpURLConnection conn = null;
        try {
            URL mURL = new URL(url);
            conn = (HttpURLConnection) mURL.openConnection();

            conn.setRequestMethod("GET");
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(10000);
            //类似于Content-Type这种约定俗成的,还可以包括一些自定义的例如CUSTOM_OS,
            //需要和服务器约定好
            conn.setRequestProperty("Content-Type", "text/xml");
            conn.addRequestProperty("CUSTOM_OS", "android");

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {

                InputStream is = conn.getInputStream();
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = -1;
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
                is.close();
                String response = os.toString();
                os.close();
                return response;
            } else {
                throw new NetworkErrorException("response status is "+responseCode);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return null;
    }

post:

public String post(String url, String content) {
        HttpURLConnection conn = null;
        try {
            URL mURL = new URL(url);
            conn = (HttpURLConnection) mURL.openConnection();
            conn.setRequestMethod("POST");
            conn.setReadTimeout(5000);
            conn.setConnectTimeout(10000);
            conn.setDoOutput(true);// 设置此方法,允许向服务器输出内容,必须在connect之前调用

            String data = content;
            OutputStream out = conn.getOutputStream();
            out.write(data.getBytes());
            out.flush();
            out.close();

            int responseCode = conn.getResponseCode();
            if (responseCode == 200) {

                InputStream is = conn.getInputStream();
                ByteArrayOutputStream os = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = -1;
                while ((len = is.read(buffer)) != -1) {
                    os.write(buffer, 0, len);
                }
                is.close();
                String response = os.toString();
                os.close();
                return response;
            } else {
                throw new NetworkErrorException("response status is "+responseCode);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return null;
    }

首先URL的openConnection()会返回一个URLConnection的实例对象,这个对象可以对服务器读写,openConnection()方法是调用的URLStreamHandler实例对象的openConnection(),

在SDK中的URLConnection有坑,这个类中的方法很多都是空的实现,真正的源码的JDK里面,所以下面讨论的都是JDK中的类,首先URL的openConnection()会返回一个URLConnection的实例对象,这个对象可以对服务器读写,其中URL的openConnection()会辗转调用到URLStreamHandler实例对象的openConnection(),下图:

这里写图片描述
其中(1)(2)均在sun.net.www.protocol.http包中(Handler HttpURLConnection),这个包JDK中没有,其他均在java.net包中,到这里都比较简单,平常基本使用都可以了,但是我们除了要知其然还要知其所以然,很有必要继续深究下其中的原理以及其他的用法。

先从URL类开始,这个类主要是解析出主机名,端口号,协议等信息,
这里写图片描述
其中的协议有http,ftp,ftp适合传送大文件,http适合传送网页等数据,平时我们的网址还有接口都是http协议的。
然后调用openConnection或openStream打开链接,不同点是openStream会调用先openConnection返回一个URLConnection对象后在调用改对象的getInputStream,这里就是打开链接后直接获取输入流,如果我们直接调用openConnection可能后面还会写入一些东西然后在获取输入流,典型的就是post请求的设置header和写出参数。

get和post最大的区别就是有没有通过OutputStream 被虚拟机包装到body中向服务器传递数据,先分析下getOutputStream的过程

public synchronized OutputStream getOutputStream() throws IOException {
        try {
            if (!doOutput) {//通过setDoInput设置
                throw new ProtocolException("cannot write to a URLConnection"
                        + " if doOutput=false - call setDoOutput(true)");
            }
            if (method.equals("GET")) {//请求方法为get时会被置为post
                method = "POST"; // Backward compatibility
            }
            if (!"POST".equals(method) && !"PUT".equals(method) &&
                    "http".equals(url.getProtocol())) {//必须为post或put,协议为http
                throw new ProtocolException("HTTP method " + method +
                        " doesn't support output");
            }
            // if there's already an input stream open, throw an exception
            if (inputStream != null) {
                throw new ProtocolException("Cannot write output after reading input.");
            }
            if (!checkReuseConnection())
                connect();//如果没有connect会调用connect()
            boolean expectContinue = false;
            /*
            *是否设置了Expect: 100-Continue
            *如果设置了会先询问服务器是否处理post数据
            */
            String expects = requests.findValue("Expect");
            if ("100-Continue".equalsIgnoreCase(expects)) {
                http.setIgnoreContinue(false);
                expectContinue = true;
            }
            if (streaming() && strOutputStream == null) {
            /*
            *将没有设置的header设置默认置然后调用httpclient的writeRequests
            *向输出流写出数据
            */
             writeRequests();
            }
            if (expectContinue) {
            //如果设置了Expect: 100-Continue,检查服务器返回的code是否允许发送post请求
                expect100Continue();
            }
            ps = (PrintStream) http.getOutputStream();//调用httpclient的getOutputStream方法
            if (streaming()) {
                if (strOutputStream == null) {
                    if (chunkLength != -1) { /* chunked */
                        strOutputStream = new StreamingOutputStream(
                                new ChunkedOutputStream(ps, chunkLength), -1L);
                    } else { /* must be fixed content length */
                        long length = 0L;
                        if (fixedContentLengthLong != -1) {
                            length = fixedContentLengthLong;
                        } else if (fixedContentLength != -1) {
                            length = fixedContentLength;
                        }
                        strOutputStream = new StreamingOutputStream(ps, length);
                    }
                }
                return strOutputStream;
            } else {
                if (poster == null) {
                    poster = new PosterOutputStream();
                }
                return poster;
            }
        }
        //部分删减
    }

HttpURLConnecion的getOutputStream会调用HttpClient的getOutputStream,看一下HttpClient的getOutputStream方法

public OutputStream getOutputStream() {
    return serverOutput;
}

直接返回了serverOutput,这个变量在openServer(String server, int port)中赋值,openServer方法有两个地方会调用,一个是HttpClient初始化时(调用父类NetworkClient 的构造方法),一个是会在parseHTTP(MessageHeader responses, ProgressSource pi, HttpURLConnection httpuc)中调用,而这个方法会在HttpURLConnecion中getInputStream中调用。

这样获得了输出流,这时只是把数据写入到内存中,并没有发送给服务器,发送过程在HttpURLConnection的getInputStream方法中调用strOutputStream.close ()写入到流中发送给服务器。

不过到这肯定还有很多疑问,不是先调用的getOutputStream,难道是在这里面就初始化了一个HttpClient对象?确实是,在HttpURLConnecion的getOutputStream和getInputStream中都会在没有建立连接时调用connect(),它又调用了plainConnect(),在这里面new了一个HttpClient 对象,这样就调用到了前面提到的openServer(),它里面建立了一个socket连接(对使用的是socket连接)。

现在就比较清楚了,post请求比get请求多了一步getOutputStream,但不管什么请求都是先调用connect(),建立一个socket连接,如果是post还会返回一个outputStream对象用来向服务器发送数据,然后getOutputStream和getInputStream中会调用writeRequests()方法,这个方法会设置一些默认的请求头参数,然后里面调用HttpClient的writeRequests()方法将这些参数发送给服务器,之后post会通过outputStream对象发送数据,这些数据就是接口的参数,发送完在getInputStream()方法中将缓存中数据发送到服务器,等待服务器返回,最后关闭连接一次请求就完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值