Android的OkHttp使用和原理

news2024/9/20 0:55:15

前言

OkHttp的出现代替了HttpUrlConnection,被谷歌官方收纳为底层的网络框架。特点如下:

  • 支持HTTP/2框架下的socket复用
  • 通过连接池减少连接的延时
  • 使用GZIP进行数据压缩
  • 使用缓存技术避免重复请求

当网络出现问题时,OkHttp会静默重新恢复连接,因为是静默的,所以用户无感知。

构建

首先在模块下的build.gradle文件中导入OkHttp依赖,并开启viewBinding:

android {
    ...
    buildFeatures {
        viewBinding = true //开启viewBinding
    }
}

dependencies {
    ...
    implementation 'com.squareup.okhttp3:okhttp:3.14.+'
}

同时在AndroidManifest.xml文件中开启网络访问权限:

<uses-permission android:name="android.permission.INTERNET" />

viewBinding可以去除findViewById的操作。我们在xml中简单的使用一个button开启网络服务:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="测试请求" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

使用viewBinding获取控件,并新建一个OkHttpClient实例:

public class MainActivity extends AppCompatActivity {
    ActivityMainBinding binding;
    OkHttpClient okHttpClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // 也可以使用okHttpClient = new OkHttpClient()来创建
        okHttpClient = new OkHttpClient.Builder().build();

        binding.btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                testGet();
            }
        });
    }

    /**
     * 获取网络数据
     */
    private void testGet() {
        // 创建Request实例,它描述了我们需要请求的内容
        Request request = new Request.Builder()
                // 此处传入一个url数据请求,需要在Manifest文件开通网络权限
                .url("https://api.scryfall.com/cards/search?order=name&q=name%3Dthree+visits")
                .build();

        // 发起网络请求可以是同步也可以是异步的

        // 同步写法,因为网络请求是耗时操作,需要开辟一个新的子线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // newCall是发起网络请求的方法,需要传入Request,即我们对request的描述
                    // 该方法的返回值是response
                    Response response = okHttpClient.newCall(request).execute();
                    String result = response.body().string();
                    // 更新ui需要在主线程
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            binding.textView.setText(result);
                        }
                    });
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();

    }
}

在使用OkHttpClient时,尽量保持单例创建,因为其构造方法中存有很多内容。

在获取网络数据时,我们首先新建一个Request实例,它是用来描述我们需要向网络请求的内容的;其次使用OkHttpClient的newCall方法装在我们的请求,并使用execute方法执行。

执行请求后会返回服务端的结果,类型是Response。此时我们就可以使用请求到的结果了。

异步请求写法如下:

okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {

            }

            @Override
            public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
                String result = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        binding.textView.setText(result);
                    }
                });
            }
        });

双任务队列机制

在使用OkHttpClient的newCall方法时,会返回一个Call类型的实例。我们使用的execute等方法都是在执行Call中的方法,因此我们可以理解Call这个类就是封装了执行业务的方法。

在异步执行的方式中,我们使用了enqueue方法。查看上述的源码,可以其中发现调用了client的dispatcher().enqueue方法,并新建了一个AsyncCall实例传入(对应机制图的第一步)。

AsyncCall继承了NamedRunnable类,而这个类继承了Runnable类,因此我们主要关注AsyncCall是如何重写execute方法的。

在AsyncCall被传入的enqueue方法中,包含了我们的双队列机制的实现,。

promoteAndExcecute方法中出现了双队列。

被判断过滤后的可执行的AsyncCall会被投入一个临时的队列中用于遍历。在遍历时会执行AsyncCalls的excuteOn方法,传入一个线程池。

在这个方法中会执行线程池的execute方法,也就是AsyncCall的run方法。实际上这里做的就是把任务交给了线程池执行。

此处的线程池可以看到没有设置核心线程(corePoolSize = 0),所有的操作都是由临时线程完成。当有新任务进来时,就会创建一个执行周期为60s的临时任务;同时因为没有核心线程,被传入线程池的任务会立即执行。

