前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >笔记--解决Android使用retrofit2 OkHttp3短时间内大量通讯提示Socket Failed:EMFILE

笔记--解决Android使用retrofit2 OkHttp3短时间内大量通讯提示Socket Failed:EMFILE

作者头像
Vaccae
发布2022-02-11 08:14:16
8890
发布2022-02-11 08:14:16
举报
文章被收录于专栏:微卡智享

前言

Androidd在做Http请求,使用Retrofit2是一个比较方便的事,这个我一直也用了蛮久了,只不过最近在项目中突然遇到了问题,也是通过分析测试后解决,顺便做一下笔记。

事件回顾

报错图片

当时程序上是做盘点的业务流程,点击盘点的按钮时会同时有35个Http的请求,实际操作时是不停地地点击盘点,多次后出现的这个情况,具体的定位分析里这个操作除了Http请求返回数据后UI显示,基本没啥别的业务逻辑操作,所以问题也比较好定位,应该是同一时间多次请求Http造成的原因。

请求源码

代码语言:javascript
复制
package networking.retrofit;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.ConnectionPool;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;


/**
 * 获取Retrofit类用于Http通信
 */

public class retrofitAPIManager<T> {
    //基本URL地址
    public static String SERVER_URL = "url";
    //Cookies类型  0-每次注册时登记   1-按每次访问的URL登记
    public static int Cookiestype = 0;
    //Cookies类型如果为每次注册登记时用到检索关键前
    public static String Cookiecontains = "login";
    //Cookies类型如果为每次注册登记时用到Key
    public static String CookiesKey = "SumSoft";

    public T provideClientApi(Class<T> tClass) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(SERVER_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(genericClient())
                .build();
        return retrofit.create(tClass);
    }


    //获取OkHttpClient
    public static OkHttpClient genericClient() {
        OkHttpClient httpClient = new OkHttpClient.Builder()
                .cookieJar(new CookieJar() {
                    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();

                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                        //根据类型来判断存入的Cookies用于后面读取用
                        if (Cookiestype == 0) {
                            //判断url里面是注册的更新Key
                            if (url.toString().contains(Cookiecontains)) {
                                cookieStore.put(CookiesKey, cookies);
                            }
                        } else {
                            cookieStore.put(url.toString(), cookies);
                        }

                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {
                        List<Cookie> cookies;
                        if (Cookiestype == 0) {
                            cookies = cookieStore.get(CookiesKey);
                        } else {
                            cookies = cookieStore.get(url.toString());
                        }
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                })
                .build();

        return httpClient;
    }
}

其实这个封装好的源码一直用了很久了,都正常使用,一直使用的Retrofit2+OkHttp3进行网络请求,所以没再深入研究,既然出现问题了,那就研究下有没有相关遇到这个问题的,网上搜了下资料:

从OkHttp的Github源码是可以看到,上面说了,同一台设备在短时间内发起了200次http的请求,就会报这个错误了。

那我们就从OkHttp中来看看分析下,上面代码中使用genericClient创建的OkHttp,用到了new OkHttpClient.Builder(),那我们就分析下这里怎么处理的,OkHttpClient的ConnectionPool网络请求的线程池,在OkHttpClient源码中:

OkHttpClient.java中

代码语言:javascript
复制

    /**
     * Sets the connection pool used to recycle HTTP and HTTPS connections.
     *
     * <p>If unset, a new connection pool will be used.
     */
    public Builder connectionPool(ConnectionPool connectionPool) {
      if (connectionPool == null) throw new NullPointerException("connectionPool == null");
      this.connectionPool = connectionPool;
      return this;
    }

上面可以看到创建的连接线程池,那我们再找这个ConnectionPool。

ConnectionPool.java

代码语言:javascript
复制
  /**
   * Create a new connection pool with tuning parameters appropriate for a single-user application.
   * The tuning parameters in this pool are subject to change in future OkHttp releases. Currently
   * this pool holds up to 5 idle connections which will be evicted after 5 minutes of inactivity.
   */
  public ConnectionPool() {
    this(5, 5, TimeUnit.MINUTES);
  }

