Android 当使用http2时okhttp2 v3.4版本网络连接超时分析

OKHttp断网情况下的异常逻辑 使用OKHttp网络库时,遇到断网的情况,会报Unable to resolve host "xxx.xxxxx.com":No address associated with hostname错误。正常的DNS解析流程是会先读取本地缓存,那么断网时为什么会报Unable to resolve host的错误呢? 阅读详情

最近遇到了一个奇葩的问题,有个别用户反馈App上的菊花一直在转消失不掉,当时产生了几个猜想:

1、dns解析出问题了
2、服务端有问题
3、哪里超时了
4、哪里死循环了

一开始以为是偶然,结果短短一天内,有好几个用户反馈有这个问题,所以这绝对不是偶然,一定是有一个条件触发了这个bug。

由于我们自己调度了dns,所以一开始我们以为是httpdns的问题,但是通过简单的日志分析,发现其实并不是httpdns的问题,因为httpdns返回的解析结果和localdns的结果是完全一致的。所以基本排除了dns解析出问题了这个可能。

然后开始怀疑服务端有问题,但是通过发布系统查看发布记录,这几天并没有发过,唯一的一个改变就是运维切换了公网出口IP,但是通过httpdns日志已经排除了dns的问题,因此也不是这个问题。

在毫无头绪的时候,我们打了一个日志充足的包,让有问题的用户安装,结果很让人意外,竟然是网络库中的回调没有回来。于是去查看网络库的错误埋点日志,结果更让人意外,该APP一天内有150w的timeout,究竟是什么导致了这么大量的timeout呢?所以一开始直接把锅丢给了timeout,可是后来一想有点不对劲,哪里不对劲呢?

1、我们有两个App,该App一天有150w的timeout, 但是另一个App只有1w的timeout,这两个App有什么不同呢?不同的地方只有okhttp的版本,150w的timeout的App用的okhttp版本是3.4.1,另一个1w的timeout的App用的okhttp版本是3.9.1
2、timeout只会超时,超时时间到了还是会回调到error里,最终菊花会消失,但是现在的情况是回调没有进来。

于是排除了timeout的可能,但timeout的量过大,这绝对也是个问题,至于为什么这么大,以及怎么解决,后面再细说。

由于用户频繁遇到了这个问题,考虑到可能是okhttp的bug,于是将okhttp升到3.9.1重新给用户一个新包,神奇的是用户没有再反馈这个问题了。所以基本断定是okhttp的bug了。

那么问题到底出在哪呢,通过对okhttp的issue的搜索,基本断定是3.4.1该版本的bug,通过查看changelog发现了猫腻

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Version 3.4.2
2016-11-03

Fix: Recover gracefully when an HTTP/2 connection is shutdown.
We had a bug where shutdown HTTP/2 connections were considered usable.
This caused infinite loops when calls attempted to recover.

Version 3.4.1
2016-07-10

Fix a major bug in encoding HTTP headers.
In 3.4.0 and 3.4.0-RC1 OkHttp had an off-by-one bug in our HPACK encoder.
This bug could have caused the wrong headers to be emitted after a sequence of HTTP/2 requests! 
Everyone who is using OkHttp 3.4.0 or 3.4.0-RC1 should upgrade for this bug fix.

官方发布过一个3.4.2的版本,修复okhttp在http2.0下使用了被关闭的连接,导致出现无限死循环。

如Changlog所说,这个问题是修复http2.0的,而我们已经全站https了,并且已经很久了,从App的反馈信息上来看,已经有95+%的请求是Http 2.0的了,基本符合这一条件。所以问题基本定位在了哪里出现了死循环了,最终导致回调没有回来。

问题找到了,那么怎么复现呢?这是个很头疼的问题,在完全抓瞎的情况下,我复现了一个礼拜,结果依旧没有复现出来。在一个偶然的情况下,突然复现出了一个死循环,但是完全是不知道怎么复现出来的,但是可以肯定,肯定是会出现死循环的。

在毫无头绪的情况下,把okhttp相关的issue都浏览了一遍,突然脑洞大开。既然是修复已关闭连接被错误使用,那么只要制造出一个已经建立连接然后将其意外关闭,但是不让okhttp知道它关闭了不就好了,那么怎么复现这个场景呢,其实很简单,如下:

