1

I have a registration screen using mobile, when user signs up using mobile number, an otp is send and i listen to that otp so that user don't have to manually feed the otp But when listening to that otp i get an memory leak.

Here is my code for listening to sms

class SmsReceiver : BroadcastReceiver() {

    private var bundle: Bundle? = null
    private lateinit var currentSMS: SmsMessage
    private var otp: String? = null

    override fun onReceive(context: Context?, intent: Intent?) {
        if (intent?.action.equals("android.provider.Telephony.SMS_RECEIVED")) {
            bundle = intent?.extras
            if (bundle != null) {
                val pduObjects = bundle?.get("pdus") as Array<*>
                for (i in pduObjects) {
                    currentSMS = i?.let { getIncomingMessage(it, bundle!!) }!!
                    val sender = currentSMS.displayOriginatingAddress
                    if (sender.endsWith("VK-DICEAP") || sender.endsWith("VM-DICEAP") || sender.endsWith("ID-DICEAP")) {
                        val msgBody = currentSMS.messageBody
                        otp = msgBody.replace(("[a-zA-Z.]").toRegex(), "").trim()
                        mListener.messageReceived(otp ?: "")
                    }
                }
            }
        }
    }



    companion object {
        private lateinit var mListener: SmsListener
        fun bindListener(listener: SmsListener) {
            mListener = listener
        }
    }

    private fun getIncomingMessage(aObject: Any, bundle: Bundle): SmsMessage {
        val currentSMS: SmsMessage
        currentSMS = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val format = bundle.getString("format")
            SmsMessage.createFromPdu(aObject as ByteArray?, format)
        } else {
            SmsMessage.createFromPdu(aObject as ByteArray?)
        }
        return currentSMS
    }

}

Now in my activity i am using the code as follows

in on create i check whether user has granted the permission or not

private fun checkSmsPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (checkSelfPermission(Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED) {
                requestPermissions(arrayOf(Manifest.permission.RECEIVE_SMS), RECEIVE_SMS)
            } else {
                receiveSms()
            }
        } else {
            receiveSms()
        }
    }

If yes then i just send the otp to my server

 private fun receiveSms() {
        SmsReceiver.bindListener(object : SmsListener {
            override fun messageReceived(messageText: String) {
               //send otp to server 
            }
        })
    }

I have created a separate interface just for callback

interface SmsListener {
    fun messageReceived(messageText: String)
}

Now i searched for this on stackoverflow and i saw that instead of registering the receiver in Manifest file , you should register and unregister by code So i tried using code as follows

 override fun onStart() {
        super.onStart()
        smsReceiver = SmsReceiver()
        val filter = IntentFilter()
        filter.addAction("android.provider.Telephony.SMS_RECEIVED")
        registerReceiver(smsReceiver, filter)
        checkSmsPermission()
    }

    override fun onStop() {
        super.onStop()
        unregisterReceiver(smsReceiver)
    }

but i still get leak memory in my app. Iam using LeakCanary for detecting leaks in my app.

Here is a screenshot of the leak

enter image description here

BraveEvidence
  • 53
  • 11
  • 45
  • 119

1 Answers1

1

Here is leaked mListener field in companion object (so it is static). It is common situation of leaking statics, so it is better to not use them at all. If you can you should change your apps architecture.

But you can just create another static method to clear mListener value and call it after activity stop. It is faster and easier.

Ircover
  • 2,406
  • 2
  • 22
  • 42