  public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
    this.maxIdleConnections = maxIdleConnections;
    this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);

    // Put a floor on the keep alive duration, otherwise cleanup will spin loop.
    if (keepAliveDuration <= 0) {
      throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
    }
  }

ConnectionPool中的构造方法里默认最大线程空闲数是5,keepAlive时间为5分钟。也就是发起一次网络连接后,5分钟内不会断开连接。

这样像刚才我们多次点击盘点的时,短时间内链接不会断开,很快就直接200个了,所以问题应该就出在这里,修改了ConnectPool的连接参数就可以解决这个问题。

解决方法

在创建OkHttpClient.Builder中加入ConnectPool的设置。所以在原来的创建代码中加入了connectPool设置其连接时间,保活时间,我这里设置了500毫秒的时间,如下图

代码语言:javascript
复制
   //获取OkHttpClient
    public static OkHttpClient genericClient() {
        OkHttpClient httpClient = new OkHttpClient.Builder()
                .cookieJar(new CookieJar() {
                    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();

                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                        //根据类型来判断存入的Cookies用于后面读取用
                        if (Cookiestype == 0) {
                            //判断url里面是注册的更新Key
                            if (url.toString().contains(Cookiecontains)) {
                                cookieStore.put(CookiesKey, cookies);
                            }
                        } else {
                            cookieStore.put(url.toString(), cookies);
                        }

                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {
                        List<Cookie> cookies;
                        if (Cookiestype == 0) {
                            cookies = cookieStore.get(CookiesKey);
                        } else {
                            cookies = cookieStore.get(url.toString());
                        }
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                })
                //设置okhttp连接池保活时间,源码中默认是5分钟,这里改为500毫秒。
                .connectionPool(new ConnectionPool(5,
                        500, TimeUnit.MILLISECONDS))
                .build();

        return httpClient;
    }

完整代码

代码语言:javascript
复制
package networking.retrofit;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

import okhttp3.ConnectionPool;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;


/**
 * Created by Administrator on 2016-12-06.
 * 获取Retrofit类用于Http通信
 */

public class retrofitAPIManager<T> {
    //基本URL地址
    public static String SERVER_URL = "url";
    //Cookies类型  0-每次注册时登记   1-按每次访问的URL登记
    public static int Cookiestype = 0;
    //Cookies类型如果为每次注册登记时用到检索关键前
    public static String Cookiecontains = "login";
    //Cookies类型如果为每次注册登记时用到Key
    public static String CookiesKey = "SumSoft";

    public T provideClientApi(Class<T> tClass) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(SERVER_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(genericClient())
                .build();
        return retrofit.create(tClass);
    }


    //获取OkHttpClient
    public static OkHttpClient genericClient() {
        OkHttpClient httpClient = new OkHttpClient.Builder()
                .cookieJar(new CookieJar() {
                    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();

                    @Override
                    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
                        //根据类型来判断存入的Cookies用于后面读取用
                        if (Cookiestype == 0) {
                            //判断url里面是注册的更新Key
                            if (url.toString().contains(Cookiecontains)) {
                                cookieStore.put(CookiesKey, cookies);
                            }
                        } else {
                            cookieStore.put(url.toString(), cookies);
                        }

                    }

                    @Override
                    public List<Cookie> loadForRequest(HttpUrl url) {
                        List<Cookie> cookies;
                        if (Cookiestype == 0) {
                            cookies = cookieStore.get(CookiesKey);
                        } else {
                            cookies = cookieStore.get(url.toString());
                        }
                        return cookies != null ? cookies : new ArrayList<Cookie>();
                    }
                })
                //设置okhttp连接池保活时间,源码中默认是5分钟,这里改为500毫秒。
                .connectionPool(new ConnectionPool(5,
                        500, TimeUnit.MILLISECONDS))
                .build();

        return httpClient;
    }
}

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 事件回顾
    • 报错图片
      • 请求源码
        • 解决方法
          • 完整代码
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档