一、Http协议
1、HTTP简介
大多数的Android应用程序都会使用HTTP协议来发送和接收网络数据,Android中主要提供了两种方式来进行HTTP操作,HttpURLConnection和HttpClient。所以首先介绍下Http协议。HTTP(HyperText Transfer Protocol,超文本传输协议) 是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。HTTP协议采用了请求/响应模型。通常,由HTTP客户端发起一个请求,建立一个到服务器指定端口(默认是80端口)的TCP连接,HTTP服务器则在那个端口监听客户端发送过来的请求。一旦收到请求,服务器(向客户端)发回一个状态行和响应消息,最后消息传送完毕时,可以关闭客户端和服务器端的连接。
2、HTTP消息
Http消息由从客户机到服务器的请求和从服务器到客户机的响应构成。这两种类型的消息均由一个起始行,一个或者多个头域,一个指示头域结束的空行和可选的消息体组成。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。
请求消息格式如下:
请求行 - 通用信息头 - 请求头 - 实体头 - 消息主体
如下所示:
应答消息格式如下:
状态行 - 通用信息头 - 响应头 - 实体头 - 消息主体
如下所示:
3、请求方法
注:Android HttpUrlConnection中只未提供Connect方法
方法 |
描 述 |
GET |
请求指定url的数据,请求体为空(例如打开网页) |
POST |
请求指定url的数据,同时传递参数(在请求体中) |
HEAD |
类似于get请求,只不过返回的响应体为空,用于获取响应头 |
PUT |
从客户端向服务器传送的数据取代指定的文档的内容 |
DELETE |
请求服务器删除指定的页面 |
CONNECT |
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器 |
OPTIONS |
允许客户端查看服务器的性能 |
TRACE |
回显服务器收到的请求,主要用于测试或诊断。 |
4、状态码详解(Status Code)
状态码是一个用于标识服务器响应结果的代码,由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 |
请求未完成。服务器不支持所请求的功能 |
5、头域详解
通用头域
通用头域包含请求和响应消息都支持的头域,通用头域包含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等
实体头域
请求消息和响应消息都可以包含实体信息,实体信息一般由实体头域和实体组成。实体头域包含关于实体的原信息,实体头包括Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header;
1)Location:Location头域用于重定向接收者到一个新URI地址;
2)Server:Server头域包含处理请求的原始服务器的软件信息;
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
1、HttpClient VS HttpURLConnection
HttpClient和HttpURLConnection是Android中提供的两种进行HTTP操作的方式,这两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv6、以及连接池等功能。
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团队也在积极对该方式进行维护和升级。
2、HttpURLConnection的使用
/**
* 采用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;
}
}
三、Android网络操作一般流程
开启网络权限
检查设备的网络连接状况
在工作线程中进行网络操作
<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);
来源:oschina
链接:https://my.oschina.net/u/2621851/blog/646796