一、描述
产品是行车记录仪,基于Android5.0 开发的一个APP,将摄像头的预览界面数据编码后上传到网页平台,达到实现监控的一个功能。
二、问题
测试中,不断重复 网页平台请求视频,看到视频后,又关闭的一个压力测试,发现几十次操作以后,APP崩溃。
三、分析
抓了几份log,看到崩溃的地方是预览界面出错引起的,log如下:
D/MtkOmxVenc( 254): [0xf2717800] cc dst: w=1280, h=720, s=1280, uvs=640, f=0x3140868, sec=0
E/libEGL ( 4769): eglMakeCurrent:782 error 300b (EGL_BAD_NATIVE_WINDOW)
D/AndroidRuntime( 4769): Shutting down VM
E/AndroidRuntime( 4769): FATAL EXCEPTION: main
E/AndroidRuntime( 4769): Process: com.example.mycarrecord, PID: 4769
E/AndroidRuntime( 4769): java.lang.RuntimeException: eglMakeCurrent failed
E/AndroidRuntime( 4769): at media.sdk.MediaSurfaceSdk.Opengles.grafika.EglCore.makeCurrent(EglCore.java:279)
E/AndroidRuntime( 4769): at media.sdk.MediaSurfaceSdk.Opengles.grafika.EglSurfaceBase.makeCurrent(EglSurfaceBase.java:119)
E/AndroidRuntime( 4769): at media.sdk.MediaSurfaceSdk.Opengles.KWindowSurfaceDrawer.onFrameAvailable(KWindowSurfaceDrawer.java:134)
E/AndroidRuntime( 4769): at android.graphics.SurfaceTexture$1.handleMessage(SurfaceTexture.java:203)
E/AndroidRuntime( 4769): at android.os.Handler.dispatchMessage(Handler.java:111)
E/AndroidRuntime( 4769): at android.os.Looper.loop(Looper.java:194)
E/AndroidRuntime( 4769): at android.app.ActivityThread.main(ActivityThread.java:5643)
E/AndroidRuntime( 4769): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 4769): at java.lang.reflect.Method.invoke(Method.java:372)
E/AndroidRuntime( 4769): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:982)
E/AndroidRuntime( 4769): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:777)
V/SettingsInterface( 758): from settings cache , name = dropbox:system_app_crash , value = null
很明显报了 opengles 出错。一开始还怀疑是渲染出了问题,但是想想不对啊,opengles是源码,而且发现只有上传的时候才会崩溃,如果设备不上传数据到网页平台,只进行本地编码,放在那一直运行个两周都没出现崩溃问题,
于是把问题定位到网络上传那块。
顺便多看了一下崩溃之前的log,看看能不能有什么明显的地方导致渲染出错,从而引起APP崩溃。 于是发现了如下关键的地方:
E/Fence ( 4769): merge: sync_merge("unnamed-4769-1:1", 993, 1018) returned an error: Too many open files (-24)
E/Parcel ( 4769): writeDupFileDescriptor: error 24 dup fd 1020
E/Parcel ( 254): dup() failed in Parcel::read, i is 0, fds[i] is -1, fd_count is 1, error: Bad file number
E/BufferQueueProducer( 254): [GraphicBufferSource](this:0xf5852000,id:4,api:1,p:4769,c:254) queueBuffer: fence is NULL
W/hwcomposer( 227): [JOB] (0) Timed out waiting for vsync...
E/Surface ( 4769): queueBuffer: error queuing buffer to SurfaceTexture, -22
D/Grafika ( 4769): WARNING: swapBuffers() failed
D/flashlight_drv.cpp( 254): isOn()
把这部分可疑的log全都在CSDN中搜一下,发现这是一个文件句柄的错误。
调试
按照网上的办法,adb shell 进去 cd /proc/pid/fd 下边监控文件句柄情况。
按照网上说法是当这个文件句柄超过1024就会引起APP内存溢出崩溃。
然后进行一次网页视频请求,发现文件句柄就多了一些pipe啥的,然后关闭,又请求,多操作几次过来。
果然发现了文件句柄一直在不停的增多。比如时间为 05 分钟的时候请求一次,生成一些 pipe,关闭然后10分钟的时候又请求,又生成一些 pipe,那如果这样下来多操作几十次,肯定会超过1024崩溃的。
按照正常的逻辑是,05分钟请求视频生成pipe后,关闭视频以后应该会释放,但是并没有释放,05分生成的pipe一直在那里,10分生成的pipe也会一直在那里。
知道这关键的线索以后,就定位代码,到底是哪些代码导致一直在增加pipe的。
因为前面的测试发现是上传引起的崩溃,就去查看了一下上传的代码,代码的大概流程如下:
1、APP收到视频请求的指令。
2、APP连接服务器
3、APP打包编码的数据
4、APP将打包的数据上传
感觉是打包编码数据引起的,因为觉得这块可能耗内存,就暂时把 连接服务器的功能注释掉,注释掉下边的connect函数。
再adb进去查看文件句柄,测试发现网页请求视频的时候,文件句柄竟然不会增加pipe了。
所以问题的原因不是编码打包数据,而是在这一服务器连接connect函数上。
改正
这个连接是用了当今流行的网络框架netty。
想想有点不对。因为这个connect函数每次连接都要先配置一下什么参数啥的,如下:
这样想想不对啊,因为按道理说这些参数应该是初始化的时候配置过一次就OK了,不用每次都配置吧,会不会是每次配置增加pipe,而且又不释放掉呢。
于是做出了如下修改后:
结果给了我很大一个惊喜。
重新测试个200多次都APP都没崩。