目录
前言
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
前言
大部分andriod应用需要与服务器进行数据交互,HTTP、FTP、SMTP或者是直接基于SOCKET编程都可以进行数据交互,但是HTTP必然是使用最广泛的协议。
在总结之前先来了解一下Http协议,也是对技术支持的一些补充。
1、http协议定义
HTTP协议是基于TCP/IP协议之上的协议,是客户端和服务器之间的应用层的协议,是通用的、无状态的面向对象的协议。
2、客户端连接服务器实现内部的原理
分析上图,步骤如下:
第一步:在浏览器客户端中得到用户输入的内容。
第二步:浏览器得到这个网址之后,内部会将这个域名发送到DNS上,进行域名解析。得到它的IP之后就会链接到指定的服务器上,假如服务器的地址是:221.104.13.32:80,从浏览器到服务器端口它使用到最底层的TCP/IP协议。
第三步:实现TCP/IP协议用Socket来完成,使用了Socket的套接字。
第四步:服务器端的80端口监听客户端的链接,这样客户端到服务器就链接上了。
通俗一点讲,用户在浏览器输入网址,通过http协议发出去,网址经过DNS域名解析,解析成指定的ip地址,并在80端口上监听用户的请求。服务器监听到请求之后,会以三种方式返回给客户端:HTML、XML、JASON。
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为好。
4、HTTP返回请求数据的三种方式:
- 以HTML代码内容返回。
- 以XML字符串的形式返回,在以后的android开发中这种形式返回数据比较多。
- 以JSON对象形式返回,在网络流量上考虑JSON要比XML方式要好一些,便于解析。
在Android当中,一般使用xml和Json数据解析。
一、Android中的网络请求方式
Android中的WebView控件已经在后台帮我们处理好了发送HTTP请求、接收服务响应、解析返回数据,以及最终的页面展示这几步工作,不过由于它封装得太好了,反而不能直观地看出HTTP协议是如何工作的。因此接下来我们通过手动发送HTTP请求的方式,来更加深入的了解这一过程。
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中添加如下权限
二、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();
}
-
效果
-
打印消息
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();
}
-
效果
三、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也可以实现功能。
3、HttpCient实现步骤
(i)创建代表客户端的HttpClient对象。
(ii)创建代表请求的对象,如果需要发送GET请求,则创建HttpGet对象,如果需要发送POST请求,则创建HttpPost对象。
【对于发送请求的参数,GET和POST使用的方式不同,GET方式可以使用拼接字符串的方式,把参数拼接在URL结尾;POST方式需要使用setEntity(HttpEntity entity)方法来设置请求参数。】
(iii)调用HttpClient对象的execute(HttpUriRequest request)发送请求,执行该方法后,将获得服务器返回的HttpResponse对象。服务器发还给我们的数据就在这个HttpResponse相应当中。调用HttpResponse的对应方法获取服务器的响应头、响应内容等。
(iv)检查相应状态是否正常。服务器发给客户端的相应,有一个相应码:相应码为200,正常;相应码为404,客户端错误;相应码为505,服务器端错误。
(v)获得相应对象当中的数据。
4、HttpCient实现实例(GET)(Demo)
private void sendRequestWithHttpClient() {
new Thread(new Runnable() {
@Override
public void run() {
//用HttpClient发送请求,分为五步
//第一步:创建HttpClient对象
HttpClient httpCient = new DefaultHttpClient();
//第二步:创建代表请求的对象,参数是访问的服务器地址
HttpGet httpGet = new HttpGet("http://www.baidu.com");
try {
//第三步:执行请求,获取服务器发还的相应对象
HttpResponse httpResponse = httpCient.execute(httpGet);
//第四步:检查相应的状态是否正常:检查状态码的值是200表示正常
if (httpResponse.getStatusLine().getStatusCode() == 200) {
//第五步:从相应对象当中取出数据,放到entity当中
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8");//将entity当中的数据转换为字符串
//在子线程中将Message对象发出去
Message message = new Message();
message.what = SHOW_RESPONSE;
message.obj = response.toString();
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
-
效果
5、HttpCient实现实例(POST)(Demo)
private void sendRequestWithHttpClientPOST() {
new Thread(new Runnable() {
@Override
public void run() {
//用HttpClient发送请求,分为五步
//第一步:创建HttpClient对象
HttpClient httpClient = new DefaultHttpClient();
//第二步:创建代表请求的对象,参数是访问的服务器地址
HttpPost httpPost= new HttpPost("https://reg.163.com/logins.jsp");
// 使用NameValuePair(键值对)存放参数
List data = new ArrayList();
// 添加键值对
data.add(new BasicNameValuePair("id", "helloworld"));
data.add(new BasicNameValuePair("pwd", "android"));
try {
// 使用setEntity方法传入编码后的参数
httpPost.setEntity(new UrlEncodedFormEntity(data, "utf-8"));
//第三步:执行请求,获取服务器发还的相应对象
HttpResponse httpResponse = httpClient.execute(httpPost);
//第四步:检查相应的状态是否正常:检查状态码的值是200表示正常
if (httpResponse.getStatusLine().getStatusCode() == 200) {
Log.i("mm","success");
//第五步:从相应对象当中取出数据,放到entity当中
HttpEntity entity = httpResponse.getEntity();
String response = EntityUtils.toString(entity, "utf-8");//将entity当中的数据转换为字符串
//在子线程中将Message对象发出去
Message message = new Message();
message.what = 1;
message.obj = response.toString();
handler.sendMessage(message);
}else{
Log.i("mm","fail");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
-
效果
四、HttpURLConnection抽象请求方法(Demo)
1、JQuery
如果你使用过JQuery(一个javasript库),你一定对JQuery的网路编程印象深刻,比如一个HTTP请求只需以下几行代码。
// JQuery的post方法
$.post("http://www.cnblogs.com/gzdaijie",{
"stu_no":12345,
"stu_name":"Tom",
}).done(function(){
//...请求成功的代码
}).fail(function(){
//...请求失败的代码
}).always(function(){
//...总会执行的代码
})
我们当然不希望每次网络请求都写下“二”中那么繁琐的代码,那么android的HTTP请求能否像JQuery那么简单呢?当然可以!下面的代码实现了HttpURLConnection的HTTP请求方法封装
2、定义接口HttpCallbackListener,为了实现回调
// 定义HttpCallbackListener接口
// 包含两个方法,成功和失败的回调函数定义
public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
3、创建HttpTool类,抽象请求方法(GET)
public class HttpTool {
public static void sendRequest(final String address,
final HttpCallbackListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
// 调用URL对象的openConnection方法获取HttpURLConnection的实例
URL url = new URL(address);
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", "请求chenggong");
InputStream in = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
if (listener != null) {
// 回调方法 onFinish()
listener.onFinish(response.toString());
}
} else {
Log.i("HttpURLConnection.GET", "请求失败");
}
} catch (Exception e) {
e.printStackTrace();
if (listener != null) {
// 回调方法 onError()
listener.onError(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}
4、调用示例
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//使用该HttpTool发起GET请求
String url = "http://www.jianshu.com";
HttpTool.sendRequest(url,new HttpCallbackListener(){
@Override
public void onFinish(String response) {
// ...省略对返回结果的处理代码
Message message = new Message();
message.what = 0;
message.obj = response.toString();
handler.sendMessage(message);
}
@Override
public void onError(Exception e) {
// ...省略请求失败的处理代码
}
});
}
});
-
效果
5、抽象请求方法(POST)
/* 在GET方法实现的基础上增加一个参数params即可,
* 将参数转换为字符串后传入
* 也可以传入键值对集合,再处理 */
public static void sendRequest(final String address,
final String params, final HttpCallbackListener listener){
//...
}
五、文件下载(Demo)
1、DownLoadManager简介
虽然我们可以通过HTTP请求的方式下载文件,在api level 9之后,android系统为我们提供了DownLoadManager类,这是android提供的系统服务,我们通过这个服务完成文件下载。整个下载过程全部交给系统负责,不需要我们过多的处理。
其包含两个内部类:
DownLoadManager.Query:主要用于查询下载信息。
DownLoadManager.Request:主要用于发起一个下载请求。
2、功能实现
//使用系统下载器下载
private void downloadAPK(String versionUrl, String versionName) {
//创建下载任务
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(versionUrl));
request.setAllowedOverRoaming(false);//漫游网络是否可以下载
request.setTitle("jar包下载");//下载是通知栏标题
// request.setAllowedNetworkTypes() 设置制定网络下下载,传入系统常量值。
// 提供的网络常量有:NETWORK_BLUETOOTH、NETWORK_MOBILE、NETWORK_WIFI。
//设置文件类型,可以在下载结束后自动打开该文件
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(versionUrl));
request.setMimeType(mimeString);
//在通知栏中显示,默认就是显示的
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
request.setVisibleInDownloadsUi(true);
//sdcard的目录下的download文件夹,必须设置
request.setDestinationInExternalPublicDir("/download/", versionName);
//request.setDestinationInExternalFilesDir(),也可以自己制定下载路径
//将下载请求加入下载队列
downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
//加入下载队列后会给该任务返回一个long型的id,
//通过该id可以取消任务,重启任务等等,看上面源码中框起来的方法
mTaskId = downloadManager.enqueue(request);
//注册广播接收者,监听下载状态
registerReceiver(receiver,
new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
广播接收器
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
checkDownloadStatus();//检查下载状态
}
};
检查下载状态
private void checkDownloadStatus() {
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(mTaskId);//筛选下载任务,传入任务ID,可变参数
Cursor c = downloadManager.query(query);
if (c.moveToFirst()) {
int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
switch (status) {
case DownloadManager.STATUS_SUCCESSFUL:
Toast.makeText(this,"下载完成",Toast.LENGTH_SHORT).show();
break;
case DownloadManager.STATUS_FAILED:
Toast.makeText(this,"下载失败",Toast.LENGTH_SHORT).show();
break;
}
}
}
-
效果
-
文件下载路径
六、JSON数据解析(Demo)
1、解析单条Json数据
//单条数据
private String parseItemJSONWithJSONObject(String jsonData) {
String status=null;
String message=null;
try {
//第一步:将从网络字符串jsonData字符串装入JSONObject
JSONObject jsonObject = new JSONObject(jsonData);
//第二步:因为单条数据,所以用jsonObject.getString方法直接取出对应键值
status = jsonObject.getString("status");
message = jsonObject.getString("message");
} catch (Exception e) {
e.printStackTrace();
}
return "status: "+ status+"\n"+"message: " + message;
}
2、解析多条Json数据
//多条数据
private String parseJSONWithJSONObject(String jsonData) {
StringBuffer sb =new StringBuffer();
try {
//第一步:将从网络字符串jsonData字符串装入JSONObject,即JSONObject
JSONObject jsonObject = new JSONObject(jsonData);
//第二步:因为多条数据,所以将"取出来的、要遍历的"字段装入JSONArray(这里要遍历data字段)
JSONArray jsonArray = jsonObject.getJSONArray("data");
//第三步:循环遍历,依次取出JSONObject对象
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject2 = jsonArray.getJSONObject(i);
String time = jsonObject2.getString("time");
String ftime = jsonObject2.getString("ftime");
String context = jsonObject2.getString("context");
sb.append("time: " + time+" "+"ftime: " + ftime+"\n"+"context: " + context+"\n\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
-
效果
七、图片数据解析(Demo)
使用Http请求下载网络图片并通过handler机制更新系统ui。
public class MainActivity extends AppCompatActivity {
private final String PATH = "https://www.baidu.com/img/bdlogo.png";
private Button button;
private ImageView imageView;
public Handler handler = new Handler() {
public void handleMessage(Message msg) {
if (msg.what == RESULT_OK) {
byte[] data = (byte[]) msg.obj;
//使用Bitmap类解析图片
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,
data.length);
imageView.setImageBitmap(bitmap);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView1);
button = (Button) findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
picDown();
}
});
}
private void picDown() {
new Thread(new Runnable() {
@Override
public void run() {
HttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(PATH);
try {
HttpResponse httpResponse = httpClient.execute(httpGet);
if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
byte[] data = EntityUtils.toByteArray(httpResponse
.getEntity());
Message message = Message.obtain();
message.obj = data;
message.what = RESULT_OK;
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (httpClient != null
&& httpClient.getConnectionManager() != null) {
httpClient.getConnectionManager().shutdown();
}
}
}
}).start();
}
}
【附录】
Demo
- HttpURLConnection
- HttpCient
- HttpURLConnection抽象请求方法
- 文件下载
- JSON数据解析
- 图片数据解析
整理作者:汪博
少壮不努力,老大徒悲伤。
本文为Android学习规划打造,如有不好的地方请多多指教。