1. 使用http2.0协议跟url建立连接,但是不关闭它
2. 断网
3. 联网
4. 再次向url建立连接,这时候会错误复用已关闭连接
5. 死循环出现

为了验证我的猜想,简单写了一个demo用于复现,我们先来看一段神奇的网络请求代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
final OkHttpClient httpClient = new OkHttpClient.Builder()
       .followRedirects(false) //为了制造非200状态码,禁止302跳转
       .protocols(Collections.unmodifiableList(Arrays.asList(Protocol.HTTP_1_1, Protocol.HTTP_2)))//启用http2.0协议
       .addNetworkInterceptor(new Interceptor() {
           @Override
           public Response intercept(Chain chain) throws IOException {
               Request request = chain.request();

               try {
                   Connection connection = chain.connection();
                   if (connection != null) {
                       Route route = connection.route();
                       //为了判断死循环,打印路由信息
                       Log.e("TAG", "x-route:" + route.socketAddress().toString() + "");
                   }
               } catch (Throwable e) {
                   e.printStackTrace();
               }
               return chain.proceed(request);
           }
       })
       .build();

public String getResult(final String url) {
       Request request = new Request.Builder()
               .url(url)
               .method("GET", null)
               .build();
       ResponseBody responseBody = null;
       String result = null;
       try {
           Response response = httpClient.newCall(request).execute();
           if (response.code() == 200) {
               responseBody = response.body();
               result = responseBody.string();
           }
           Log.e("TAG", "Successful-> protocol:" + response.protocol() + " code:" + response.code());
           return result;
       } catch (Exception e) {
           e.printStackTrace();
           Log.e("TAG", "Unsuccessful-> ", e);
       } finally {
           if (responseBody != null) {
               try {
                   responseBody.close();
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }
       }
       return null;
   }

简单看上面的getResult函数,其功能是向对应url发送一个get请求,并且禁止了302跳转,启用了Http2.0协议,在拦截器中打印了路由信息,在状态码为200的时候,返回了结果,完全没有毛病。其实这个函数存在了一个致命的问题,刚好触发了okhttp的这个bug,那就是在http code!=200的时候,responseBody没有关闭。

让我们一起来复现一下这个问题:

1、 首先开启网络
2、 发送一个http code返回非200的请求,导致流没有被关闭

1
2
3
4
5
6
7
new Thread(new Runnable() {
    @Override
    public void run() {
        //会出现http code 302,导致连接未关闭
        getResult("https://h5.weidian.com/");
    }
}).start();

3、 关闭网络,如进入飞行模式
4、 开启网络,如退出飞行模式
5、 发送一个http code 返回200的请求

1
2
3
4
5
6
7
new Thread(new Runnable() {
    @Override
    public void run() {
        //会出现http code 200
        getResult("https://h5.weidian.com/m/weidian-buyer/error/index.html");
    }
}).start();

6、 开始上演死循环悲剧,如图所示
loop.png

很完美的复现出了这个死循环问题,为什么会产生死循环呢?通过查看okhttp的代码发现,okhttp代码有太多的while(true)了,一不小心就break不了陷入死循环。比如在拦截器里随便抛一个IOException,随随便便死循环,3.3.0之后的版本全部中招,包括最新版3.10.0。

1
2
3
4
5
6
7
//仅供演示,请勿模仿
//.addNetworkInterceptor(new Interceptor() {
//    @Override
//    public Response intercept(Chain chain) throws IOException {
//        throw new IOException("mock io exception");
//    }
//})

在RetryAndFollowUpInterceptor中有这么一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public final class RetryAndFollowUpInterceptor implements Interceptor {
 
  @Override public Response intercept(Chain chain) throws IOException {
    //....省略n行代码
    while (true) {
      
      //....省略n行代码
      Response response = null;
      boolean releaseConnection = true;
      try {
      	//复用已关闭连接,出现IOException("shutdown")
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), true, request)) throw e.getLastConnectException();
        releaseConnection = false;
        continue;
      } catch (IOException e) {
      	//异常被捕获,尝试恢复
        // An attempt to communicate with a server failed. The request may have been sent.
        if (!recover(e, false, request)) throw e;
        //如果不能恢复,抛异常,如果能恢复,进行重试
        //很不幸该场景下,这里一直返回了true
        releaseConnection = false;
        continue;
      } finally {
        // We're throwing an unchecked exception. Release any resources.
        if (releaseConnection) {
          streamAllocation.streamFailed(null);
          streamAllocation.release();
        }
      }
      //省略n行代码
    }
  }

  private boolean recover(IOException e, boolean routeException, Request userRequest) {
    streamAllocation.streamFailed(e);

    // The application layer has forbidden retries.
    if (!client.retryOnConnectionFailure()) return false;

    // We can't send the request body again.
    if (!routeException && userRequest.body() instanceof UnrepeatableRequestBody) return false;

    // This exception is fatal.
    if (!isRecoverable(e, routeException)) return false;

    // No more routes to attempt.
    if (!streamAllocation.hasMoreRoutes()) return false;

    // For failure recovery, use the same route selector with a new connection.
    return true;
  }

  private boolean isRecoverable(IOException e, boolean routeException) {
    // If there was a protocol problem, don't recover.
    if (e instanceof ProtocolException) {
      return false;
    }

    // If there was an interruption don't recover, but if there was a timeout connecting to a route
    // we should try the next route (if there is one).
    if (e instanceof InterruptedIOException) {
      return e instanceof SocketTimeoutException && routeException;
    }

    // Look for known client-side or negotiation errors that are unlikely to be fixed by trying
    // again with a different route.
    if (e instanceof SSLHandshakeException) {
      // If the problem was a CertificateException from the X509TrustManager,
      // do not retry.
      if (e.getCause() instanceof CertificateException) {
        return false;
      }
    }
    if (e instanceof SSLPeerUnverifiedException) {
      // e.g. a certificate pinning error.
      return false;
    }

    // An example of one we might want to retry with a different route is a problem connecting to a
    // proxy and would manifest as a standard IOException. Unless it is one we know we should not
    // retry, we return true and try a new route.
    return true;
  }
}

