网络相关知识
HTTP
一次http网络请求的过程
浏览器发起请求-> 解析域名得到 ip 进行 TCP 连接 ->浏览器发送 HTTP 请求和头信息发送->服务器对浏览器进行应答,响应头信息和浏览器所需的内容-> 关闭 TCP 连接或保持-> 浏览器得到数据数据进行操作。
对于HTTP协议工作原理:就是客户端向服务器发出一条HTTP请求,服务器收到请求之后会返回一些数据给客户端,然后客户端再对这些数据进行解析和处理。
先找到对方 ip 地址,然后用指定的传输协议传送到指定的端口。
HTTP分层
- Application Layer 应用层:HTTP、FTP、DNS
- Transport Layer 传输层(通讯规则、传输协议):TCP、UDP(负责传输,找到端口)
- Internet Layer 网络层:IP(负责连接,找到IP)
- Link Layer 数据链路层:以太网、Wi-Fi(以太网:网线,为网络提供现实世界(物理设备的支持))
为什什么要分层?
因为网络的不稳定性,所以要url分块传输
常见通讯规则、传输协议:TCP/UDP
UDP(面向无连接)-->聊天、网络视频会议、步话机
DatagramSocket
将数据及源和目的封装成数据包中,不需要建立连接
每个数据包的大小限制在64k内
因无连接,是不可靠的协议
不需要建立连接,速度快
TCP(面向连接)-->下载,打电话
ServiceSocket
建立连接,形成传输数据的通道
在连接中进行大数据量的传输
通过三次握手完成连接,是可靠的协议
必须建立连接,效率会稍低
丢包
todo:失败了会重连吗
什么是iP地址
Internet上的主机有两种方式表示地址: 域名:www.baidu.com, IP 地址:202.108.35.210 ,域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。
什么是端口
用于标识进程的逻辑地址,不同进程的标识,有效地址065535,其中01024系统使用或保留端口。
HTTP 协议版本
HTTP/1.0
- 链接后,只能获取一个web资源。
- 链接后,发送请求,服务器做出响应,链接立即断开。
HTTP/1.1
- 链接后,可以获取多个web资源。
- 链接后,发送请求,服务器做出响应,链接不会立即断开。再次发送请求,直接有一段时间没操作,自动断开。(服务端可以配置)
三次握手
SYN,SYN/ACK,ACK
syn 客户端发送syn包给服务器,进入服务状态
syn-ack 服务器收到,客户端确实并发送给syn.这时进去接受状态
ack 客户端收到服务端的syn-ack,并向服务器确认,此时链接成功!
第一次本方发送请求,第二次对方确认连接,第三次本方再次发送确认信息告诉对方,这样双方就都知道了,从而才能建立连接。
三次握手的目的,两次行不行?
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误的问题。网络不好的话不知道双方的状态
四次挥手
- 我不要消息了
- 我知道了
- 我没有消息给你发了
- 我知道了
为什么 TCP 连接在断开时是四次挥手而不是三次?
因为在客户端停止向服务器发送消息时,也许服务器还有消息需要向客户端发送,因此在它对客户端的「Fin」(即「我不再给你发送消息」,这个词不必记住)消息进行回应时,不需要立即附加上「我也不再向你发送消息」。在稍后服务器的消息发送完毕之后,才需要向客户端发送通知。
fin 发送请求连接
ack 同意断开连接
fin+ack 服务端断开连接
ack 同意断开
Keep-Alive
从 HTTP/1.1起,默认都开启了 Keep-Alive,保持连接特性,简单地说,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接 Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如 Apache)中设定这个时间。虽然这里使用 TCP 连接保持了一段时间,但是这个时间是有限范围的,到了时间点依然是会关闭的,所以我们还把其看做是每次连接完成后就会关闭。
HTTP 的缺点
通信使用明文, 内容可能被窃听
不验证通信方身份, 因此有可能遭遇伪装
WWW
HTTP 协议同时具备极强的扩展性,虽然浏览器请求的是 http://www.sina.com.cn/的首页 ,但是新浪在 HTML 中可以链入其他服务器的资源,比如 XXX,从而将请求压力分散到各个服务器上,并且,一个站点可以链接到其他站点,无数个站点互相链接起来,就形成了 World Wide Web,简称 WWW。
HTTPS
- HTTPS并不是一个单独的协议,对于一些隐秘性比较高的数据可以用https协议。( 它是在TCP层与http层之间加了个SSl/TLS。TLS是SSL v3.0的升级版,而SSL协议,是一种安全传输协议。)
- SSL/TLS层负责客户端和服务器之间的加解密算法协商、密钥交换、通信连接的建立。
主要用到对称加密、非对称加密、证书,等技术进行客户端与服务器的数据加密传输,最终达到保证整个通信的安全性。 - 说白了就是加密通信的 HTTP
第三步:请求网址后返回证书的公钥和数字证书,客户端验证数字证书的有效性,是ca的,怎么验证的?数字签名,都有标准的,x509
第四步:客户端 通过后,使用公钥(对称加密里的)加密内容,非对称加密加密分别对公钥和内容信息摘要(hash,数字签名),发送
第六步:服务端使用非对称加密解密对称加密的 key,然后通过对称加密的 key 解密内容。并验证数字签名,看是否篡改过。后台返回数据
第七步:客户端 用对称加密的 key 解密内容
详细
HTTPS 要使客户端与服务器端的通信过程得到安全保证,必须使用的对称加密算法传输数据(效率),但是协商对称加密算法的过程,需要使用非对称加密算法(不适合大量数据)来保证安全(用非对称加密来给对称加密里的秘钥进行加密),然而直接使用非对称加密的过程本身也不安全,会有中间人篡改公钥的可能性,所以客户端与服务器不直接使用公钥,而是使用数字证书签发机构颁发的证书来保证非对称加密过程本身的安全。这样通过这些机制协商出一个对称加密算法,就此双方使用该算法进行加密解密。从而解决了客户端与服务器端之间的通信安全问题。
证书就是HTTPS中数字证书,证书编号就是数字签名,而第三方机构就是指数字证书签发机构(CA)。
更详细
SSL/TLS防止的安全风险:
- 窃听风险
- 篡改风险
- 冒充风险
SSL/TLS防止的安全风险原理:
- 加密:非对称加密+对称加密,主要解决的是窃听风险
- 校验:数字签名,主要解决的是篡改风险
- 证书:数字证书,主要解决的是冒充风险
非对称加密
- 对称加密的问题:秘钥如何保存和传输(非对称加密算法)
- “非对称加密”加密算法,特点是公钥加密后的密文,只有私钥可以解密,私钥加密后的密文,公钥都可以解密。私钥只有一个人有,而公钥可以发给所有的人。
- 非对称加密一般不会单独拿来使用,他并不是为了取代对称加密而出现的,非对称加密速度比对称加密慢 很多,极端情况下会慢 1000 倍,所以一般不会用来加密大量数据,通常我们经常会将对称加密和非对称 加密两种技术联合起来使用,例如用非对称加密来给称加密里的秘钥进行加密(即秘钥交换)。
RSA 加密简单过程
- 服务端生成配对的公钥和私钥
- 私钥保存在服务端,公钥发送给客户端
- 客户端使用公钥加密明文传输给服务端
- 服务端使用私钥解密密文得到明文
数字签名
数字签名技术结合Hash算法和加密算法,来防止消息被篡改和进行身份认证。
数字签名就是:发送方使用自己的私钥对信息摘要加密产生
- 发送方使用Hash算法对原文产生信息摘要,原文不变则信息摘要不变。
- 发送方使用自己的私钥对信息摘要加密产生数字签名,并和加密的原文一起发送。
- 接收方使用发送方的公钥解密获取数字签名
- 接收方使用Hash算法对原文计算信息摘要与解密的信息摘要比对,成功表示未篡改、未成功表示篡改。
**
数字证书
数字签名一般不单独使用,基本都是用在数字证书里实现 SSL 通信协议。
还有有效期
在 Android 中使用 HTTPS
正常情况:直接使用
需要自⼰写证书验证过程的场景
- 用的是自签名证书(例如只用于内网的 https)
- 信息不全,缺乏中间证书机构(可能性不大)
- 手机操作系统较旧,没有安装最新加入的根证书
OKHTTP
okhttp中就能验证自己的签名,就是为了让自己的签名通过验证。
如果是ca机构的证书,OKHTTP不需要配置直接就可以访问,如果是自定义的证书,OKHTTP就不行了,但是可以信任指定证书或者所有证书来访问。
拦截器:网络日志、缓存(okhttp自己也有)
移动端网络优化
弱网环境优化
-
请求合并:如果某个页面内请求过多,可以将多个请求合并为一个请求
-
常用信息添加本地缓存,可以让我们的应用看起来更快, 也能避免一些不必要的流量消耗.
-
可以在获取图片时告知服务器需要的图片的宽高, 以便服务器给出合适的图片, 避免浪费. oss
-
HttpDns
[[关于httpdns]]
HTTPDNS 利用 HTTP 协议与 DNS 服务器交互,代替了传统的基于 UDP 协议的 DNS 交互,绕开了运营商的 Local DNS,有效防止了域名劫持,提高域名解析效率。另外也减少了平均访问的的延迟、降低了用户连接的失败率。后期观察效果好的话,可以推广所有域名使用。 -
IP 直连: 省去 DNS 解析过程,DNS 全名 Domain Name System,解析意指根据域名得到其对应的 IP 地址。如 http://www.codekk.com 的域名解析结果就是 104.236.147.76。首次域名解析一般需要几百毫秒,可通过直接向 IP 而非域名请求,节省掉这部分时间,同时可以预防域名劫持等带来的风险。当然为了安全和扩展考虑,这个 IP 可能是一个动态更新的 IP 列表,并在 IP 不可用情况下通过域名访问。
-
减小请求数据和返回数据大小: 对于 POST 请求,请求体可以做 Gzip 压缩,如日志,response.setHeader("Content-Encoding", "gzip") ;
-
连接复用:节省连接建立时间,如开启Connection:keep-alive。在请求头中添加(已经默认开启了)
-
CDN优化,CDN说是可以使用最近的网络节点提供服务
Android P 要求网络请求必须为Https,Http请求会抛异常
Volley底层使用HttpUrlConnection实现,而HttpUrlConnection底层实质是OkHttp
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" /></network-security-config>
android:networkSecurityConfig="@xml/network_security_config"
Socket
Socket 就是为网络服务提供的一种机制,通讯的两端都必须有 Socket(套接字,就是接口的意思),网络通讯其实就是 Socket 间的通讯,数据在两个 Socket 间通过 IO 传输,IP 地址标识 Internet 上的计算机,端口号标识正在计算机上运行的进程(程序)。端口号与 IP 地址的组合得出一个网络套接字。
创建 Socket 连接时,可以指定使用的传输层协议,Socket 可以支持不同的传输层协议(TCP 或 UDP),当使用 TCP 协议进行连接时,该 Socket 连接就是一个 TCP 连接。
socket 则是对 TCP/IP 协议的封装和应用
1)创建客户端对象: Socket(String host,int port),指定要接收的IP地址和端口号
2)创建服务端对象:ServerSocket(int port):指定接收的客户端的端口
3)Socket accept():侦听并接受到此套接字的连接,服务器用于接收客户端socket对象的方法
主要通过S.getOutputstream和S.getInputSteram
注:服务器没有 socket 流,也就没有读写操作的流,服务器是通过获取到客户端的 socket 流(accept)然后获取到其中的读写方法,对数据进行操作的,也正是因为这样服务器与客户端的数据操作才不会错乱
UDP 传输的流程
创建 UDPSocket 发送服务对象:DatagramSocket (),可不指定端口
创建 UDPSocket 接收服务对象:DatagramSocket (int port),
发送:void send (DatagramPacket p)
接收:void receive (DatagramPacket p)
TCP 传输流程
Socket (客户端)和 ServiceSocket 服务端()
建立客户端和服务端
建立连接后,通过 socket 中的 IO 流进行数据的传输
关闭 socket
1)创建客户端对象: Socket (String host, int port),指定要接收的 IP 地址和端口号
2)创建服务端对象:ServerSocket (int port):指定接收的客户端的端口
3)Socket accept ():侦听并接受到此套接字的连接,服务器用于接收客户端 socket 对象的方法
主要通过S.getOutputstream 和S.getInputSteram
注:服务器没有 socket 流,也就没有读写操作的流,服务器是通过获取到客户端的 socket 流(accept)然后获取到其中的读写方法,对数据进行操作的,也正是因为这样服务器与客户端的数据操作才不会错乱
定义 tcp 的服务端
建立服务端的 socket 服务,ServerSocket,并监听一个端口
获取连接过来的客服端对象,通过 ServerSokcet 的 accept 方法。没有连接就会等,所以这个方法阻塞式的。
客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据。
关闭服务端(可选)
```
public class TCPServerDemo {
public static void main(String[] args) throws IOException {
// 建立服务端的socket服务,并监听一个端口
ServerSocket ss = new ServerSocket(10003);
// 通过accept方法获取连接过来的客户端对象
Socket socket = ss.accept();
String ip = socket.getInetAddress().getHostAddress();
System.out.println(ip + "......connected");
// 获取客户端发过来的数据,那么要使用客户端对象的读取流来获取数据
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf, 0, len));
socket.close();// 关闭客户端
ss.close();// 关闭服务端,可选的操作
}
}
tcp 分为客户端和服务端,客服端对象的对象是 socket,服务端对应的是 serversoceket
客户端,在建立的时候就可以去连接指定的主机
因为 tcp 是面向连接的,所以在建立 socket 时就需要有服务端存在
并连接成功,形成通路,才能在该通道上传输数据
public class TCPClientDemo {
public static void main(String[] args) throws Exception {
//1、创建客户端的socket服务,指定目的主机和端口
Socke1
}
}
Http缓存
Pragma和Cache-control共存时,Pragma的优先级是比Cache-Control高的。
only-if-cached表示不进行网络请求,完全只使用缓存,若缓存不命中,则返回503错误
- max-age:告知缓存多长时间,在没有超过缓存时间的情况下,请求会返回缓存内的数据,在超出max-age的情况下向服务端发起新的请求,请求失败的情况下返回缓存数据(测试中已验证),否则向服务端重新发起请求。
- max-stale:指示客户机可以接收超出max-age时间的响应消息,max-stale在请求设置中有效,在响应设置中无效。因此max-age和max-stale在请求中同时使用的情况下,缓存的时间可以为max-age和max-stale的和。
OKHTTP 一般控制缓存有两种方式:
1、在request里面去设置cacheControl()策略
2、在header里面去添加cache-control
有网时不缓存,没网时设置缓存,通过Cache-Control和max-age
public class HttpCacheInterceptor implements Interceptor {
@Overridepublic Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetWorkHelper.isNetConnected(MainApplication.getContext())) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
Response response = chain.proceed(request);
if (NetWorkHelper.isNetConnected(MainApplication.getContext())) {
int maxAge = 60 * 60; // 如果想要不缓存,直接时间设置为0,但是需要保存下来吧
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, max-age=" + maxAge)
.build();
} else {
int maxStale = 60 * 60 * 24 * 28; // tolerate 4-weeks stale
response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
.build();
}
return response;
}}
//设置缓存100M
Cache cache = new Cache(new File(MainApplication.getContext().getCacheDir(),"httpCache"),1024 * 1024 * 100);
return new OkHttpClient.Builder()
.cache(cache)
.addNetworkInterceptor(new HttpCacheInterceptor())
.build();
其他
get和post的区别
get提交:提交的信息都显示在地址栏中,对于敏感数据不安全,传送的数据量较小,不能大于2KB,因为地址栏存储体积有限。将信息封装到了请求的请求行中。
post提交:提交的信息不显示在地址栏中,对于敏感数据安全,可以提交大体积数据。将信息封装到了请求体中
Cookie
Session和Cookie是一种记录客户端状态的机制的话是由服务器发给客户端特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会在headers里带上这些特殊的信息
Cookie 的作⽤
会话管理:登录状态、购物⻋
个性化:⽤户偏好、主题
Tracking:分析⽤户⾏为
Session
不同于Cookie保存在客户端中,他是保存在服务器上。
session 的工作原理
1.第一步创建Session
2.在创建了Session的同时,服务器会为该Session生成唯一的Session id
3.在Session被创建之后,就可以调用Session相关的方法往Session中增加内容
4.当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session
cookie和session 的区别
1.存放位置不同
2.存取方式的不同
3.安全性(隐私策略)的不同
4.有效期上的不同
5.对服务器造成的压力不同
URI和 URL
统一资源标识符(URI)用于标识某一互联网资源,而统一资源定位符(URL)表示资源的地点(互联网上所处的位置)。所以 URL 是 URI 的子集。URI:http:,https:,ftp:,本地文件系统(file:),和Jar文件(jar:)。
如何上传
用post,通过表单,不过结构单一,比较繁琐,还可以通过将数据转换成jsonstring、文件、string(base64)来上传,如果是多个图片可以写个循环上传。在上传的时候可以在head添加content-type告诉服务器这是什么数据
多线程断点下载
单线程的话从输入流的第0个字节读取
原理:服务器CPU分配给每条线程的时间片相同,服务器带宽平均分配给每条线程,所以客户端开启的线程越多,就能抢占到更多的服务器资源。所以要用多线程去读取。
请求网络时首先获取资源长度设置被进度条,然后除以要开启的线程数,计算出每个线程应该下载多少字节。然后每个线程去请求网络读取数据。
//设置本次http请求所请求的数据的区间
conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);
//请求部分数据,相应码是206
if(conn.getResponseCode() == 206){
//流里此时只有1/3原文件的数据
InputStream is = conn.getInputStream();
int length = conn.getContentLength();
特别注意: 对于移动端来说,如果不是比较大的文件,不建议使用这种方式上传,因为断点续传是通过分片上传实现的,上传单个文件需要进行多次网络请求,效率不高。
请求
* 请求行
* 请求方式
* POST、GET、HEAD、OPTIONS、DELETE、TRACE、PUT、CONNECT
* 请求地址
* 请求资源
* 协议版本
* HTTP/1.1
* 请求头
* 重要的头
* If-Modified-Since 必须和响应头信息一起来完成控制本地的缓存。
* Referer 当前的网页的来源。(防止盗链)
* User-Agent 判断浏览器的版本(文件下载的时候)
* 空行
* 请求体
* 封装post参数列表。
响应
* 响应行
* 协议版本
* HTTP/1.1
* 状态码
200 :请求成功处理,一切OK
302 :请求重定向
304 :服务器端资源没有改动,通知客户端查找本地缓存
404 :客户端访问资源不存在
500 :服务器内部出错
* 响应头
重要的头
* Location 和302一起完成重定向。
* Last-Modified 和请求头If-Modified-Since一起控制缓存。和状态码304
* Refresh 完成页面的定时跳转
* Content-Disposition 设置文件是以附件打开
* Expires: -1
* Cache-Control: no-cache
* Pragma: no-cache
* 禁用缓存(网银系统)
* 空行
* 响应体
* 存放真正的数据。
转发:找班长借钱,他自己找富班长借钱,一次请求
重定向:找班长借钱,发送一次请求,回了我没钱,返回状态码302,给副班长地址,再去找富班长借钱,又发送了一次
乱码的处理
乱码的出现是因为服务器和客户端码表不一致导致
//手动指定码表
text = new String(bos.toByteArray(), "UTF-8");
网络结构有哪些
C/S client/server
特点:
该结构的软件,客户端和服务端都需要编写。
开发成本较高,维护较为麻烦。
好处:
客户端在本地可以分担一部分运算。(大型网游)
B/S browser/server
特点:
该结构的软件,只开发服务器端,不开发客户端,因为客户端直接由浏览器取代。
开发成本相对低,维护更为简单。(页游)
缺点:所有运算都要在服务端完成。
什么是内网外网
私有网段地址, 内网IP有3种:第一种10.0.0.0~10.255.255.255,第二种172.16.0.0~172.31.255.255,第三种192.168.0.0~192.168.255.255
运营商给你的100.64.* . * 也是私有地址,以前大家大家共享一个地址池,有随机的公网地址,现在公网地址更加紧张,运营商只给客户分配私网地址,然后nat后大家共享一个公网地址
172.31.40.210内网
192.168.17.207外网
透明代理
透明代理就像任何其他类型的代理一样,客户端发送到 Web 服务器的请求在到达 Web 服务器之前会先到达透明代理。当您使用透明代理时,网站所有者知道您使用的是“HTTP_VIA”代理,也知道您的真实 IP。
HTTP_X_FORWARDED_FOR 标头将携带您的 IP 地址并启用 HTP_VIA,让网站知道该请求来自您,但通过代理服务器。毫无疑问,透明代理安全性比较低,但在某些情况下也是可以使用的,如用作身份验证的网关
在 Android 上发送 HTTP 请求的方式
一般有两种,HttpURLConnection和HttpClient,两种方式都支持HTTPS协议、以流的形式进行上传和下载、配置超时时间、IPv6、以及连接池等功能。HttpURLConnection的API提供的比较简单,可以更加容易地去使用和扩展它。而且速度快、还能节省电量。
HttpURLConnection用法:
发送GET请求
URL url = new URL(path);
//获取连接对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置连接属性
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//建立连接,获取响应吗
if(conn.getResponseCode() == 200){
//获取服务器响应头中的流,流里的数据就是客户端请求的数据
InputStream is = conn.getInputStream()
}
获取服务器返回的流,从流中把html源码读取出来
流转换成string的方法第一种:
byte[] b = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while((len = is.read(b)) != -1){
//把读到的字节先写入字节数组输出流中存起来
bos.write(b, 0, len);
}
//把字节数组输出流中的内容转换成字符串
//默认使用utf-8
text = new String(bos.toByteArray());
//第二种:
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder builder = new StringBuilder();
String line;
while ((line=reader.readLine())!=null) {
builder.append(line);
}
String text=builder.toString();
//第三种
Reader reader = null;
reader = new InputStreamReader(stream, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
//图片的流
InputStream is = null;
...
Bitmap bitmap = BitmapFactory.decodeStream(is);
ImageView imageView = (ImageView) findViewById(R.id.image_view);
imageView.setImageBitmap(bitmap);
post
connection.setRequestMethod("POST");
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.writeBytes("username=admin&password=123456");
HttpUtils的使用
public class HttpUtil {
public static void sendHttpRequest(final String address,
final HttpCallbackListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL(address);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
connection.setDoInput(true);
connection.setDoOutput(true);
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());
}
} catch (Exception e) {
if (listener != null) {
// 回调onError()方法
listener.onError(e);
}
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}
HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
@Override
public void onFinish(String response) {
// 在这里根据返回内容执行具体的逻辑
}
@Override
public void onError(Exception e) {
// 在这里对异常情况进行处理
}
});
这样的话,当服务器成功响应的时候我们就可以在 onFinish()方法里对响应数据进行处理了,类似地,如果出现了异常,就可以在 onError()方法里对异常情况进行处理。如此一来,我们就巧妙地利用回调机制将响应数据成功返回给调用方了。
另外需要注意的是,onFinish()方法和 onError()方法最终还是在子线程中运行的,因此我们不可以在这里执行任何的 UI操作,如果需要根据返回的结果来更新 UI,则仍然要使用异步消息处理机制。