畅聊 OkHttp

1、HTTP协议回顾:

01HTTP协议概述

WEB浏览器与WEB服务器之间的一问一答的交互过程必须遵循一定的规则,这个规则就是HTTP协议。

02,HTTP是HyperText Transfer Protocol(超文本传输协议)

它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程及数据本身的格式。

Http协议是一种应用层协议,它通过TCP实现了可靠的数据传输,能够保证数据的完整性、正确性,
而TCP对于数据传输控制的优点也能够体现在Http协议上,使得Http的数据传输吞吐量、效率得到保证

03,请求格式

请求:
	请求行  : 请求方式 请求路径 版本
	请求头  : 以key-value形式组成,K:V。。。
	空行
	请求体  :  用于数据传递:get方式没有请求体(参数地址传递)   post方式有请求体
响应:
	响应行	:版本 响应码 响应信息
	响应头	:以key-value形式组成,K:V。。。
	空行
	响应体	:响应正文

04,常永请求头

Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:64.0) Gecko/20100101 Firefox/64.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cache-Control: max-age=0
Content-Type: text/html
Content-Length:120

05,请求方式

Get:请求获取Request-URI所标识的资源
POST:在Request-URI所标识的资源后附加新的数据
HEAD 请求获取由Request-URI所标识的资源的响应信息报头
PUT:请求服务器存储一个资源,并用Request-URI作为其标识
DELETE:请求服务器删除Request-URI所标识的资源
TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT:保留将来使用
OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项


GET方式(以在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔,通常传送的数据不超过1kb),
通过请求URI得到资源。一般用于获取/查询资源信息

POST方式(在请求的正文内容中向服务器发送数据,传送的数据无限制),
用于向服务器提交新的内容。一般用于更新资源信息

06,状态码

200(正常),
302/307(临时重定向),
304(未修改),
404(找不到),
500(服务器内部错误)

06Http协议的特点

1支持客户/服务器模式。
2简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、 HEAD、POST。每种方法规定了客户与服务器联系的类型不同。 由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。

​ 3灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
​ 4无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求, 并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
​ 5无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。 缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每 次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快

###07Http 1.0 与 Http1.1的区别
1.0协议,客户端与web服务器建立连接后,只能获得一个web资源! 而1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源!

2、网络通信

01、网络三要素

IP:主机的唯一表示  
端口号:正在运行的程序
协议:通信规则,TCP以及UDP

###02、网络模型
ISO模型:
应用层
会话层
表示层
网络层
传输层
数据链路层
物理层

TCP/IP模型:
	应用层
	网际层
	数据层
	物理层

###03、TCP与UDP区别:
TCP:
建立连接
安全可靠协议
以流进行数据传递,无大小限制
三次握手协议

UDP:
	不建立连接
	不可靠协议
	以数据包传递,有大小限制64K

04、Socket以及Http:

Socket:长连接
Http:短连接

05、网络通信中的具体内部细节

URL地址、IP地址:
	http://www.abc.com:80/abc/index.html
	http://202.108.22.5/

①客户端执行网络请求,从URL中解析出服务器的主机名
②将服务器的主机名转换成服务器的IP地址
③从URL中将端口号解析出来
④建立一条客户端与Web服务器的TCP连接
⑤客户端通过输出流向服务器发送一条Http请求
⑥服务器向客户端回送一条Http响应报文
⑦客户端从输入流获取报文
⑧客户端解析报文,关闭连接
⑨客户端将结果显示在UI上

3、异步和同步的区别

同步:就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。也就是必须一件一件事做,等前一件做完了才能做下一件事;

异步:当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者;

同步是阻塞模式,异步是非阻塞模式

同步是指:发送方发出数据后,等接收方发回响应以后才发下一个数据包的通讯方式。 

异步是指:发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式

4、HttpURlconnection发送网络请求:

01,get请求