妈的最外层一个while(true),在执行response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);时发生了IOException(“shutdown”),被捕获,然后调用recover函数尝试恢复连接,但是很不幸的是recover返回了true,而此时连接却已经被意外关闭的(断网再联网),也就是后续将复用一个已关闭的连接,悲剧的导致了无限死循环。不看不知道,一看吓一跳,除了这一处while(true),okhttp其他地方还有很多while(true),这要是一个不小心,就掉坑里了。

问题找到了吗?确切的说是找到了一个问题,还有很多问题没有找到,这里只是找到了一个复现场景,但是不能保证用户的复现路径是怎么样的,但至少已经验证了猜想。

那么要怎么修复这个问题呢?其实非常简单。

1、这个问题只有在Http 2.0下有问题,因为Http 2.0使用多路复用,一个域名不会建立多个connection,所有请求进行多路复用使用同一个connection,因此解决这个无限死循环的一个方法就是禁用Http 2.0,但显然是无法接受的,所以不予考虑,不过也给出OkHttp禁用http 2.0的方式

1
2
3
OkHttpClient httpClient = new OkHttpClient.Builder()
            .protocols(Collections.unmodifiableList(Arrays.asList(Protocol.HTTP_1_1)))//启用http1.1,禁用http 2.0
            .build();

2、okhttp在连接失败时会进行重试,从而导致了这个无限重试的悲剧,因此可以关闭失败重试的功能,但是这会降低请求成功率,也是不能接受的,所以也不予考虑,不过也给出对应代码。

1
2
3
OkHttpClient httpClient = new OkHttpClient.Builder()
            .retryOnConnectionFailure(false)
            .build()

3、经过测试,发现以上的无限死循环问题在okhttp [3.3.0,3.5.0]全部中招,因此可以使用3.2.0版本或者使用最新的3.10.0版本,可以从一定程度上杜绝这个问题,但是也不能保证一定不会再出现死循环,也许还有其他触发条件呢,谁也不能保证。所以升级okhttp是必要的,可以考虑。

