androidmemory-leaksleakcanary

received Activity#onDestroy() callback and Activity#mDestroyed is true - Android


How can I resolve this leak?

┬───
│ GC Root: Global variable in native code
│
├─ com.samsung.android.content.clipboard.SemClipboardManager$2 instance
│    Leaking: UNKNOWN
│    Retaining 204.4 kB in 4075 objects
│    Anonymous subclass of android.sec.clipboard.IClipboardDataPasteEvent$Stub
│    ↓ SemClipboardManager$2.this$0
│                            ~~~~~~
├─ com.samsung.android.content.clipboard.SemClipboardManager instance
│    Leaking: UNKNOWN
│    Retaining 204.4 kB in 4074 objects
│    mContext instance of ....presentation.ui.login.LoginActivity with mDestroyed = true
│    ↓ SemClipboardManager.mContext
│                          ~~~~~~~~
╰→ ....presentation.ui.login.LoginActivity instance
​     Leaking: YES (ObjectWatcher was watching this because ....presentation.ui.login.LoginActivity
​     received Activity#onDestroy() callback and Activity#mDestroyed is true)
​     Retaining 202.6 kB in 4027 objects
​     key = e0f29b52-1c73-4933-9856-a987582ccb1d
​     watchDurationMillis = 22643
​     retainedDurationMillis = 17638
​     mApplication instance of ....presentation.App
​     mBase instance of androidx.appcompat.view.ContextThemeWrapper

Here is my code:

@AndroidEntryPoint
class LoginActivity : AppCompatActivity(R.layout.activity_login) {
    private val lessonsViewModel by viewModels<LoginViewModel>()
    private var context: Context? = null
    private var binding: ActivityLoginBinding? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityLoginBinding.inflate(layoutInflater)
        setContentView(binding?.root)
        context = this@LoginActivity
        context?.let {
            SemEmergencyManagerLeakingActivity.applyFix(context!!.applicationContext as Application)
        }
        with(binding!!) {
            btnSignIn.setOnClickListener {

                if (edtEMail.text.isNullOrBlank() || edtEMail.text.isNullOrEmpty()) {
                    txtInputUserName.error = getString(R.string.error_user_name)
                    if (!edtPasswords.text.isNullOrBlank() && !edtPasswords.text.isNullOrEmpty()) {
                        txtInputPassword.error = null
                    }
                } else if (edtPasswords.text.isNullOrBlank() || edtPasswords.text.isNullOrEmpty()) {
                    txtInputPassword.error = getString(R.string.error_user_password)
                    if (!edtEMail.text.isNullOrBlank() && !edtEMail.text.isNullOrEmpty()) {
                        txtInputUserName.error = null
                    }
                } else if (
                    !edtEMail.text.isNullOrBlank() &&
                    !edtEMail.text.isNullOrEmpty() &&
                    !edtPasswords.text.isNullOrBlank() &&
                    !edtPasswords.text.isNullOrEmpty()
                ) {
                    hideBtnSignIn()
                    callAPI(edtEMail.text.toString(), edtPasswords.text.toString())
                }
            }
        }

    }

    private fun callAPI(userName: String, password: String) {
        lifecycleScope.launch {
            val response = lessonsViewModel.getLoginValues(userName, password)
            response.observe(this@LoginActivity) {
                when (it) {
                    is Resource.Success -> {
                        it.data?.let { vl ->
                            hideProgressBar()
                            disableBtnSignIn()
                            lessonsViewModel.saveTokensLogged(
                                TokensDataStore(
                                    vl.token!!,
                                    vl.privatetoken!!
                                )
                            )
                            val intent = Intent(context, MainActivity::class.java)
                            startActivity(intent)
                            finish()
                        }
                    }
                    is Resource.Error -> {
                        it.message?.let { msg ->
                            hideProgressBar()
                            showBtnSignIn()
                            when (msg) {
                                getString(R.string.invalid_login) -> {
                                    Toast.makeText(
                                        context,
                                        R.string.error_user_pass,
                                        Toast.LENGTH_LONG
                                    )
                                        .show()
                                }
                                getString(R.string.service_not_available) -> {
                                    Toast.makeText(
                                        context,
                                        R.string.error_service_not_available,
                                        Toast.LENGTH_LONG
                                    )
                                        .show()
                                }
                                else -> {
                                    Toast.makeText(context, msg, Toast.LENGTH_LONG)
                                        .show()
                                }
                            }
                        }
                    }
                    is Resource.Loading -> {
                        hideBtnSignIn()
                        showProgressBar()
                    }
                }
            }
        }
    }

    private fun showBtnSignIn() {
        binding!!.btnSignIn.visibility = View.VISIBLE
    }

    private fun hideBtnSignIn() {
        binding!!.btnSignIn.visibility = View.INVISIBLE
    }

    private fun disableBtnSignIn() {
        binding!!.btnSignIn.isEnabled = false
    }

    private fun hideProgressBar() {
        binding!!.prLogin.visibility = View.INVISIBLE
    }

    private fun showProgressBar() {
        binding!!.prLogin.visibility = View.VISIBLE
    }

    override fun onDestroy() {
        context = null
        binding = null
        super.onDestroy()
    }
}

Solution

  • Resolved it. I removed it:

    override fun onDestroy() {
        context = null
        binding = null
        super.onDestroy()
    }
    

    And change it to:

    override fun onStop() {
        super.onStop()
        context = null
        binding = null
        finish()
    }
    

    And edit bellow:

    val intent = Intent(context, MainActivity::class.java)
                                startActivity(intent)
                                finish()
    

    To:

    val intent = Intent(context, MainActivity::class.java)
                                startActivity(intent)