大多数的Android应用程序都会使用HTTP协议来发送和接收网络数据,Android中主要提供了两种方式来进行HTTP操做,HttpURLConnection和HttpClient。因此首先介绍下Http协议。HTTP(HyperText Transfer Protocol,超文本传输协议) 是互联网上应用最为普遍的一种网络协议。全部的WWW文件都必须遵照这个标准。HTTP协议采用了请求/响应模型。一般,由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP链接,HTTP服务器则在那个端口监听客户端发送过来的请求。一旦收到请求,服务器(向客户端)发回一个状态行和响应消息,最后消息传送完毕时,能够关闭客户端和服务器端的链接。java
Http消息由从客户机到服务器的请求和从服务器到客户机的响应构成。这两种类型的消息均由一个起始行,一个或者多个头域,一个指示头域结束的空行和可选的消息体组成。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。android
请求消息格式以下:缓存
请求行 - 通用信息头 - 请求头 - 实体头 - 消息主体服务器
以下所示:网络
应答消息格式以下:异步
状态行 - 通用信息头 - 响应头 - 实体头 - 消息主体ide
以下所示:工具
注:Android HttpUrlConnection中只未提供Connect方法 post
方法性能 |
描 述 |
GET |
请求指定url的数据,请求体为空(例如打开网页) |
POST |
请求指定url的数据,同时传递参数(在请求体中) |
HEAD |
相似于get请求,只不过返回的响应体为空,用于获取响应头 |
PUT |
从客户端向服务器传送的数据取代指定的文档的内容 |
DELETE |
请求服务器删除指定的页面 |
CONNECT |
HTTP/1.1协议中预留给可以将链接改成管道方式的代理服务器 |
OPTIONS |
容许客户端查看服务器的性能 |
TRACE |
回显服务器收到的请求,主要用于测试或诊断。 |
状态码是一个用于标识服务器响应结果的代码,由3位数字表示。状态码的第一个数字定义响应的类别,后两个数字没有分类的做用。(状态码后面会跟随一段用于简要描述状态码信息的文本)
状态码类型:
类型 |
类型描述 |
1xx |
信息响应类,表示接收到请求而且继续处理 |
2xx |
处理成功响应类,表示动做被成功接收、理解和接受 |
3xx |
重定向响应类,为了完成指定的动做,必须接受进一步处理 |
4xx |
客户端错误,客户请求包含语法错误或者是不能正确执行 |
5xx |
服务端错误,服务器不能正确执行一个正确的请求 |
常见状态码详解:
状态码 |
描述 |
200 OK |
请求成功(其后是对GET和POST请求的应答文档) |
301 Moved Permanently |
所请求的页面已经转移至新的url |
304 Not Modified |
服务器端的数据在If-modified-since后未发生修改 |
400 Bad Request |
服务器未能理解请求 |
401 Unauthorized |
被请求的页面须要用户名和密码 |
404 Not Found |
服务器没法找到被请求的页面 |
408 Request Timeout |
请求超出了服务器的等待时间 |
500 Internal Server Error |
请求未完成。服务器遇到不可预知的状况 |
501 Not Implemented |
请求未完成。服务器不支持所请求的功能 |
通用头域
通用头域包含请求和响应消息都支持的头域,通用头域包含Cache-Control、Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via.
1) Cache-Control:Cache-Control指定请求和响应遵循的缓存机制。请求时的缓存指令包括no-cache、no-store、max-age、max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-age。
2) Date头域:Date头域表示消息发送的时间,如Date:Mon,31Dec200104:25:57GMT
请求头域
请求头域中包含请求消息特有的头域信息,如Host、Range、User-Agent等
1) Host:Host头域指定请求资源的Intenet主机和端口号;
2)Range:Range头域能够请求实体的一个或者多个子范围,如bytes=500-600,601-9993);
3)User-Agent:User-Agent头域的内容包含发出请求的用户信息
响应头域
响应头域中包含响应消息中特有的头域信息,如Location、Server等
1)Location:Location头域用于重定向接收者到一个新URI地址;
2)Server:Server头域包含处理请求的原始服务器的软件信息;
实体头域
请求消息和响应消息均可以包含实体信息,实体信息通常由实体头域和实体组成。实体头域包含关于实体的原信息,实体头包括Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD五、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header;
1)Content-Type:Content-Type实体头用于向接收方指示实体的介质类型,指定HEAD方法送到接收方的实体介质类型,或GET方法发送的请求介质类型;
2)Content-Range:Content-Range实体头用于指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。主要用于对范围请求的响应或对一系列范围的重叠请求。
通常格式:Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-legth
如:Content-Range:bytes0-499/1234;
3)Last-modified:Last-modified实体头指定服务器上保存内容的最后修订时间
HttpClient和HttpURLConnection是Android中提供的两种进行HTTP操做的方式,这两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv六、以及链接池等功能。
DefaultHttpClient和AndroidHttpClient都是HttpClient的具体实现类,都拥有众多的API,并且实现比较稳定,bug数量也不多。但同时也因为HttpClient的API数量过多,使得很难在不破坏兼容性的状况下对它进行升级和扩展,因此目前Android团队在提高和优化HttpClient方面的工做态度并不积极;
而HttpURLConnection是一种多用途、轻量极的HTTP客户端,使用它来进行HTTP操做能够适用于大多数的应用程序,同时其升级与维护也相对比较简单。但在Android 2.3版本以前,HttpURLConnection一直存在着一些使人厌烦的bug。好比说对一个可读的InputStream调用close()方法时,就有可能会致使链接池失效。一般的解决办法就是直接禁用掉链接池的功能。
注意:Google建议Android2.3及以上版本的应用使用HttpURLConnection,而Android2.3如下版本的应用使用HttpClient。缘由以下:
在Android 2.2版本以前,HttpClient拥有较少的bug,所以使用它是最好的选择。
而在Android 2.3版本及之后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,于是很是适用于Android项目。压缩和缓存机制能够有效地减小网络访问的流量,在提高速度和省电方面也起到了较大的做用。同时,Google Android团队也在积极对该方式进行维护和升级。
/** * 采用HttpURLConnection进行网络请求的工具类 */ public class AsyncNetworkUtil { private static String TAG = "AsyncNetworkUtil"; /** * 发送一个post请求 * @param url:请求的地址 * @param content:post请求参数 * @return: 返回服务器响应信息 */ public static String post(String url, String content) { HttpURLConnection conn = null; String response = null; try { // 建立一个URL对象 URL mURL = new URL(url); // 调用URL的openConnection()方法,获取HttpURLConnection对象 conn = (HttpURLConnection) mURL.openConnection(); conn.setRequestMethod("POST");// 设置请求方法为post conn.setReadTimeout(5000);// 设置读取超时为5秒 conn.setConnectTimeout(10000);// 设置链接网络超时为10秒 conn.setUseCaches(false); //设置不使用缓存 conn.setDoOutput(true);// 设置此方法,容许向服务器输出内容 // post请求的参数 String data = content; // 得到一个输出流,向服务器写数据,默认状况下,系统不容许向服务器输出内容 OutputStream out = conn.getOutputStream(); out.write(data.getBytes()); out.flush(); out.close(); int responseCode = conn.getResponseCode();// 调用此方法就没必要再使用conn.connect()方法 if (responseCode != -1) { InputStream is = conn.getInputStream(); response = getStringFromInputStream(is); } else { response = "No valid response code"; } } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect();// 关闭链接 } } return response; } /** * 发送一个get请求 * @param url:请求的地址 * @return: 响应信息 */ public static String get(String url) { HttpURLConnection conn = null; try { // 利用string url构建URL对象 URL mURL = new URL(url); conn = (HttpURLConnection) mURL.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); conn.setConnectTimeout(10000); conn.connect(); int responseCode = conn.getResponseCode(); if (responseCode == 200) { InputStream is = conn.getInputStream(); String response = getStringFromInputStream(is); return response; }else if ((responseCode + "") != null && (responseCode + "").length() != 0){ return responseCode + " "; } else { throw new NetworkErrorException("response status is "+responseCode); } } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); } } return null; } /** * 从输入流中获取数据 * @param is * @return * @throws IOException */ private static String getStringFromInputStream(InputStream is) throws IOException { 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 state = os.toString();// 把流中的数据转换成字符串,采用的编码是utf-8(模拟器默认编码) os.close(); return state; } }
开启网络权限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
检查设备的网络链接情况
/** * 这是一个工具类,用于检测网络链接情况 */ public class NetworkConnectivityUtil { private static String TAG = "NetworkConnectivityUtil"; //Log Tag private static ConnectivityManager connMgr; //网络链接管理器对象 public NetworkConnectivityUtil(Context ctx){ connMgr = (ConnectivityManager) ctx.getSystemService(ctx.CONNECTIVITY_SERVICE); } /** * 检查网络链接情况 * @return true:网络连通 * false:网络未连通 */ public boolean checkNetwork() { NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); return networkInfo.isConnected() ? true : false; } /** * 检查WIFI链接情况 * @return true:网络连通 * false:网络未连通 */ public boolean checkWifi() { NetworkInfo wifiInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); return wifiInfo.isConnected() ? true : false; } /** * 检查手机网络链接情况 * @return true:网络连通 * false:网络未连通 */ public boolean checkMobile() { NetworkInfo mobileInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); return mobileInfo.isConnected() ? true : false; } }
在工做线程中进行网络操做
/** * 经过一个异步任务AsyncTask进行网络操做 */ new AsyncTask<String, Void, String>() { @Override protected String doInBackground(String... params) { //NetworkUtil.get即上面HttpURLConnection中的方法 return NetworkUtil.get(params[0]); } @Override protected void onPostExecute(String s) { editText.setText(s.substring(0, 100)); } }.execute(url);