4、既然http code!=200时流未关闭的情况下,连接意外中断触发了这个死循环,那么避免问题出现的简单有效的途径就是我管你http code为多少,统统关闭就好了,对应修改后的getResult函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public String getResult(final String url) {
       Request request = new Request.Builder()
               .url(url)
               .method("GET", null)
               .build();
       Response response = null;
       String result = null;
       try {
           response = httpClient.newCall(request).execute();
           if (response.code() == 200) {
               ResponseBody responseBody = response.body();
               result = responseBody.string();
           }
           Log.e("TAG", "Successful-> protocol:" + response.protocol() + " code:" + response.code());
           return result;
       } catch (Exception e) {
           e.printStackTrace();
           Log.e("TAG", "Unsuccessful-> ", e);
       } finally {
           if (response != null) {
               try {
                   response.close();
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }
       }
       return null;
   }

但是这只是避免了流没有关闭触发的死循环,避免不了其他条件触发的死循环,需要特别注意这一点。

bug差不多修完了,还剩下一个问题待解决,150w的timeout是从哪里来的?这个问题我没办法在现有手头上的真机复现,但是我在genymotion模拟器上复现了出来,所有我猜想必然还会存在一个机型复现出这个问题,只是目前手头上的机型没有这个问题,而这个问题的前提条件也必须是Http 2.0,复现方式也是非常简单:

1、联网
2、发送一个Http2.0请求建立连接,使用完后关闭连接(Http 2.0下会发送一个帧)
3、断网
4、联网
5、发送Http2.0请求,超时时间到了后,会触发timeout
6、之后发送n个请求,都不会成功,直到连接复用池中已关闭的连接被清理掉(默认5分钟,5分钟后才会有成功的请求产生)

这个问题的解决方法也比较简单:

1、升级okhttp到最新3.10.0的版本,高版本对复用池中的连接做了严格的ping pong的验证,一定程度上可以保证被使用的连接是没有被关闭的,即使被关闭,也可以通过重试重新建立一个新的连接。

2、捕捉SocketTimeoutException异常,清理连接池中无用连接,这个代码中稍微处理一下就好了,对应的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
okhttp3.Response response = null;
try {
    response = call.execute();
    //read response content
} catch (SocketTimeoutException e) {
    e.printStackTrace();
    try {
        okhttpclient.connectionPool().evictAll();
    } catch (Exception e1) {
        e1.printStackTrace();
    }
    throw e;
} catch (IOException e) {
    throw e;
} finally {
    if(response != null){
        try {
            response.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

除了这个timeout异常之外,还有一个异常也特别多,就是UnknownHostException,这个问题也可以用timeout的复现步骤复现出来,因此这个异常发生时,也最好清理一下连接池中的无用连接。

1
java.net.UnknownHostException: Unable to resolve host "your.domain.com": No address associated with hostname

优化后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
okhttp3.Response response = null;
try {
    response = call.execute();
    //read response content
} catch (SocketTimeoutException e) {
    e.printStackTrace();
    try {
        okhttpclient.connectionPool().evictAll();
    } catch (Exception e1) {
        e1.printStackTrace();
    }
    throw e;
} catch (UnknownHostException e) {
    e.printStackTrace();
    try {
        okhttpclient.connectionPool().evictAll();
    } catch (Exception e1) {
        e1.printStackTrace();
    }
    throw e;
} catch (IOException e) {
    throw e;
} finally {
    if(response != null){
        try {
            response.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

排查了一个礼拜,最终总算把问题给解决了,也算是一身轻松了。

转载自:https://fucknmb.com/2018/04/16/%E5%BD%93OkHttp%E9%81%87%E4%B8%8AHttp-2-0/

Android SSL Pinning绕过:Xposed+JustTrustMe实战指南 SSL/TLS证书验证是移动应用安全的基础机制,其中SSL Pinning技术通过固定可信证书来防御中间人攻击。在安全测试场景下,需要动态绕过这种保护机制进行漏洞检测。Xposed框架通过Hook系统API实现运行时修改,配合JustTrustMe模块可快速禁用证书验证逻辑。这种组合方案相比反编译或Frida注入更高效稳定,适用于金融APP等高安全需求场景的授权测试。实施时需注意Android版本兼容性和测试合规性,典型工具链包括BurpSuite代理和ADB调试工具。 阅读详情

相关推荐

遥感数据下载——FIRM:VIIRS火点数据、MODIS火点数据简介及下载

一、FIRMS(Fire Information for Resoutce Management System) 网站链接:https://firms.modaps.eosdis.nasa.gov/map/ 这个网站上对VIIRS及MODIS的火灾数据进行了显示,可以进行按日期、产品类别等动态的展示火点数据或火烧迹地,同时也提供了数据下载控件。高级选择中还可以进行数据统计结果展示。 二、VIIR...

knkn123的博客 3万+

OKHTTP 超时时间

okhttp3.OkHttpClient.Builder clientBuilder = new okhttp3.OkHttpClient.Builder(); //读取超时 clientBuilder.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS); //连接超时 clientBuilder.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECOND...

vistaup的博客 1万+

别再到处找包了!Keil5芯片支持包(Pack)最全管理指南:安装、更新、迁移与离线备份

本文详细解析了Keil5芯片支持包(Pack)的全生命周期管理,包括安装、更新、迁移与离线备份的工程化解决方案。针对STM32F10x等常见芯片,提供了高效获取Pack的策略、版本管理技巧及团队协作方案,帮助开发者解决常见的'Device Family Pack missing'等问题,提升嵌入式开发效率。

weixin_28729205的博客 541

Android 使用DevRing、okhttp3请求网络,总是报错连接超时,问题解决

近日,同事出现一个问题,使用DevRing方式请求接口时,总是报java.net.SocketTimeoutException: timeout异常,连接超时问题

Kstar_Ming的博客 4020

从零构建智能ai语音助手:ESP32s3+Python+大语言模型实战指南

本文详细介绍了基于ESP32S3硬件和Python后端的智能语音助手系统开发指南。系统采用ESP32S3采集语音,通过WebSocket实时传输到Python后端处理,利用大语言模型实现自然语言理解和意图识别,再通过TTS合成语音返回。文章从系统架构、工作流程、技术实现(包括OPUS编码、WebSocket通信、ESP32音频处理)等方面进行了详细讲解,并提供了分阶段学习路线和常见问题解决方案。该项目展示了物联网技术与AI技术的融合应用,相比传统语音系统具有更高的灵活性和智能性,为开发者提供了完整的实现方案

Despacito0o的博客 2630

Centos7.6操作系统安装+Bond配置(600GB磁盘版本)

1.安装前准备工作 准备centos7.6操作系统镜像 CentOS-7-x86_64-DVD-1810.iso 服务器安装一般都是在BMC界面操作,所以安装前需要在BMC界面挂载好操作系统镜像 重启服务器后自动进入安装界面。 华为大数据集群安装操作系统配置

小时光 6010

图文详解OkHttp的超时时间

虽然网上有很多关于okhttp超时时间的文章但大多都一笔带过并没有进行详细的讲解各自的作用,于是就看了下源码大致写一下其中的发现.本文以 'com.squareup.okhttp3:okhttp:3.12.0'源码为参考首先我们一共可以设置5个超时时间分别如下:.build();其中callTimeout,readTimeout,writeTimeout和okio的AsyncTimeout有着密不可分的关系,其内部维护了一个Watchdog,单独开一个线程死循环判断是否超时。

chuyouyinghe的专栏 7176

airodump-ng、aircrack-ng使用教程

wifi破解、airodump-ng、aircrack-ng脚本

ssmbyss的博客 2233

okhttpclient请求超时

2.当请求成功后,但实际使用还是请求超时时查看请求时间,当时间超过 okhttpclient 的默认请求时间 10s 时,就测试修改 okhttpclient 的请求超时时间。//设置connectTimeout、readTimeout、writeTimeout超时时间。1.判断是否为请求参数的问题,如果没有问题,使用postman测试请求。

Tchat的博客 2626

STM32串口通信实战:从CubeMX配置到printf重定向全解析

串口通信是嵌入式系统中最基础且广泛使用的数据交换方式,它基于异步串行通信原理,通过TX(发送)和RX(接收)两根信号线实现设备间的全双工通信。其技术价值在于协议简单、硬件成本低、可靠性高,是单片机与上位机调试、传感器数据采集、设备控制等场景的首选方案。在STM32等ARM Cortex-M系列MCU中,USART外设通过硬件自动处理时序,大大减轻了CPU负担。本文聚焦于嵌入式竞赛与开发中的高频需求,以STM32G431RBT6为例,详解通过CubeMX配置USART1、实现按键触发数据发送、并完成print

weixin_30603633的博客 548

利用大规模生物活性挖掘,计算识别针对癌基因驱动蛋白 EGFR、BRAF 与 CDK4 的高亲和力抑制剂

背景:精准癌症治疗需要小分子抑制剂与致瘤蛋白靶点之间的精确匹配。方法:我们使用 Python 及 ChEMBL 数据库 API 开发了一套计算流程,系统性地筛选针对 EGFR、BRAF 与 CDK4 的高亲和力配体。对 IC50 < 1000 nM 的生物活性进行过滤,并检索候选化合物的分子结构(SMILES)以供后续优化。结果:本研究共识别出 [插入总数] 种独特化合物。其中,对 BRAF 激酶而言,我们挑选了一批 IC50 低至 6.0 nM 的高效实验性化合物。

u012133341的博客 96

Android之okhttp_v3.4版本网络连接超时问题解决方案

APP上的loading框一直在转消失不掉,当时产生了几个猜想: 1、DNS解析出问题了 2、服务端有问题 3、哪里超时了 4、哪里死循环了 一开始以为是偶然,结果短短一天内,有好几个用户反馈有这个问题,所以这绝对不是偶然,一定是有一个条件触发了这个bug。 由于我们自己调度了dns,所以一开始我们以为是httpdns的问题,但是通过简单的日志分析,发现其实并不是httpdns的问题,因为httpdns返回的解析结果和localdns的结果是完全一致的。所以基本排除了dns解析出问题了这个可能。 然

垂天云的专栏 7426

Allegro基本规则设置指导书之Spacing Region

当网络需要在Region中需要按照自身的规则匹配时,可以创建Region Class来匹配。Region也支持Class to Class规则,点击ok即可。Allegro基本规则设置指导书之Spacing Region。下面介绍基本规则设置指导书之Spacing Region。给创建好的Region 匹配一个规则。空白的地方创建一个Region。

weixin_54787054的博客 564

android okhttp异步请求使用详解

okhttp一、OKHttp简介okhttp导入okhttp主要功能 一、OKHttp简介 OkHttp 是一款高效的 HTTP 客户端,内部依赖okio,它有以下优点: HTTP/2 支持允许对同一主机的所有请求共享同一个socket。 连接池可减少请求延迟(如果 HTTP/2 不可用)。 透明 GZIP 可缩小下载大小。 响应缓存可完全避免重复请求的网络。 okhttp导入 okhttp官网地址 OkHttp 3.12.x 及以下的版本支持 Android 2.3+(API 级别 9+)和 Java

fairy0000000的博客 1307

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

android okhttp异步请求使用详解 (2)

okhttp一、OKHttp简介okhttp导入okhttp主要功能二、okhttp基本使用1.发起get异步请求2.发起post提交请求总结 一、OKHttp简介 OkHttp 是一款高效的 HTTP 客户端,内部依赖okio,它有以下优点: HTTP/2 支持允许对同一主机的所有请求共享同一个socket。 连接池可减少请求延迟(如果 HTTP/2 不可用)。 透明 GZIP 可缩小下载大小。 响应缓存可完全避免重复请求的网络。 okhttp导入 okhttp官网地址 OkHttp 3.12.x 及

fjnu_se的博客 633

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Android 网络框架(Android-async-http,OKHttp,retrofit,volley,xUtils,Afinal)

目前常用的网络框架,当然呢,每种网络框架都会有其优点和缺点的,主要是根据我们自身的项目进行选择:1.Android-async-http Android-async-http 是一个强大的网络请求库,这个网络请求库是基于 Apache HttpClient 库之上的一个异步网络请求处理库,网络处理均基于 Android 的非 UI 线程,通过回调方法处理请求结果。 android-async-

qq_37278353的博客 1901

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

android okhttp jar包,Android OkHttp

OkHttp基本用法URL和Uri所属的包不同。URI位置在java.net.URI,显然是Java提供的一个类。而Uri位置在android.net.Uri,是由Android提供的一个类。所以初步可以判断,Uri是URI的“扩展”以适应Android系统的需要。作用的不同。URI类代表了一个URI(这个URI不是类,而是其本来的意义:通用资源标志符——Uniform Resource Iden...

weixin_34561373的博客 1335

Android常用的网络框架介绍和使用

一、选择开源框架的原则1.学习的成本:对该框架学习的时间长短,文档是否齐全的考虑 2.流行的程度:该开源框架是否流行,github 上 start 的个数,都是我们考量的标准 3.是否还在维护:如果该框架没人维护了,随着技术的不断更新,都会出现大大小小的问题的 4.代码的体积: 体积当然不能太大了 5.代码的设计: 整体框架的设计二、Android 常用的网络框架 大多数应用程序基本都需要连接网络,发送一些数据给服务端,或者从服务端获取一些数据。通常在 Android 中进行网络连接一般使用 Sco

yzwfeng的专栏 6300

Android网络请求开发指南:权限配置与HTTP客户端选型

网络请求是移动应用开发的核心技术之一,特别是在Android平台上,需要处理权限声明、网络安全配置等特有机制。HTTP协议作为应用层通信基础,通过客户端-服务器模型实现数据交换。Android提供了多种网络请求实现方式,从原生的HttpsURLConnection到主流的OkHttp和Retrofit,开发者需要根据项目需求选择合适方案。OkHttp凭借连接池、GZIP压缩等优化成为行业标准,而Retrofit则通过接口抽象简化了REST API调用。在实际开发中,还需要考虑网络状态检测、缓存策略以及网络安

weixin_30598225的博客 387

使用okHttp、HttpURLConnection 获取网络连接 并使用gson解析数据

获取网络工具类: package com.djp.magpietest.json; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; impo...

这个男孩不简单的博客 416

android okhttp异步请求使用详解 (2),太牛了

HTTP/2 支持允许对同一主机的所有请求共享同一个socket。 连接池可减少请求延迟(如果 HTTP/2 不可用)。 透明 GZIP 可缩小下载大小。 响应缓存可完全避免重复请求的网络。 okhttp导入 okhttp官网地址 OkHttp 3.12.x 及以下的版本支持 Android 2.3+(API 级别 9+)和 Java 7+。同时还要导入okhttp依赖的okio库,而Okio在1.x版本是基于Java实现的,2.x则是Kotlin实现的。这里我的Android版本是2..

m0_64383081的博客 362

Android网络连接和网络数据处理

Android网络连接和网络数据处理HTTP请求方式GET与POSTAndroid网络请求 HTTP请求方式GET与POST 在Android开发中我们会接触到数据的交互,比如写入数据、读取数据等,因此我们需要使用通讯间的协议来进行请求,最常见的协议是HTTP协议,而GET和POST则是HTTP协议中最常用的两种请求方式。 HTTP协议 HTTP(HyperText Transfer Protocol超文本传输协议)是一个设计来使客户端和服务器顺利进行通讯的协议。 HTTP在客户端和服务器之间以reque

weixin_45715286的博客 1251

【愚公系列】《移动端AI应用开发》022-Android端DeepSeek集成实战(数据传输与接口调用)

本文详细介绍了在Android端集成DeepSeek大模型的关键技术,重点围绕数据传输与接口调用的实现方法展开。文章分为两部分核心技术讲解: JSON结构体与API响应解析 阐述了JSON作为API响应标准格式的优势 通过示例演示了使用Gson库实现JSON到Java对象的反序列化 展示了嵌套JSON结构的处理方法,定义对应数据类实现自动映射 网络连接池与异步回调机制 分析了连接池技术在复用网络连接、提升性能方面的作用 详细说明了异步回调避免主线程阻塞的原理 提供OkHttp实现代码示例,包含连接池配置和异

愚公智库 1万+

OkHttp的使用

一、OkHttp发起Get请求网络数据 直接上代码: package com.canary.okhttptest; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.io.IOException; impor

Giousa的博客 1040

Android Okhttp缓存:Cache,创建OkHttpClient实现(一)

Android Okhttp缓存Cache,创建OkHttpClient实现Android Okhttp使用缓存通过Cache实现。在创建OkHttpClient实现。构造Cache需要传递一个缓存文件目录已经缓存的大小尺寸。下面演示一个简单功能,加载一张图片到一个ImageView,如果该连接下图片已经存在,那么即便在断网时候,也能使用缓存加载出来。package zhangphil.app;

Zhang Phil 4312
上一篇: Mac 下adb端口占用及USB商品无法被创建问题解决
下一篇: 安卓隐藏及恢复状态栏和虚拟按键
VIPqiangqiang
博客等级 码龄14年 7粉丝 83原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值