android网络请求全面解析

来源:互联网 发布:matlab 符号矩阵 编辑:程序博客网 时间:2024/06/05 16:13

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()方法中将缓存中数据发送到服务器,等待服务器返回,最后关闭连接一次请求就完成了。

0 0