而在AsyncCall的excute方法中,最后会执行finished方法。

在执行到finished方法时,会有会开始新一轮的promoteAndExecuted方法。这样做我们就达到了从等待队列中循环获取AsyncCall。

如果我们使用的是一个while循环,那么cpu就会一直要为了这个循环释放资源,而不是等待队列中有内容才去执行循环。 

责任链模式和拦截器

拦截器就好比请假审批,你的请假会被人事、主管、部门领导等层层审批,最终回到你的手里。

我们可以实现一个自己的拦截器。

public abstract class Handler {
    // 对于每个handler,它会持有下一个handler(next变量)
    protected Handler next;

    public Handler getNext() {
        return next;
    }

    public void setNext(Handler next) {
        this.next = next;
    }

    // 处理请求的接口方法
    public abstract void handleRequest(String request);
}

 拦截器的具体实现方法如下。

public class MainHandler1 extends Handler {
    @Override
    public void handleRequest(String request) {
        if (request.equals("one")) {
            Log.i("TAG", "具体处理者1处理该请求");
        } else {
            if (getNext() != null) {
                next.handleRequest(request);
            } else {
                Log.i("TAG", "没有人处理请求");
            }
        }
    }
}

可以生成多个MainHandler模拟多个拦截器。

Handler handler1 = new MainHandler1();
Handler handler2 = new MainHandler2();
Handler handler3 = new MainHandler3();
handler1.setNext(handler2);
handler2.setNext(handler3);
handler1.handleRequest("one");

真实的拦截器接口实现如下。

public class LogIntercept implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        // 该层级下的责任链请求request
        Request request = chain.request();
        long curTime = System.currentTimeMillis();
        Log.i("TAG", "intercept: REQUEST = " + request.toString());
        // 该层级下的责任链传递response
        Response response = chain.proceed(request);
        Log.i("TAG", "intercept: RESPONSE = " + response.toString());
        Log.i("TAG", "intercept: 耗时 = " + (System.currentTimeMillis() - curTime) + "ms");
        return response;
    }
}

我们自己定义的拦截器会先于系统的拦截器执行。

所以如果我们的拦截器中没有很好的做到chain.request责任链请求和chain.proceed责任链传递、而导致链路首先在我们自己的拦截器断了的话,整个责任链就会断开,系统的拦截器自然也不会执行。 

连接池的复用机制

TCP的三次握手和四次挥手

在挥手(即断开连接)时,服务器在接收到来自客户端的断连申请(即第一次挥手)时,会反馈两次,一次是表明接收到断连申请,但不会马上断开,因为很有可能服务器中还有客户端的数据在处理;待数据处理完毕后,会再挥手一次,表明自身已处理完毕,可以断开。

Socket连接池复用

考虑到每次连接都要三次握手、每次断开都要四次挥手显然会造成效率低下,Http协议中有一种KeepAlive机制,它可以在数据完成传输(即原本应断连的情况)后仍然保留连接状态。

在这个机制下,会有一个链路的存活时间。当存活时间到达后,该连接才真正断开。

OkHttp默认支持5个并发KeepAlive,链路默认存活时间为5分钟。

真实的连接信息保存在RealConnection中,包含socket等内容。

RealConnectionPool用于存储RealConnection的队列,同时还有对socket的清理机制。

 

若判断条件成立(需要被清理),会执行cleanupRunnable。 

参考

10-OkHttp小结_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2044223.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp组件使用

uni-popup 默认z-index是99 https://uniapp.dcloud.net.cn/component/uniui/uni-popup.html#uni-popup-%E5%BC%B9%E5%87%BA%E5%B1%82 uni-icons uniapp自带图标&#xff1a;https://hellouniapp.dcloud.net.cn/pages/extUI/icons/icons <uni-icons type"left"…

基于JAVA的在线教育系统设计与实现,源码、部署+讲解