//子线程中执行请求
URL url = new URL(path);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setConnectTimeout(5000);
if (urlConnection.getResponseCode() == 200){
    InputStream inputStream = urlConnection.getInputStream();
    
}

02,post请求

//子线程中执行请求
URL url = new URL(path);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setConnectTimeout(5000);
urlConnection.setReadTimeout(5000);
String content = "name="+URLEncoder.encode(name)+"&pass="+URLEncoder.encode(pass);//数据编解码
urlConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");//设置请求头
urlConnection.setRequestProperty("Content-Length",content.length()+"");
urlConnection.setDoOutput(true);
OutputStream outputStream = urlConnection.getOutputStream();
outputStream.write(content.getBytes());
if (urlConnection.getResponseCode() == 200){ }

03,json解析:

json解析

bean:
	JSONObject jsonObject = new JSONObject(text);
	String code = jsonObject.getString("resultCode");

array:
	ArrayList<StuClzInfo> list = new ArrayList<>();
	JSONArray array = new JSONArray(text);
    for (int i = 0; i < array.length(); i++) {
            StuClzInfo info = new StuClzInfo();
            JSONObject object = array.getJSONObject(i);
            info.setCode(object.getString("code"));
            info.setCount(object.getInt("count"));
            list.add(info);
    }


bean中带array:
JSONObject object = new JSONObject(text);
String resultCode = object.getString(“resultCode”);
if (“1”.equals(resultCode)) {
tv_name.setText(object.getString(“school”));
JSONArray clazz = object.getJSONArray(“clazz”);
List list = new ArrayList<>();
for (int i = 0; i < clazz.length(); i++) {
JSONObject o = clazz.getJSONObject(i);
StuClzInfo info = new StuClzInfo();
info.setCode(o.getString(“code”));
info.setCount(o.getInt(“count”));
list.add(info);
}
}

gson解析(bean,array,bean中带array)

bean:
	Gson gson = new Gson();
	LoginInfo loginInfo = gson.fromJson(text, LoginInfo.class);//bean
	
array:
	Gson gson = new Gson();
	Type type = new TypeToken<List<StuClzInfo>>() {}.getType();
	List<StuClzInfo> list = gson.fromJson(text, type);//list<bean>
	
bean中带array:
	SchoolInfo info = gson.fromJson(text, SchoolInfo.class);
	list = info.getClazz();//bean.list<bean>

fastjson解析(bean,array,bean中带array)

bean:
	LoginInfo loginInfo1 = JSON.parseObject(text, LoginInfo.class);

array:
	List<StuClzInfo> list = JSON.parseArray(text, StuClzInfo.class);
	
bean中带array:
	SchoolInfo info = JSON.parseObject(text, SchoolInfo.class);
	List<StuClzInfo> list = info.getClazz();

5,OkHttp3讲解

01,OkHttp3简介

	1.支持http和https协议,api相同,易用; 
	2.http使用线程池,https使用多路复用;
	3.okhttp支持同步和异步调用; 
	4.支持普通form和文件上传form; 
	5.操作请求和响应(日志,请求头,body等); 
	6.okhttp可以设置缓存;
	7.支持透明的gzip压缩响应体

02,OkHttp3 配置

	1,Android Studio 配置gradle:
	implementation 'com.squareup.okhttp3:okhttp:3.12.0'
	2,添加网络权限:
	<uses-permission android:name="android.permission.INTERNET"/>

03,OkHttp3 使用思路

get请求思路
    1.获取okHttpClient对象
 	2.构建Request对象
 	3.构建Call对象
 	4.通过Call.enqueue(callback)方法来提交异步请求;execute()方法实现同步请求
post请求思路
    1.获取okHttpClient对象
	2.创建RequestBody
 	3.构建Request对象
 	4.构建Call对象
 	5.通过Call.enqueue(callback)方法来提交异步请求;execute()方法实现同步请求

