栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android开发问题集锦五--OkHttp资源释放与内存泄露

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android开发问题集锦五--OkHttp资源释放与内存泄露

Android开发问题集锦五--OkHttp资源释放与内存泄露
  • 程序之美

程序之美

OkHttp这里就不过多介绍了,在网络框架界可为家喻户晓,很多网络框架都是基于OkHttp封装的,也有很多第三方框架都和OkHttp兼容,在我们做网络通信的时候,OkHttp基本上起着挑大梁的作用。
下面简单说下其调用过程,熟知的朋友都知道,OkHttp调用很简单,只需要以下简单的几步。
1.引入库

在Android Studio工程的build.gradle中加入如下依赖就可以了。

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    implementation 'com.squareup.okhttp3:okhttp:3.8.1'
}

implementation ‘com.squareup.okhttp3:okhttp:3.8.1’ 我选用的是3.8.1版本,朋友们可以选择高一点的版本,应该会优化好一点的。
2.打开网络权限
保证程序可以正常的网络通信,我们需要加入网络权限:

    
    

至于怎么加入网络权限,盆友们可以查看我之前发布的动态权限篇,也可自行百度查找相应的介绍,这里不再啰嗦。

3.接口封装和调用
下面我们就可以着手写代码了,将其封装成函数:

    public static String base_URL = "http://192.168.3.216:80";
    public static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
    private OkHttpClient client = null;

    public void TestLogin(String jsonString) {

        String url = base_URL + "/amd/http/post/url";
        if (client == null){
            client = new OkHttpClient.Builder()
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(60, TimeUnit.SECONDS)
                    .build();
        }

        RequestBody requestBody = RequestBody.create(MEDIA_TYPE, jsonString);

        Request request = new Request.Builder().addHeader("Content-Type", "application/json").url(url).post(requestBody).build();

        Call call = client.newCall(request);

        call.enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e){
                try{
                	String info = "Login:" + " onError:" + e.getMessage();
                	Log.e(TAG, info);
                }catch (Exception e){
                    e.printStackTrace();
                }
                finally {
                    response.close();
                    response.body().close();
                    response.body().source().close();
                    response.body().charStream().close();
                }

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException{
                try{
                    if (response.isSuccessful()) {
                        //This is in the sub-thread, we shuold transport the data to the main thread
                        String responseBody = response.body().string();

                        if (!TextUtils.isEmpty(responseBody))
                        {
                        Log.e(TAG, responseBody);
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
                finally {
                    response.close();
                    response.body().close();
                    response.body().source().close();
                    response.body().charStream().close();
                }
            }

        });
    }

调用方式为:

    	Map paramsMap = new HashMap();
    	paramsMap.put("type", "ANDROID_PHONE");
    	paramsMap.put("deviceid", "123456");
    	paramsMap.put("username", "zhongguoren");
    	paramsMap.put("password", "666999");
    	String jsonString = JSON.toJSonString(paramsMap);
    	TestLogin(jsonString);

这样调用过程就完成了。
我这里主要用了POST方法,有兴趣的朋友也可以尝试下GET,PUT,DELETE,PATCH等方法,这里就不一一叙述了。
下面着重讲一讲调用OkHttp遇到的问题:
1.内存泄露
这个也是我们最容易遇到的问题,很多朋友都苦恼于此,为啥程序运行一段时间就无响应了,或者崩溃了,就是因为这个原因。
主要引起内存泄露的有两个地方:一个是

    client = new OkHttpClient.Builder()
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(60, TimeUnit.SECONDS)
                    .build();

很多朋友都写成局部变量,如下:

                    OkHttpClient client = new OkHttpClient.Builder()
                    .connectTimeout(30, TimeUnit.SECONDS)
                    .readTimeout(60, TimeUnit.SECONDS)
                    .writeTimeout(60, TimeUnit.SECONDS)
                    .build();

有问题么?没问题,控制好久没有问题,如果是这样,那么每次请求结束后我们需要把clent也释放掉。
另外一个地方就是

                    response.close();
                    response.body().close();
                    response.body().source().close();
                    response.body().charStream().close();

每次调用一次之后,不管成功或者失败,都调用一次关闭。保证资源正常释放。
有的朋友会说,我的程序没有调用也没有发现有什么问题,那是和应用的场景有关系的,OkHttpClient有其自身的释放能力,比如资源紧张的时候,他们是可以加入到资源释放的大军里去的,但是他是有周期的。看了下okhttp的框架源码后,你就会发现有一个ConnectionPool类里面有一段初始化线程池的代码。

static {
executor = new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), Util.threadFactory("OkHttp ConnectionPool", true));
}

可以看到,最大的线程数是2147483647,空闲时间是60秒就回收。这个框架要求使用对象不会高并发的调用请求,不会产生多少线程数,且一分钟后就会回收,故而不会有什么问题。
但是如果我们需要高并发操作的时候,就会瞬间积累大量的句柄和链接,来不及释放掉,从而产生泄露。想想,除非你断线重连要一分钟后做,否则就会有挤压,就会泄露。
解决方法:1、OkHttpClient作为全局变量,只创建一次,后面最好用一个client,尽可能少的减少连接数。
2、请求返回要及时释放掉
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-response-body/官方文档中已经详细介绍了相关的释放内容

Response.close()
Response.body().close()
Response.body().source().close()
Response.body().charStream().close()
Response.body().byteStream().close()
Response.body().bytes()
Response.body().string()

其实在实际过程中只需要调用

Response.close()
Response.body().close()

就可以了,除非真正的调用流字节传输,才要加上相应的字节流相关释放。

2.JSON解析的时候防止空指针,定义好协议,严格按照协议进行解析,解析部分加入try{}catch(){}异常捕获字段,保证当前次异常后,下次还能正常运行。
3.回调函数中加入相应的线程池处理,防止出现阻塞。

我相信经过上述的优化,你的网络请求模块一定可以长期运行下去,你获取可以说,目前我不需要,没有业务需求,收藏下,万一需要呢。
很感谢朋友们能在百忙之中听我唠叨,也希望我说的对您能有所帮助。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/337768.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号