android网络编程及协议
网络的基本概念及Http协议
1.网络的基本知识
IP地址和端口号
- IP地址:网络中的每台计算机都必须有一个唯一的IP地址作为标识,用一组由"."分隔的十进制组成。
- 端口号:IP地址只能保证将数据送到指定的计算机,但无法知道交给该主机的哪个网络程序,因此采用端口号标识计算机上正在运行的进程。
- 每个被发送的网络数据包都包含端口号,用于将该数据帧交给具有相同端口号的应用程序处理。
Java的网络编程由java.net包中的类进行处理
IntetAddress类:描述IP地址。
2.网络协议
3.HTTP协议
属于应用层的面向对象的协议,适用于分布式媒体信息系统。
主要特点:
- 支持C/S模式
- 简单快速:只需传送请求方法和路径,请求方法常用的有:GET、HEAD、POST等
- 灵活:允许传输任意类型的数据对象,用Content-Type进行标记
- 无连接:限制每次连接只处理一个请求
- 无状态:对事务处理没有记忆功能
HTTP的URL的格式:
- http://host[:port][/path]
- http表示要通过HTTP协议来定位网络资源;host表示合法的Internet主机或域名或者IP地址;port指定一个端口号,为空则使用默认端口号80;path指定请求资源的URL。
4.HTTP请求报文
由请求行、请求报头、空行和请求数据四个部分组成。
具体介绍:
- 请求行:声明请求方法、主机域名、资源路径和协议版本;
- 请求头:声明客户端、服务器/报文的部分信息;
- 请求体:存放需发送的数据信息。
5.HTTP响应报文
由状态行、消息报头、空行、响应正文组成。
具体介绍
- 状态行:声明协议版本、状态码、状态码描述;
- 响应头:声明客户端、服务器/报文的部分信息;
- 响应体:存放需发送的数据信息。
6.常见的状态码
- 200 OK:客户端请求成功
- 400 Bad Request:客户端请求有语法错误,不能被服务器所理解
- 401 Unauthorized:请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
- 403 Forbidden:服务器收到请求,但是拒绝提供服务
- 404 Not Found:服务器无法根据客户端的请求找到资源
- 500 Internal Server Error:服务器发生不可预期的错误
- 503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常。
7.HTTPS的请求过程
Android的网络编程
1.通信方式
Http通信方式
- HttpURLConnection:Android2.3之后,HttpURLConnection是Android网络编程的最佳选择,它的API简单,体积较小,压缩和缓存机制有效较少网络访问的流量。
- HttpClient(自学):开发团队向开发者建议:在Android2.2版本及以下可以使用HttpClient,在2.3以上版本则应该使用HttpURLConnection;Android6.0直接删除了HttpClient类库。
Socket通信方式
2.Android P的http网络请求的问题
- Android要求默认使用加密连接,禁止App使用任何未加密的连接,因此要使用TLS (Transport Layer Security)传输层安全协议,也就是Https。
- Android P使用HttpUrlConnection进行http的get请求会出现以下异常:w/System.err: java. io. IOException: Cleartext HTTP traffic to **** not permitted.
- 解决方案:推荐使用Https连接;targetSdkVersion降到27以下版本;更改网络安全配置,此更改与网站有关,有时不能得到响应。
- 更改网络安全配置:在res新增xml目录,创建network_security_config.xml,开启http请求:
然后在AndroidManifest.xml中的application标签增加以下属性:android:networkSecurityConfig="@xml/network_security_config"
3.URL类
统一资源定位符(URL): 是对可以从互联网.上得到的资源的位置和访问方法的一种简洁的表示,是互联网.上标准资源的地址。
- 互联网.上的每个文件都有一个唯一的URL
- URL类提供了多个构造器用于创建URL对象
- URL类提供多个方法访问URL对应的资源:URLConnection openConnection():返回一个URLConnect ion对象,它表示到URL 所弓|用的远程对象的连接;InputStream openStream():开此URL的连接,并返回一个用于读取该URL资源的InputStream。
4.Android URL通信
Android HTTP URL接口的基本操作包括:
- 创建URL以及HttpURLConnection对象。HttpURLConnection是Java API的标准接口,含在Java.net包中,继承自URLConnection类;URLConnect ion与HttpURLConnection都是抽象类,无法直接实例化,通过URL的openConnection方法获得对象;InputStream getInputStream():获得输入流,调用此方法后才真正发生连接;outputStream getoutputStream():获得输出流。
- 连接参数设置
- 连接到服务器
- 向服务器写数据
- 从服务器读取数据
HttpURLConnect ion的属性设置
//设置连接超时为15s
connection. setConnectT imeout( 15000 ); .
//设置读取超市为15s
connection. setReadTimeout( 15000);
//设置请求方式,可以为GET或POST
connection . setRequestMethod( method);
//设置Http Header
connection. setRequestProperty( "Connection","Keep-Alive");
//设置模拟浏览器访间
connection . setRequestProperty( " user-agent”,
"Mozilla/5.0 (Windows NT 6.3;
WOW64) AppleWebKit/537 .36
"(KHTML, like Gecko) Chrome/51.0.2704.7 Safari/537.36");
//设置是否从Ht tpURLConnection读入,默认为true
connection . setDoInput(true);
//.设置是否向Ht tpURLConnection输出,默认为false, 对于POST请求需设为true
if("POST" .equals(method)) {
connection. setDo0utput(true);
HttpURLConnect ion访问HTTP资源的步骤:
- 根据URL地址创建URL对象
- 使用URL对象的openConnection( )方法获取HttpURLConnection对象
- 设置连接的属性,包括GET/POST请求方式
- 输入、输出数据
- 关闭输入、输出流
- 在AndroidManifest配置文件中设置访问INTERNET的权限。
GET方法:
URL url = new URL (params[0]);
URLConnection connection = url . openConnection( );
InputStream is = connection. getInputStream( ) ;
InputStreamReader isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr) ;
String line;
while ((line = br.readLine()) != nul1) {
System. out. println(line);
}
br.close();
isr.close();
is.close():
POST方法:
URL url = new URL(params[0]);
HttpURLConnection connection = (HttpURLConnection) url . openConnection();
connection. setDoInput(true);
connection. setDo0utput(true);
connection. setRequestMethod("POST");
OutputStreamWriter oSW = new OutputStreamWriter( connection . get0utputStream(),"utf-8");
BufferedWriter bw = new BufferedWriter(osw);
bw.write( "keyfrom=NiitHt tpGet&key=523317896&type=data&doc type=json&version=1. 1&q=good");
bw.flush();
InputStream is = connection. getInputStream( );
InputStreamReader isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System. out. println(line);
br.close();
isr.close();
is.close();
HttpURLConnection使用的注意事项:
- 使用setConnectTimeout()方法设置连接超时,当网络不好时,Android系统会在超过设置时间后收回资源,中断操作。
- 通过getResponseCode()对响应码进行判断,如果返回的响应码为200,则表示连接成功。
- 在对大文件操作时,要将文件写到SDCard上,不要直接写到手机内存上。
- 操作大文件时,要-边从网络上读取,一边往SDCard上写入,减少手机内存的使用。
- 对文件流操作完毕后要及时关闭。
- Android4.0后所有网络通信的操作都不能在主线程进行,需要使用独立的线程完成。
常用的网络编程框架
1.Android常用的网络编程框架
Volley:
- Google官方推出的一套小而巧的异步请求库,支持HttpClient(Android 6.0之后不再支持)、HttpURLConnection
- 基于网络队列,适合小数据频繁通信,请求线程池默认大小为4
0kHttp:
- 高性能的http库,支持同步步,而且支持http2、websocket协议,api简洁易用,实现了http缓存
- Android网络访问的源码已用0kHt tp代替了HttpURLConnection
Retrofit:
- 基于0kHttp封装的一套RESTful网络请求框架,底层默认采用0kHttp
- 目前网络框架的最好选择: RxJava + Retrofit + 0kHttp
2. OkHttp
配置:
- 0kHttp支持Android2.3及其以上版本,Java要求JDK1.7以上
- 添加依赖:
implementation ' com. squareup. okhttp3:okhttp:4.2.1'
- 添加权限:
<uses-permission android:name=" android. permission. INTERNET"/>
特点:
- 0KHttp是Android版Http喀户端,非常高效,支持SPDY、连接池、GZIP和HTTP缓存。
- 默认情况下,0KHttp会自动处理常见的网络问题,像二次连接、SSL的握手问题。
- 如果应用程序集成了0KHttp,Retrofit默认会使用0KHttp处理其他网络层请求。
- 从Android4.4开始,HttpURLConnection的底层实现采用了okHttp。
OkHttp开发基本思路
- 0kHttp的每次网络请求是一个Request,提供Request必要的参数url、header等,基于Request构造出一个Call对象,再调用它的execute( )方法,就能取得WebServer回复的数据。
- 如果同步调用,需要在独立的线程中执行,使佣异步调用,则采用回调的方式执行,在内部封装了一个请求队列。
- 0kHttp依赖另一个组件oki0完成高性能的I/0操作
- 基本用法:新建一个0kHttpClient对象;通过Request.Builder对象新建一个Request对象;通过Request对象构造Call对象,调用enqueue()以异步的方式将call加入调度队列,等待request执行完成;通过Call对象的Callback对象返回执行结果。
- 一直等待http请求,直到返回响应,请求期间会阻塞进程,因此不能在Android的主线程执行,需要用Android的多线程方式进行处理,否则会报错。
Get同步请求
- 当HTTP响应码位于200到300之间时,认为操作是成功的
- response.body()返回-个ResponseBody对象,封装了HTTP响应的主体数据,它的string()方法将这些数据转换为字符串,另一个byteStream()方法则返回一个InputStream流
- 注意事项:要独立的线程中执行网络操作;对于超过1MB的响应body,应使用流的方式来处理body。
- 一般不适用
Get异步请求
- 在另外的工作线程中执行http请求,请求时不会阻塞当前的线程,所以可以在Android主线程中使用。
- 异步请求需要加入到一个请求队列中,并且要指定回调方法。
Http Header的读写
- 写请求头的时候,使用header(name,value)可以设置唯一的name、value。如果已经有值,旧的将被移除,然后添加新的。
- 使用addHeader(name,value)可以添加多值(添加,不移除已有的)
post异步请求
- 通过RequestBody构建请求数据
private void postForm(String url){
RequestBody formBody = new FormBody.Builder().add("city","南京").build();
Request request = new Request. Builder().url(url).post(formBody).build();
mClient.newCall(request).enqueue(new Callback(){
@Override
public void onResponse(Call call, Response response) throws IOException {
final String str = response .body().string();
Log.d(TAG, str);
runOnUiThread(new Runnable() {
@Override
public void run() {
textView. setText(str);
});
}
@Override
public void onFailure(Call call, IOException e) {
});
}
Post方式提交Multipart文件
- MultipartBuilder可以构建复杂的HTTP Request Body,这与HTML文件上传形式兼容。
- 多块请求体中每块请求都是一 个请求体,可以定义自己的请求Header,例如设定其Content-Disposition。
- 如果Content-Length和Content-Type可用的话,他们会被自动添加到请求Headers中。
图片加载框架Glide
- 添加依赖:
implementation 'com.github.bumptech.glide:glide:4.10.0'
annotationProcessor 'com. github.bumptech.glide:compiler:4.10.0'
//https图片处理
implementation "com.github.bumptech.glide:okhttp3-integration:4.10.0"
- 添加网络权限:
<uses-permission android: name= "android. permi ssion . INTERNET" />
- 使用:
Glide.with(this).load("http://goo.gl/gEgYUd").into(imageView);
- 加载图片的一般方法:
Glide.with(Context context).load(String url).into(ImageView imageView);
Glide加载https图片
- 创建支持https的0kHttpClient对象:
public static 0kHttpClient handleSSLHandshakeBy0kHttp() {
return new 0kHttpClient.Builder()
.connectTimeout(10,TimeUnit. SECONDS)
.writeTimeout(10,TimeUnit. SECONDS)
.readTimeout(10,TimeUnit . SECONDS)
//支持HTTPS请求,跳过证书验i证
.sslSocketFactory(getSSLSocketFactory(),(X509TrustManager) getTrustManager()[0])
.hostnameVerifier(new TrustAllHostnameVerifier())
.build();
}
- 创建继承AppGlidModule类的自定义类,重写registerComponents( )方法:
@GlideModule
public class 0kHttpGlideModule extends AppGlideModule {
@Override
public void registerComponents( @NonNull Context context,
@NonNull Glide glide,@NonNull Registry registry) {
0kHttpClient client = HttpsUtil.handl esSLHandshakeByokHttp();
registry.replace(GlideUrl.class,InputStream.class,
new 0kHttpUrlLoader.Factory(client));
}
}
3.Volley框架
Volley的开发流程
- 创建一个RequestQueue对象
- 创建一个Request对象
- 将Request对象添加到RequestQueue里面
JsonRequest的用法
- JsonRequest是抽象类
- 有两个直接子类:Json0bjectRequest和JsonArrayRequest
ImageRequest的构造方法接收6个参数
- 第1个参数:图片的ur1地址
- 第2个参数:图片请求成功的回调,将图片设置到ImageView
- 第3、4个参数:指定允许的图片最大宽度和高度,如果指定则对图片进行压缩,指定为0则不会压缩
- 第5个参数:指定图片的颜色属性,ARGB_ 8888是展示最好的颜色属性,每个图片像素占4个字节,而RGB_ 565则占2个字节
- 第6个参数:图片请求失败的回调
ImageLoader开发步骤
- 创建RequestQueue对象。
- 创建 ImageLoader对象,ImageLoader的构造函数接收两个参数,第1个参数是RequestQueue对象,第2个参数是ImageCache对象。
- 获取ImageListener对象:通过ImageLoader的getImageListener()方法获取这个对象。getImageListener()方法接收3个参数,第1个参数指定用于显示图片的ImageView控件,第2个参数指定加载图片的过程中显示的图片,第3个参数指定加载图片失败时显示的图片。
- 调用ImageLoader的get()方法加载网络上的图片,get()方法接收2个参数,第1个参数是图片的url地址,第2个参数是ImageListener对象。
ImageCache
- 借助于LruCache进行图片缓存
- LruCache在android-support-v4包提供
- 主要算法:把最近使用的对象用强引用存储在LinkedHashMap中,并把最近最少使用的对象在缓存值达到预设定值之前从内存移除
NetworkImageView
- NetworkImageView是Volley辨的自定义控件,继承自ImageView。
- 开发步骤:创建RequestQueue对象;创建一个ImageLoader对象;在布局文件中添加NetworkImageView控件;在代码中获取该控件的实例;设置要加载的图片地址。
4.Rtrofit
工作原理:
- Retrofit使用一个Java接口来访问一个特定的RESTAPI,在定义接口时,通过各种注解。 (annotation)来定义HTTP请求的各种参数与特性,集成了URL参数替换和查询字符串生成的功能,并能支持文件_上传和基于HTML表单的数据发送。
- Retrofit底层依赖0kHttp实现,Retrofit本质上是对0kHttp的更进一步封装。
编程套路:
- Retrofit将HTTP API转化成了Java接口的形式
- Retrofit类可以针对之前定义的接口生成一个具体实现
- 然后就可以调用生成的对象所提供的方法进行网络的同步或异步访问
Retrofit的开发步骤
- 步骤1:添Retrofit库的依赖:
implementation'com.squareup.retrofit2:retrofit:2.4.0'
implementation'com.google.code.gson:gson:2.8.5'
implementation'com.squareup.retrofit2:converter-gson:2.4.0'
- 步骤2:创建接收服务器返回数据的类
- 步骤3:创建用于描述网络请求的接口
- 步骤4:创建Retrofit实例
- 步骤5:创建网络请求接口实例,并配置网络请求参数
- 步骤6:发送网络请求(异步/同步)
- 步骤7:处理服务器返回的数据
Retrofit使用的注解分为三类:
- http请求类:@GET、@POST、@PUT、@DELETE、@PATH、@HEAD、@OPTIONS、@HTTP
- 标记类:@FormUrlEncoded、@Multipart、@Streaming
- 请求参数类:@Header、@Headers、@URL、@Body、@Path、@Field、@FieldMap、@Part、@PartMap、@Query、@QueryMap
请求头注解: 该类型的注解用于为请求添加请求头。
JSON数据解析
1.JSON概述
概述:
- JSON: JavaScript 0bject Notation,最初是针对JavaScript而设计,能够很方便地在字符串和JavaScript对象中进行转换。后来成为了一种世界通用的数据交换标准,独立于具体的编程语言。
- 与XML相比,JSON语法简明,格式紧凑,易读易懂,数据传输量小,在移动互联时代,这些特点有着巨大的吸引力,因此,JSON成为了 XML的替代者而被广泛应用。
JSON的属性值的类型:
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或false)
- 数组(在方括号中)
- 对象(在花括号中)
- null
JSONObject:
- JSONObject是"Key-Value"的集合。Key:String类型;Value:基本数据类型包装类(String,Boolean,Integer,Long,Double),JSONObject, JSONArray
- JSONObject可以再嵌套JSONObject或JSONArray,构成一个多级嵌套的结构
JSONArray: JSONArray代表一个JSON对象的集合,但其中也可以再放置另一个JSONArray,构成一种多层嵌套的结构。
基本编程套路:
- 当需要创建JSON字符串时,new- 个JSONobject或者JSONArray对象, 使用它们的put系列方法向其中追加数据,根据实际情况组合装配出JSONobject或JSONArray对象。
- 完成对象的装配工作之后,调用JSONObject或JSONArray的toString()方法即可生成JSON字符串。
- 首先区分一下JSON字符串是单个对象还是数组,然后以这个字符串作为参数,new一个JSONObject或者是JSONArray对象,Android内置的JSON解析组件就会解析JSON字符串,自动完成JSONObject或者是JSONArray对象的装配工作。
- 得到JSONObject或者是JSONArray对象引用之后,调用它的get系列方法,就能提取出JSON字符串中特定属性的值。
Java对象数组的序列化:
- JDK中定义有诸多集合类型,Gson对其支持不好,因此,在开发中. 建议只针对数组进行序列化和反序列化。
- FastJSON对集合的解析优于Gson。
- 如果需要反序列化为其他集合类型,请使用JSONObject和JSONArray手动完成,或者使用FastJson。