摘 要 随着信息化的日益发展&#xff0c;互联网信息技术的发展日新月异。互联网在线教育模式也在不断的被革新。从传统的线下辅导授课&#xff0c;转变成现在的线上教育遍地开花。线上教育已经犹如雨后春笋一般冒芽而出&#xff0c;这为我们的生活带来了许多变动。 基于网络…

江协科技STM32学习笔记(第12章 PWR电源控制)

第12章 PWR电源控制 12.1 PWR电源控制 12.1.1 PWR简介 芯片在3种低功耗模式下&#xff0c;是没法直接再下载程序的。这是因为芯片在睡眠&#xff0c;不会关注调试端口了。解决办法就是&#xff1a;1.按住复位键不动&#xff1b;2.点下载按钮&#xff1b;3.及时从开复位键。这…

怎样使用sudo的时候不需要输入密码?

在Ubuntu等Linux系统下&#xff0c;经常要在个人账户使用sudo命令来执行一些需要root权限的命令&#xff0c;但是需要输入该账户的密码&#xff0c;有时候显得很繁琐&#xff0c; 那么怎样使用sudo的时候不需要输入密码呢&#xff1f; 有如下两种方法&#xff1a; 常规方法1…

颠覆传统 北大新型MoM架构挑战Transformer模型,显著提升计算效率

挑战传统的Transformer模型设计 在深度学习和自然语言处理领域&#xff0c;Transformer模型已经成为一种标准的架构&#xff0c;广泛应用于各种任务中。传统的Transformer模型依赖于一个固定的、按深度排序的层次结构&#xff0c;每一层的输出都作为下一层的输入。这种设计虽然…

Spring Boot集成selenium实现自动化测试

1.什么是selenium&#xff1f; Selenium 是支持web 浏览器自动化的一系列工具和 库的综合项目。 它提供了扩展来模拟用户与浏览器的交互&#xff0c;用于扩展浏览器分配的分发 服务器&#xff0c; 以及用于实现W3C WebDriver 规范 的基础结构&#xff0c; 该规范允许您为所有主…

全国各地认可再+4,美创入选ZJCERT等多省市网络数据安全支撑单位

近一个月以来&#xff0c;美创科技连获多省市认可&#xff0c;相继入选&#xff1a; ZJCERT网络安全应急服务支撑单位 杭州市委网信办网络安全技术服务单位 南通市网络和数据安全技术支撑单位 济南市卫生健康系统网络和数据安全应急技术支撑单位 ZJCERT第三届网络安全应急服…

力扣3148. 矩阵中的最大得分

题目 给你一个由 正整数 组成、大小为 m x n 的矩阵 grid。你可以从矩阵中的任一单元格移动到另一个位于正下方或正右侧的任意单元格&#xff08;不必相邻&#xff09;。从值为 c1 的单元格移动到值为 c2 的单元格的得分为 c2 - c1 。 你可以从 任一 单元格开始&#xff0c;并…

Ubuntu+QT编译QTXlsx库

1.在GitHub上下载QT Xlsx 的源码&#xff0c;网站链接如下&#xff08;需要科学上网&#xff09; https://github.com/dbzhang800/QtXlsxWriter 下载好的内容如下 然后在目录下右击启动终端 输入如下命令 先输入qmake qtxlsx.pro再输入make最后sudo make install 注意&…

医药企业如何选择数字化营销模式

有产品&#xff0c;有市场&#xff0c;便有了窜货这一现象&#xff0c;经销商之间窜货不仅伤害了生产企业的渠道和价格体系&#xff0c;还影响企业的形象&#xff0c;降低了企业品牌的价值。而这一问题的根源就是企业对产品的营销管理信息不对称&#xff0c;而数字化营销被视为…

【常见算法题】斐波那契数列(矩阵快速幂)

一、题目描述 大家都知道斐波那契数列&#xff0c;现在要求输入一个正整数 n &#xff0c;请你输出斐波那契数列的第 n 项。 斐波那契数列满足如下 二、解题思路 2.1 普通处理方式 使用递归直接计算 int fib(int n) {if (n 1 || n 2) return 1;return fib(n - 1) fib(n…

