9.1 初识Http协议
前言
在android app开发中,经常做的一个操作就是网络访问,具体来说,当你向用户展示一个炫酷的页面之前,通常需要先向服务器请求数据,然后拿到数据之后,解析数据,再更新到你的UI界面上;当用户在你的app上做一些交互操作时,你需要把用户操作完成后的变化的这部分数据提交给服务器,这是服务器一般回去数据库中更新该用户的一些数据等等。既然网络的使用是如此的频繁,也是如此的重要,那我们还是有必要去稍加了解一下网络访问的一些基础原理。这篇文章就主要介绍一下http协议。
Http概述
概念 HTTP全称Hypertext Transfer Protocol,即超文本传输协议,是基于TCP/IP的协议,在计算机网络中属于五层协议(从底层到上层依次是:物理层、数据联络层、网络层、运输层、应用层)中最上面的应用层。它定义了客户端如何向服务器请求数据以及服务器端如何把数据传输给客户端。
特点
1.支持C/S(客户端/服务器端)架构。
2.简单快速:客户端向服务器端请求服务时,只需传送请求方法和路径即可。因为HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.HTTP协议基于请求响应模型,一次请求对于一次响应,请求只能由客户端发出,服务器只能被动的等待请求作出响应。
4.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
工作流程
一次完整的Http操作称为一个事务,其工作流程大体上可以分为四步:
1.客户端与服务器建立连接
2.连接成功后,客户端通过一个URL地址向服务器发送请求
3.服务器接收到请求后,给予相应的响应信息。
4.客户端接收到服务器返回的信息,并面向用户作出相应的UI显示,最后客户端断开连接,这样一次http请求也就结束了。
URL详解
URL全称Universal Resource Locator,即统一资源定位符。URL一般由三部组成:协议、主机域名或者IP地址(也包括端口号)、主机资源的具体地址。如目录和文件名等。一般格式如下:
schema://host:port/path/params scheme 指定低层使用的协议(例如:http, https, ftp) host HTTP服务器的域名IP地址 port# HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口, 必须指明,例如 http://www.cnblogs.com:8080/ path 访问资源或者具体文件目录的路径 params Url请求参数
举个例子
http://www.dearxiaoyun.com.cn/app/index.php?r=goods.detail&id= http:表示协议 www.dearxiaoyun.com.c:表示服务器域名,这里用的就是默认端口号 index.php:表示访问的具体文件路径 r、id:表示URL请求的参数
请求方式
客户端和服务端通过HTTP协议来传递数据,这一部分是通过请求报文和响应报文的方式来实现的,这一部分可以在读者熟练的掌握了网络操作以后作为进阶来自行学习,这里不展开细讲。对于网络请求,我们一般关心请求方式,常见的请求方式有:PUT、DELETE、POST、GET、OPTION、HEAD、TRACE、CONNECT。其中前四种对应增删改查,而在android app开发当中,我们最常用的还是GET、POST请求。
OPTION:请求一些选项的信息
GET:根据URL地址,读取服务器的数据
HEAD:请求读取由URL所标志的信息的首部
POST:给服务器发送信息
PUT:在指明的URL下存储一个文档
DELETE:删除指明的URL所标志的资源
TRACE:用来进行环回测试的请求报文
CONNECT:用于代理服务器
响应状态码
当发出一次网络请求以后,一般会收到一个状态码来表示这次请求的结果,状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:
100~199:指示信息,表示请求已接收,继续处理 200~299:请求成功,表示请求已被成功接收、理解、接受 300~399:重定向,要完成请求必须进行更进一步的操作 400~499:客户端错误,请求有语法错误或请求无法实现 500~599:服务器端错误,服务器未能实现合法的请求
常见的状态码如下:
200 OK:客户端请求成功,服务器已成功处理了请求,并返回了信息。只有这个状态码才能确定请求成功。400 Bad Request:客户端请求有语法错误,不能被服务器所理解401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用403 Forbidden:服务器收到请求,但是拒绝提供服务404 Not Found: 服务器找不到请求的网页,也就是说客户端给的URL地址有误。500 Internal Server Error:服务器发生不可预期的错误503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常
写在最后
要想查看网页或者手机请求网络的请求报文和响应报文有很多种方法,这里推荐2个抓包工具:Fiddler,Charles(青花瓷),这个使用简单,可自行查阅研究。
9.2 Android基于HTTP协议网络传输的实现
概述
在上一篇文章,我们已经讲到了http协议的一些基础概念以及客户端和服务端的一个交互流程,而这一篇就来讲讲在android app开发中,是如何实现一个网络请求的。虽然现在已经有了很多比较优秀的网络访问的开源库,但是对于一个初学者而言,还是应当先去学习一下android原生的API的实现方式,因为这是官方推出的原生API,而且所有的开源库也都是基于这些原生的API基础上封装而成的。Android原生API提供的网络请求方式是通过HttpURLConnection或者HttpClient这2个类来实现的。由于在Android6.0版本Google官方直接删除了HttpClient类库,所以这里只讲解HttpURLConnection。
HttpURLConnection简介:
HttpURLConnection是一种多用途、轻量极的HTTP客户端。它的API简单,体积较小,因而非常适用于Android项目,压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用,使用它来进行HTTP操作可以适用于大多数的应用程序。HttpUrlConnection是Android SDK的标准实现,直接支持系统级连接池,即打开的连接不会直接关闭,在一段时间内所有程序可共用;直接在系统层面做了缓存策略处理,加快重复请求的速度。
HttpURLConnecetion的使用步骤
GET请求
(1)创建一个URL 对象,通过这个URL对象获取到HttpURLConnection 的实例,创建连接。
URL url = new URL('http://img1.3lian.com/2015/a1/43/d/82.jpg');// 打开一个HttpURLConnection连接HttpURLConnection connection = (HttpURLConnection) url.openConnection();(2)得到了HttpURLConnection 连接对象以后,可以设置一些关于连接的属性,比如设置连接超时、读取超时
connection.setConnectTimeout(8 * 1000); //设置连接超时,单位毫秒connection.setReadTimeout(8 * 1000); //设置读取超时,单位毫秒// 设置是否使用缓存 默认是true,而且只有Get请求才有效,Post是无效的connection.setUseCaches(true);// 设置为GET请求connection.setRequestMethod('GET');(3) 判断相应码
//相应码为200的时候才是连接成功,才能进行后续的获取数据操作connection.getResponseCode()
(4)获取到服务器返回的输入流,对I/O流的操作
InputStream in = connection.getInputStream();//服务器返回的数据都在这个流里面了,接下来就是I/O流的读写操作了
下载网络图片(Get请求)
HttpUtils
/**
* 下载网络图片
*/
fun getBitmap(imgUrl: String): Bitmap? { var bitmap: Bitmap? = null
try { //创建URL对象
val url = URL(imgUrl) // 打开一个HttpURLConnection连接
val connection = url.openConnection() as HttpURLConnection //设置连接相关的属性
connection.readTimeout = 8 * 1000 //读取数据超时,8s
connection.connectTimeout = 8 * 1000 //连接超时,8s
//判断响应码,200成功
var responseCode = 0
responseCode = connection.responseCode if (responseCode == 200) { //连接成功
//获取服务器返回的输入流
val inputStream = connection.inputStream
Log.i('tag', 'downloadImg: ' + inputStream)
bitmap = BitmapFactory.decodeStream(inputStream) //关闭连接
connection.disconnect()
}
} catch (e: IOException) {
e.printStackTrace()
} return bitmap
}Activity
public class MainActivity extends AppCompatActivity { private static final String URL_IMG = 'http://img1.3lian.com/2015/a1/43/d/82.jpg'; private ImageView imageView; @Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView);
findViewById(R.id.download_btn).setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View view) { new DownloadImageTask().execute(URL_IMG);
}
});
} /**
* 下载网络图片的异步任务
*/
class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { @Override
protected Bitmap doInBackground(String... strings) { return HttpUtils.getBitmap(strings[0]);
} @Override
protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); if (bitmap != null) { //在主线程更新UI
imageView.setImageBitmap(bitmap);
}
}
}
}别忘了加上网络权限
<!-- 连接网络的权限 --><uses-permission android:name='android.permission.INTERNET' />
登录(Post请求)
PostUtils
object PostUtils {
/**
* 用户名,密码登录(需要配置服务器信息)
* @param loginUrl 登录的URL
* @param name 用户名
* @param pwd 密码
* @return 服务器返回的登录结果
*/
fun login(loginUrl: String, name: String, pwd: String): String {
//登录后,服务器返回的信息
var result = ''
try {
val url = URL(loginUrl)
//创建连接
val connection = url.openConnection() as HttpURLConnection
//设置请求方式为post请求
connection.requestMethod = 'POST'
//设置连接,读取超时
connection.readTimeout = 8 * 1000
connection.connectTimeout = 8 * 1000
//设置允许输入,输出
connection.doOutput = true
connection.doInput = true
//Post方式不能缓存,需手动设置为false
connection.useCaches = false
val data = 'passwd=' + URLEncoder.encode(pwd, 'UTF-8') +
'&number=' + URLEncoder.encode(name, 'UTF-8')
//这里可以写一些请求头的东西
//connection.setRequestProperty('Connection','Keep-Alive');
//获取输出流
val out = connection.outputStream
out.write(data.toByteArray())
out.flush()
//判断是否响应成功
if (connection.responseCode == 200) {
result = getStr(connection)
}
} catch (e: IOException) {
e.printStackTrace()
}
return result
}
/**
* 获取字符串
*
* @param connection
* @return
* @throws IOException
*/
@Throws(IOException::class)
private fun getStr(connection: HttpURLConnection): String {
// 获取响应的输入流对象
val inputStream = connection.inputStream
// 创建字节输出流对象
val bos = ByteArrayOutputStream()
// 定义读取的长度
var len = 0
// 定义缓冲区
val buffer = ByteArray(1024)
// 按照缓冲区的大小,循环读取
while ((len = inputStream.read(buffer)) != -1) {
// 根据读取的长度写入到os对象中
bos.write(buffer, 0, len)
}
// 返回字符串
val msg = String(bos.toByteArray())
// 释放资源
inputStream.close()
bos.close()
return msg
}
}本文转自:陪你唠嗑





































苏公安网备 32011402010933号