04,OkHttp3 发送异步请求(GET)

	String url = "http://";

    //第一步获取okHttpClient对象
    OkHttpClient client = new OkHttpClient.Builder()
            .build();
    //第二步构建Request对象
    Request request = new Request.Builder()
            .url(url)
            .get()
            .build();
    //第三步构建Call对象
    Call call = client.newCall(request);
    //第四步:异步get请求
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.i("onFailure", e.getMessage());
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            String result = response.body().string();
            Log.i("result", result);
        }
    });

04,OkHttp3 发送同步请求(GET)

	String url = "http://";

	//第一步获取okHttpClient对象
    OkHttpClient client = new OkHttpClient.Builder()
            .build();
    //第二步构建Request对象
    Request request = new Request.Builder()
            .url(url)
            .get()
            .build();
    //第三步构建Call对象
    final Call call = client.newCall(request);
	//第四步:同步get请求
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Response response = call.execute();//必须子线程执行
                String result = response.body().string();
                Log.i("response", result);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();

05,OkHttp3 发送异步请求(POST)

	//接口参数 String username,String password

    String url = "http://";
    //第一步创建OKHttpClient
    OkHttpClient client = new OkHttpClient.Builder()
            .build();
    //第二步创建RequestBody(Form表达)
    RequestBody body = new FormBody.Builder()
            .add("username", "admin")
            .add("password", "123456")
            .build();
    //第三步创建Rquest
    Request request = new Request.Builder()
            .url(url)
            .post(body)
            .build();
    //第四步创建call回调对象
    final Call call = client.newCall(request);
	//第五步发起请求
    call.enqueue(new Callback() {
        @Override
        public void onFailure(Call call, IOException e) {
            Log.i("onFailure", e.getMessage());
        }

        @Override
        public void onResponse(Call call, Response response) throws IOException {
            String result = response.body().string();
            Log.i("result", result);
        }
    });

05,OkHttp3 发送同步请求(POST)

	//接口参数 String username,String password

    String url = "http://";
    //第一步创建OKHttpClient
    OkHttpClient client = new OkHttpClient.Builder()
            .build();
    //第二步创建RequestBody
    RequestBody body = new FormBody.Builder()
            .add("username", "admin")
            .add("password", "123456")
            .build();
    //第三步创建Rquest
    Request request = new Request.Builder()
            .url(url)
            .post(body)
            .build();
    //第四步创建call回调对象
    final Call call = client.newCall(request);
	//第五步发起请求
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Response response = call.execute();
                String result = response.body().string();
                Log.i("response", result);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();	

06, 请求头处理(Header)以及超时和缓冲处理以及响应处理

    //超时设置
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(5,TimeUnit.SECONDS)
            .readTimeout(5,TimeUnit.SECONDS)
            .writeTimeout(5,TimeUnit.SECONDS)
			.cache(new Cache(cacheDirectory,10*1024*1024))
            .build();

	//表单提交
    RequestBody requestBody = new FormBody.Builder()
            .add("pno", "1")
            .add("ps","50")
            .add("dtype","son")
            .add("key","4a7cf244fd7efbd17ecbf0cb8e4d1c85")
            .build();

	//请求头设置
  	Request request = new Request.Builder()
            .url(url)
			.addHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
			.header("User-Agent", "OkHttp Example")
            .post(body)
            .build();
  
	//响应处理
	@Override
    public void onResponse(Call call, Response response) throws IOException {
        //响应行
		Log.d("ok", response.protocol() + " " +response.code() + " " + response.message());
		//响应头
        Headers headers = response.headers();
        for (int i = 0; i < headers.size(); i++) {
            Log.d("ok", headers.name(i) + ":" + headers.value(i));
        }
		//响应体
        final String string = response.body().string();

        Log.d("ok", "onResponse: " + string);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tx.setText(string);
            }
        });
    }

