美文网首页
WebSocket和Handler引发的内存泄漏处理

WebSocket和Handler引发的内存泄漏处理

作者: 刺客的幻影 | 来源:发表于2018-08-01 14:48 被阅读0次
    image.png
    基于Android Studio3.1.3和Kotlin开发,使用到Android Profiler和LeakCanary工具

    最新在项目中使用到websocket和服务器交互,发现项目的主要页面存在严重的内存泄漏问题,下面通过示例记录排查、分析的解决步骤:

    • 造成内存泄漏的Activity代码

    class LeakTestActivity : BaseActivity() {
        private lateinit var mSocketClient: WebSocketClient
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_leak_test)
            getData()
        }
    
        val handler = @SuppressLint("HandlerLeak")
        object : Handler() {
            override fun handleMessage(msg: Message?) {
                when (msg?.what) {
                //todo
                }
            }
        }
    
        private fun getData() {
            mSocketClient = object : WebSocketClient(URI(ApiConstants.SOCKET_ADDRESS)) {
                override fun onError(ex: Exception?) {
                }
    
                override fun onMessage(message: String?) {
                }
    
                override fun onMessage(bytes: ByteBuffer?) {
                    super.onMessage(bytes)
                    val msg = handler.obtainMessage()
                    msg.what = 1
                    msg.obj = bytes
                    handler.sendMessage(msg)
                }
    
                override fun onClose(code: Int, reason: String?, remote: Boolean) {
                }
    
                override fun onOpen(handshakedata: ServerHandshake?) {
                }
            }
            mSocketClient.connect()
            if (mSocketClient.isOpen) {
                mSocketClient.send(getUnSubUrl("etcbtc"))
            }
        }
    
    
        override fun onDestroy() {
            super.onDestroy()
            handler.removeCallbacksAndMessages(null)
            mSocketClient.close()
        }
    
    }
    
    • 使用Android Profiler初步分析

    进入被检测的Activity然后退出,点击垃圾桶执行GC操作,继续运行程序,点击向下箭头,可查看内存分配结果: leak_img1.jpeg leak_img2.jpeg
    • 使用LeakCanary精确定位

    leak_img3.png

    从上图可以看到内存泄漏的引用链,由于该类实例被QuotesDetailActivity的getData方法中的0所引用,导致无法回收,而 0就是代表匿名内部类的写法,这里就是指我们创建的WebSocketClient匿名内部类

    • 为什么会发生内存泄漏?

    因为WebSocketClient与网络的交互是一个耗时操作,当页面关闭时操作还没结束,而匿名内部类又默认持有外部类的引用,导致了这个Activity不能被GC掉

    • 如何解决

    使用静态类代替匿名内部类,由于静态类不能直接调用外部类成员,所以构造WebSocketClient对象的时候传入Activity对象的弱引用,具体代码如下:

    class LeakTestActivity : BaseActivity() {
        private lateinit var mSocketClient: WebSocketClient
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_leak_test)
            getData()
    
    
        }
    
        val handler = @SuppressLint("HandlerLeak")
        object : Handler() {
            override fun handleMessage(msg: Message?) {
                when (msg?.what) {
                //todo
                }
            }
        }
    
        private fun getData() {
            mSocketClient = MyWebSocketClient()
            mSocketClient.connect()
            if (mSocketClient.isOpen) {
                mSocketClient.send(getUnSubUrl("etcbtc"))
            }
        }
    
    
        class MyWebSocketClient(activity: LeakTestActivity) :WebSocketClient(URI(ApiConstants.SOCKET_ADDRESS)){
            val weakReference = WeakReference<LeakTestActivity>(activity)
            override fun onError(ex: Exception?) {
            }
    
            override fun onMessage(message: String?) {
                val mActivity = weakReference.get()?:return
                // do sth 调用mActivity的成员
                
            }
    
            override fun onClose(code: Int, reason: String?, remote: Boolean) {
            }
    
            override fun onOpen(handshakedata: ServerHandshake?) {
            }
    
        }
    
        override fun onDestroy() {
            super.onDestroy()
            handler.removeCallbacksAndMessages(null)
            mSocketClient.close()
        }
    
    }
    

    再次使用Android Porfiler和LeakCanary检测,问题得到解决,handler的泄漏情况可用同种方式处理,因为当MessageQueue里的消息未处理完,而handler又持有外部类的引用也会导致页面关闭而内存不能及时回收

    • 总结

    匿名内部类中不要做耗时操作,应该使用静态类和弱引用
    tips : Kotlin中class修饰的类默认为静态类,inner class修饰的类为普通的内部类

    相关文章

      网友评论

          本文标题:WebSocket和Handler引发的内存泄漏处理

          本文链接:https://www.haomeiwen.com/subject/nlqrvftx.html