实现信创Linux麦克风摄像头录制(源码,银河麒麟、统信UOS)

随着信创国产化浪潮的来临&#xff0c;在国产操作系统上的应用开发的需求越来越多&#xff0c;其中一个就是需要在银河麒麟或统信UOS上实现录制摄像头视频和麦克风声音&#xff0c;将它们录制成一个mp4文件。那么这个要如何实现了&#xff1f; 一. 技术方案 要完成这些功能&a…

北大研究生公选课资料现已公开,数据库学习秘籍速来get!

为促进基础软件在中国高校的传播&#xff0c;进一步提高在校研究生对基础软件的学习和开发实践能力&#xff0c;拓数派与开源联盟 PG 分会携手合作&#xff0c;走进北京大学&#xff0c;进行了北大软件与微电子学院 2024 年《北京大学 PostgreSQL 内核开发&#xff1a;从入门到…

构建高效沃尔玛自养号测评系统:技术策略与实战指南

搭建沃尔玛自养号测评技术系统是一个涉及多方面技术和资源投入的过程&#xff0c;旨在通过自行构建和掌控测评环境&#xff0c;利用真实国外买家的信息和资料来创建买家账号&#xff0c;模拟真实的购买和评价过程&#xff0c;从而提升商品权重和销量。以下是搭建该系统的主要步…

mysql Ubuntu安装与远程连接配置

一、安装&#xff08;Ubuntu22环境安装mysql8&#xff09; 这里使用Xshell链接Ubuntu和mysql windows进行操作&#xff0c;特别提醒&#xff1a;安装之前建议对Ubuntu快照处理备份&#xff0c;避免安装中出错导致Ubuntu崩溃。 查看是否安装的有可以用指令&#xff1a;ps -ef|…

IOS 05 OC和Swift混合编程

为什么需要使用OC和Swift混合编程&#xff1f; 在真实项目开发过程中&#xff0c;大部分时候我们往往都会使用到OC和Swift混合编程&#xff0c;主要原因如下&#xff1a; 老项目是OC语言实现的&#xff0c;但需要引用Swift的框架&#xff1b;新项目是Swift实现的&#xff0c;…

【操作系统】二、进程管理:1.进程与线程(程序、进程(PCB、状态转换、原语、进程间通信)、线程(多线程模型))

二、进程与线程 文章目录 二、进程与线程1.程序1.1顺序执行的特征1.2并发执行的特征 2.进程Process2.1定义&#xff08;组织&#xff09;2.1.1程序段2.1.2数据段2.1.3进程控制块PCB1&#xff09;内容2&#xff09;作用3&#xff09;进程组织方式 2.2特征2.3进程的状态与转换2.3…

云服务器是什么?云服务器可以用来干什么?

云服务器&#xff0c;顾名思义&#xff0c;是指运行在云计算环境中的虚拟服务器。与传统的物理服务器相比&#xff0c;云服务器不需要用户自行购买、搭建和维护硬件设备&#xff0c;而是通过互联网从云服务提供商处获取计算资源、存储空间和网络服务。用户可以根据自己的需求&a…

spring揭秘05-ApplicationContext

文章目录 【README】【1】ApplicationContext概述【1.1】spring通过Resource对文件抽象【1.2】统一资源加载策略-ResourceLoader【1.2.1】 DefaultResourceLoader【1.2.2】FileSystemResourceLoader【1.2.3】 ResourcePatternResolver批量加载资源【1.2.4】Resource与ResourceL…

使用住宅代理抓取奥运奖牌新闻,全面掌握赛事精彩瞬间

引言 什么是新闻抓取&#xff1f;目的是什么&#xff1f; 新闻抓取有哪些好处&#xff1f; 为什么需要关注奥运奖牌新闻&#xff1f; 如何进行新闻抓取——以Google 新闻为例 总结 引言 近日&#xff0c;巴黎奥运会圆满落幕&#xff0c;在这16天中&#xff0c;全球顶尖运…