07, 请求体处理(Form表单,String字符串,流,文件)

	//1.POST方式提交String/JSON    application/json;json串
    MediaType mediaType1 = MediaType.parse("application/x-www-form-urlencoded;charset=utf-8");
    String requestBody = "pno=1&ps=50&dtype=son&key=4a7cf244fd7efbd17ecbf0cb8e4d1c85";
    RequestBody requestBody1 = RequestBody.create(mediaType1, requestBody);

    //POST方式提交JSON:传递JSON同时设置son类型头
    RequestBody requestBodyJson = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), "{}");
    request.addHeader("Content-Type", "application/json")//必须加json类型头
		
	//POST方式提交无参
    RequestBody requestBody1 = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded;charset=utf-8"), "");


    //2.POST方式提交流
    RequestBody requestBody2 = new RequestBody() {
        @Nullable
        @Override
        public MediaType contentType() {
            return MediaType.parse("application/x-www-form-urlencoded;charset=utf-8");
        }

        @Override
        public void writeTo(BufferedSink sink) throws IOException {
            sink.writeUtf8("pno=1&ps=50&dtype=son&key=4a7cf244fd7efbd17ecbf0cb8e4d1c85");
        }
    };

   //3.POST方式提交表单
    RequestBody requestBody4 = new FormBody.Builder()
            .add("pno", "1")
            .add("ps","50")
            .add("dtype","son")
            .add("key","4a7cf244fd7efbd17ecbf0cb8e4d1c85")
            .build();

    //4.POST提交文件
    MediaType mediaType3 = MediaType.parse("text/x-markdown; charset=utf-8");
    File file = new File("test.txt");
    RequestBody requestBody3 = RequestBody.create(mediaType3, file);

    //5.POST方式提交分块请求
    MultipartBody body = new MultipartBody.Builder("AaB03x")
            .setType(MultipartBody.FORM)
            .addPart(
                    Headers.of("Content-Disposition", "form-data; name=\"title\""),
                    RequestBody.create(null, "Square Logo"))
            .addPart(
                    Headers.of("Content-Disposition", "form-data; name=\"image\""),
                    RequestBody.create(MediaType.parse("image/png"), new File("website/static/logo-square.png")))
            .build();

08, 拦截器:网络拦截器,缓存拦截器,日志拦截器,重定向拦截器

一、Application Intercetor和NetworkInterceptor的区别
#1,Application interceptors 应用拦截器
builder.addInterceptor(new LoggingInterceptor())

Application Interceptor 是第一个 Interceptor 因此它会被第一个执行,因此这里的 request 还是最原始的。而对于 response 而言呢,因为整个过程是递归的调用过程,因此它会在 CallServerInterceptor 执行完毕之后才会将 Response 进行返回,因此在 Application Interceptor 这里得到的 response 就是最终的响应,虽然中间有重定向,但是这里只关心最终的 response

1,不需要去关心中发生的重定向和重试操作。因为它处于第一个拦截器,会获取到最终的响应 
2,只会被调用一次,即使这个响应是从缓存中获取的。
3,只关注最原始的请求,不去关系请求的资源是否发生了改变,我只关注最后的 response 结果而已。
4,因为是第一个被执行的拦截器,因此它有权决定了是否要调用其他拦截,也就是 Chain.proceed() 方法是否要被执行。
5,因为是第一个被执行的拦截器,因此它有可以多次调用 Chain.proceed() 方法,其实也就是相当与重新请求的作用了。
2,Network Interceptors 网络拦截器
builder.addNetworkInterceptor(new LoggingInterceptor())

NetwrokInterceptor 处于第 6 个拦截器中,它会经过 RetryAndFollowIntercptor 进行重定向并且也会通过 BridgeInterceptor 进行 request 请求头和 响应 resposne 的处理,因此这里可以得到的是更多的信息。在打印结果可以看到它内部是发生了一次重定向操作,在上图可以看出,为什么 NetworkInterceptor 可以比 Application Interceptor 得到更多的信息了

1,因为 NetworkInterceptor 是排在第 6 个拦截器中,因此可以操作经过 RetryAndFollowup 进行失败重试或者重定向之后得到的resposne。
2,对于从缓存获取的 response 则不会去触发 NetworkInterceptor 。因为响应直接从 CacheInterceptor 返回了。
3,观察数据在网络中的传输。
4,可以获得装载请求的连接。
二、拦截器的应用
1.日志拦截器
在 LoggingInterceptor 中主要做了 3 件事:
1,请求前-打印请求信息;
2,网络请求-递归去调用其他拦截器发生网络请求;
3,网络响应后-打印响应信息

OkHttpClient client = builder
            .addInterceptor(new LoggingInterceptor())//应用拦截器
            .addNetworkInterceptor(new LoggingInterceptor())//网络拦截器
            .readTimeout(5, TimeUnit.SECONDS)
            .connectTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .build();

class LoggingInterceptor implements Interceptor {

    private static final String TAG = "LoggingInterceptor";

    @Override
    public Response intercept(Chain chain) throws IOException {
 
        Request request = chain.request();

		//1.请求前--打印请求信息
        long startTime = System.nanoTime();
        Log.d(TAG, String.format("Sending request %s on %s%n%s",
                request.url(), chain.connection(), request.headers()));

		//2.网络请求
        Response response =  chain.proceed(request);

		//3.网络响应后--打印响应信息
        long endTime = System.nanoTime();
        Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",
                response.request().url(), (endTime - startTime) / 1e6d, response.headers()));

        return response;
    }
}
2.缓存拦截器
在 MyCacheinterceptor 中主要做了(post方式无法缓存)
1,设置缓存位置
2,无网时:设置缓存协议
3,有网:加载网络数据;无网:加载缓存数据

OkHttpClient okHttpClient = new OkHttpClient.Builder()
			.addInterceptor(myCacheinterceptor)//应用拦截器
            .addNetworkInterceptor(myCacheinterceptor)//网络拦截器
            .connectTimeout(5, TimeUnit.SECONDS)
            .cache(new Cache(new File(getCacheDir(), "Cache"), 1024 * 1024 * 10))
            .build();

class MyCacheinterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {

        Request request = chain.request();
   
        if (!isNetworkAvailable(MainActivity.this)) {
            request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();

        }

        Response originalResponse = chain.proceed(request);

        if (isNetworkAvailable(MainActivity.this)) {
            int maxAge = 0;
            return originalResponse.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public ,max-age=" + maxAge)
                    .build();
        } else {
            int maxStale = 15*60;
            return originalResponse.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                    .build();
        }

    }
}

/**
	检测是否有网
 */
public static boolean isNetworkAvailable(Context context) {
    if (context != null) {
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        if (info != null) {
            return info.isAvailable();
        }
    }
    return false;
}

09注意事项

1,推荐让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。

2,response.body().string()只调用一次

3,每一个Call(其实现是RealCall)只能执行一次,否则会报异常

4,子线程加载数据后,主线程刷新数据

10,HttpURLconnection及OkHttp3的对比分析

1,HttpUrlConnection,google官方提供的用来访问网络,但是实现的比较简单,只支持1.0/1.1
2,并没有多路复用,如果碰到app大量网络请求的时候,性能比较差,
3,HttpUrlConnection底层也是用Socket来实现的
4,OkHttp像HttpUrlConnection一样,实现了一个网络连接的过程。
5,OkHttp和HttpUrlConnection是一级的,用socket实现了网络连接,OkHttp更强大,
6,HttpUrlConnection在IO方面用到的是InputStream和OutputStream,但OkHttp用的是sink和source,这两个是在Okio这个开源库里的,	feredSink(支持缓冲)、GzipSink(支持Gzip压缩)、ForwardingSink和InflaterSink(后面这两者服务于GzipSink)
7,多个相同host和port的stream可以共同使用一个socket,而RealConnection就是处理连接的,那也就是说一个RealConnection上可能有很多个Stream
8,OkHttp代码比HttpURLConnection精简的多
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值