Android 如何使用应用内更新库防止内存泄漏

Android 如何使用应用内更新库防止内存泄漏,android,performance,memory-leaks,in-app-update,Android,Performance,Memory Leaks,In App Update,我想在我的应用程序中实现新的应用程序内更新库,但我注意到它在重新创建/旋转时会触发我活动中的内存泄漏 以下是我从金丝雀那里得到的唯一细节: 显然,如果我从应用程序内更新库(尤其是addOnSuccessListener)中删除代码,我将一无所获: appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo -> if (appUpdateInfo.updateAvailability() == UpdateAva

我想在我的应用程序中实现新的应用程序内更新库,但我注意到它在重新创建/旋转时会触发我活动中的内存泄漏

以下是我从金丝雀那里得到的唯一细节:

显然,如果我从应用程序内更新库(尤其是addOnSuccessListener)中删除代码,我将一无所获:

appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
        && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
            updateInfo.value = appUpdateInfo
            updateAvailable.value = true
        }else{
            updateInfo.value = null
            updateAvailable.value = false
        }
    }
根据,我首先使用了一些LiveData,但问题是相同的,因此我使用完整的类来处理回调,使用LiveData:

我的服务级别:

class AppUpdateService {

    val updateAvailable: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
    val updateDownloaded: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
    val updateInfo: MutableLiveData<AppUpdateInfo> by lazy { MutableLiveData<AppUpdateInfo>() }

    fun checkForUpdate(appUpdateManager: AppUpdateManager){
        appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
            if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                    && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
                updateInfo.value = appUpdateInfo
                updateAvailable.value = true
            }else{
                updateInfo.value = null
                updateAvailable.value = false
            }
        }
    }

    fun checkUpdateOnResume(appUpdateManager: AppUpdateManager){
        appUpdateManager.appUpdateInfo.addOnSuccessListener {
            updateDownloaded.value = (it.installStatus() == InstallStatus.DOWNLOADED)
        }
    }
}
我真的不明白如何避免内存泄漏,因为appUpdateManager必须使用活动的上下文创建,而且它看起来是导致回调内存泄漏的原因


是否有人已经在没有此问题的情况下实现了它?

使用对上下文的弱引用可能会解决内存泄漏问题。在活动中写下以下内容:

WeakReference<Context> contextWeakReference = new WeakReference<Context>(this);

Context context = contextWeakReference.get();
if (context != null) {
   // Register using context here
}
在顶部:

someData.removeObservers()

这只是一个猜测,但我希望它能有所帮助。

多亏了@Sina Farahzadi,我搜索并尝试了很多东西,发现问题在于任务对象的
appUpdateManager.appUdateInfo
调用

我发现解决内存泄漏的方法是使用applicationContext而不是活动的上下文。我不确定这是不是最好的解决方案,但这是我目前找到的解决方案。我已经导出了服务类中的所有内容,下面是我的代码:

AppUpdateService.kt:

class AppUpdateService : InstallStateUpdatedListener {

    val updateAvailable: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
    val updateDownloaded: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
    val notifyUser: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
    val updateInfo: MutableLiveData<AppUpdateInfo> by lazy { MutableLiveData<AppUpdateInfo>() }

    private var appUpdateManager : AppUpdateManager? = null
    private var appUpdateInfoTask: Task<AppUpdateInfo>? = null

    override fun onStateUpdate(state: InstallState?) {
        notifyUser.value =  (state?.installStatus() == InstallStatus.DOWNLOADED)
    }

    fun setupAppUpdateManager(context: Context){
        appUpdateManager = AppUpdateManagerFactory.create(context)
        appUpdateManager?.registerListener(this)
        checkForUpdate()
    }

    fun onStopCalled(){
        appUpdateManager?.unregisterListener(this)
        appUpdateInfoTask = null
        appUpdateManager = null
    }

    fun checkForUpdate(){
        appUpdateInfoTask = appUpdateManager?.appUpdateInfo
        appUpdateInfoTask?.addOnSuccessListener { appUpdateInfo ->
            if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                    && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
                updateInfo.value = appUpdateInfo
                updateAvailable.value = true
            }else{
                updateInfo.value = null
                updateAvailable.value = false
            }
        }
    }

    fun startUpdate(activity: Activity, code: Int){
        appUpdateManager?.startUpdateFlowForResult(updateInfo.value, AppUpdateType.FLEXIBLE, activity, code)
    }

    fun updateComplete(){
        appUpdateManager?.completeUpdate()
        appUpdateManager?.unregisterListener(this)
    }

    fun checkUpdateOnResume(){
        appUpdateManager?.appUpdateInfo?.addOnSuccessListener {
            updateDownloaded.value = (it.installStatus() == InstallStatus.DOWNLOADED)
        }
    }

}

class AppUpdateService:InstallStateUpdatedListener{
val updateAvailable:MutableLiveData by lazy{MutableLiveData()}
val updateDownloaded:MutableLiveData被惰性{MutableLiveData()}
val notifyUser:MutableLiveData by lazy{MutableLiveData()}
val updateInfo:MutableLiveData by lazy{MutableLiveData()}
私有变量appUpdateManager:appUpdateManager?=null
私有变量AppUpdateInfo任务:任务?=null
覆盖状态更新(状态:InstallState?){
notifyUser.value=(state?.installStatus()==installStatus.download)
}
趣味setupAppUpdateManager(上下文:上下文){
appUpdateManager=appUpdateManager工厂。创建(上下文)
appUpdateManager?.registerListener(此)
checkForUpdate()
}
有趣的名字叫(){
appUpdateManager?.unregisterListener(此)
AppUpdateInfo任务=null
appUpdateManager=null
}
有趣的checkForUpdate(){
appUpdateInfo任务=appUpdateManager?.appUpdateInfo
appUpdateInfo任务?.addOnSuccessListener{appUpdateInfo->
如果(appUpdateInfo.updateAvailability()==updateAvailability.UPDATE\u可用
&&appUpdateInfo.IsUpdateType允许(AppUpdateType.FLEXIBLE)){
updateInfo.value=appUpdateInfo
updateAvailable.value=true
}否则{
updateInfo.value=null
updateAvailable.value=false
}
}
}
乐趣启动日期(活动:活动,代码:Int){
appUpdateManager?.startUpdateFlowForResult(updateInfo.value、AppUpdateType.FLEXIBLE、活动、代码)
}
乐趣更新完成(){
appUpdateManager?.completeUpdate()
appUpdateManager?.unregisterListener(此)
}
有趣的checkUpdateOnResume(){
appUpdateManager?.appUpdateInfo?.addOnSuccessListener{
updateDownloaded.value=(it.installStatus()==installStatus.DOWNLOADED)
}
}
}
主要活动简化:

class MainActivity : BaseActivity(), InstallStateUpdatedListener {

    override fun contentViewID(): Int { return R.layout.activity_main }

    private val UPDATE_REQUEST_CODE = 8000

    private lateinit var appUpdateManager : AppUpdateManager

    private val appUpdateService = AppUpdateService()

    override fun onStateUpdate(state: InstallState?) {
        if(state?.installStatus() == InstallStatus.DOWNLOADED){ notifyUser() }
    }

    // Called in the onCreate()
    override fun setupView(){
        appUpdateManager = AppUpdateManagerFactory.create(this)
        appUpdateManager.registerListener(this)
        setupAppUpdateServiceObservers()
        // Check for Update
        appUpdateService.checkForUpdate(appUpdateManager)
    }

    private fun setupAppUpdateServiceObservers(){
        appUpdateService.updateAvailable.observe(this, Observer {
            if (it)
                requestUpdate(appUpdateService.updateInfo.value)
        })

        appUpdateService.updateDownloaded.observe(this, Observer {
            if (it)
                notifyUser()
        })
    }

    private fun requestUpdate(appUpdateInfo: AppUpdateInfo?){
        appUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.FLEXIBLE, this, UPDATE_REQUEST_CODE)
    }

    private fun notifyUser(){
        showSnackbar(getString(R.string.updated_downloaded), getString(R.string.restart)) {
            appUpdateManager.completeUpdate()
            appUpdateManager.unregisterListener(this)
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == UPDATE_REQUEST_CODE) {
            if (resultCode != RESULT_OK) {
                Timber.d("Update flow failed! Result code: $resultCode")
            }
        }
    }

    override fun onDestroy() {
        appUpdateManager.unregisterListener(this)
        super.onDestroy()
    }

    override fun onResume() {
        super.onResume()
        appUpdateService.checkUpdateOnResume(appUpdateManager)
    }

}
class MainActivity : BaseActivity(){

    override fun contentViewID(): Int { return R.layout.activity_main }

    private val UPDATE_REQUEST_CODE = 8000

    private var appUpdateService: AppUpdateService? = AppUpdateService()

    /**
     * Setup the view of the activity (navigation and menus)
     */
    override fun setupView(){
        val contextWeakReference = WeakReference<Context>(applicationContext)
        contextWeakReference.get()?.let {weakContext ->
            appUpdateService?.setupAppUpdateManager(weakContext)
        }
    }

    private fun setupAppUpdateServiceObservers(){
        appUpdateService?.updateAvailable?.observe(this, Observer {
            if (it)
                requestUpdate()
        })

        appUpdateService?.updateDownloaded?.observe(this, Observer {
            if (it)
                notifyUser()
        })

        appUpdateService?.notifyUser?.observe(this, Observer {
            if (it)
                notifyUser()
        })
    }

    private fun removeAppUpdateServiceObservers(){
        appUpdateService?.updateAvailable?.removeObservers(this)
        appUpdateService?.updateDownloaded?.removeObservers(this)
        appUpdateService?.notifyUser?.removeObservers(this)
    }

    private fun requestUpdate(){
        appUpdateService?.startUpdate(this, UPDATE_REQUEST_CODE)
    }

    private fun notifyUser(){
        showSnackbar(getString(R.string.updated_downloaded), getString(R.string.restart)) {
            appUpdateService?.updateComplete()
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == UPDATE_REQUEST_CODE) {
            if (resultCode != RESULT_OK) {
                Timber.d("Update flow failed! Result code: $resultCode")
            }
        }
    }

    override fun onStop() {
        appUpdateService?.onStopCalled()
        removeAppUpdateServiceObservers()
        appUpdateService = null
        super.onStop()
    }

    override fun onResume() {
        super.onResume()
        setupAppUpdateServiceObservers()
        appUpdateService?.checkUpdateOnResume()
    }

}

class MainActivity:BaseActivity(){
重写有趣的contentViewID():Int{return R.layout.activity_main}
私人val更新请求代码=8000
私有变量appUpdateService:appUpdateService?=appUpdateService()
/**
*设置活动视图(导航和菜单)
*/
覆盖视图(){
val contextWeakReference=WeakReference(applicationContext)
contextWeakReference.get()?.let{weakContext->
appUpdateService?.setupAppUpdateManager(weakContext)
}
}
私人娱乐设置AppUpdateServiceObservators(){
appUpdateService?.updateAvailable?.observe(此,观察者{
如果(它)
请求更新()
})
appUpdateService?updateDownloaded?观察(此,观察者{
如果(它)
notifyUser()
})
appUpdateService?.notifyUser?.observe(此,观察者{
如果(它)
notifyUser()
})
}
私人娱乐RemoveAppUpdateService观察员(){
appUpdateService?.updateAvailable?.removeObservers(此)
appUpdateService?.updateDownloaded?.removeObservers(此)
appUpdateService?.notifyUser?.removeObservers(此)
}
private fun requestUpdate(){
appUpdateService?.startUpdate(此,更新请求代码)
}
私有用户(){
showSnackbar(getString(R.string.updated\u下载)、getString(R.string.restart)){
appUpdateService?.updateComplete()
}
}
重写activityResult(请求代码:Int,结果代码:Int,数据:Intent?){
super.onActivityResult(请求代码、结果代码、数据)
if(requestCode==更新请求代码){
if(resultCode!=结果\u确定){
Timber.d(“更新流失败!结果代码:$resultCode”)
}
}
}
覆盖桌面上的乐趣(){
appUpdateService?.onStopCalled()
removeAppUpdateServiceObservers()
appUpdateService=null
super.onStop()
}
重写onResume(){
super.onResume()
setupAppUpdateServiceObservers()
appUpdateService?.checkUpdateOnResume()
}
}
现在,我将保持这种方式,并继续寻找另一种方式来做到这一点。 如果有人有更好的方法,请告诉我。

使用此帮助器类:

class GoogleUpdater(activity: FragmentActivity) : LifecycleObserver {
    private val appUpdateManager = AppUpdateManagerFactory.create(activity)
    private var installStateUpdatedListener: InstallStateUpdatedListener? = null
    private var wra = WeakReference(activity)
    private val activity get() = wra.get()

    init {
        activity.lifecycle.addObserver(this)
    }

    fun checkUpdate() {
        fun showCompleteUpdateDialog() {
            activity?.let { activity ->
                if (!activity.isFinishing)
                    AlertDialog.Builder(activity)
                            .setTitle(R.string.notification)
                            .setMessage(R.string.restart_to_complete_update)
                            .setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_notification)
                                    ?.apply {
                                        mutate()
                                        alpha = 127
                                    })
                            .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> appUpdateManager.completeUpdate() }
                            .setNegativeButton(R.string.no, null)
                            .create()
                            .apply { setCanceledOnTouchOutside(false) }
                            .show()
            }
        }

        installStateUpdatedListener = object : InstallStateUpdatedListener {
            override fun onStateUpdate(state: InstallState) {
                if (state.installStatus() == InstallStatus.DOWNLOADED)
                    showCompleteUpdateDialog()
                else if (state.installStatus() == InstallStatus.INSTALLED)
                    appUpdateManager.unregisterListener(this)
            }
        }.also { appUpdateManager.registerListener(it) }

        appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
            val clientVersionStalenessDays = appUpdateInfo.clientVersionStalenessDays()
            if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                    && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
                    && clientVersionStalenessDays != null
                    && clientVersionStalenessDays >= DAYS_FOR_FLEXIBLE_UPDATE) {
                try {
                    activity?.let { activity ->
                        if (!activity.isFinishing)
                            appUpdateManager.startUpdateFlowForResult(
                                    appUpdateInfo,
                                    AppUpdateType.FLEXIBLE,
                                    activity,
                                    REQUEST_CODE_APP_UPDATE)
                    }
                } catch (e: SendIntentException) {
                    FirebaseCrashlytics.getInstance().recordException(e)
                }
            } else if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED)
                showCompleteUpdateDialog()
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private fun onStop() {
        installStateUpdatedListener?.let { appUpdateManager.unregisterListener(it) }
    }

    companion object {
        const val REQUEST_CODE_APP_UPDATE = 11
        const val DAYS_FOR_FLEXIBLE_UPDATE = 1
    }
}
在活动中:

GoogleUpdater(this).apply { checkUpdate() }

谢谢你的回答!事实上,我试过你说的,但我仍然有同样的专业
class GoogleUpdater(activity: FragmentActivity) : LifecycleObserver {
    private val appUpdateManager = AppUpdateManagerFactory.create(activity)
    private var installStateUpdatedListener: InstallStateUpdatedListener? = null
    private var wra = WeakReference(activity)
    private val activity get() = wra.get()

    init {
        activity.lifecycle.addObserver(this)
    }

    fun checkUpdate() {
        fun showCompleteUpdateDialog() {
            activity?.let { activity ->
                if (!activity.isFinishing)
                    AlertDialog.Builder(activity)
                            .setTitle(R.string.notification)
                            .setMessage(R.string.restart_to_complete_update)
                            .setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_notification)
                                    ?.apply {
                                        mutate()
                                        alpha = 127
                                    })
                            .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> appUpdateManager.completeUpdate() }
                            .setNegativeButton(R.string.no, null)
                            .create()
                            .apply { setCanceledOnTouchOutside(false) }
                            .show()
            }
        }

        installStateUpdatedListener = object : InstallStateUpdatedListener {
            override fun onStateUpdate(state: InstallState) {
                if (state.installStatus() == InstallStatus.DOWNLOADED)
                    showCompleteUpdateDialog()
                else if (state.installStatus() == InstallStatus.INSTALLED)
                    appUpdateManager.unregisterListener(this)
            }
        }.also { appUpdateManager.registerListener(it) }

        appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
            val clientVersionStalenessDays = appUpdateInfo.clientVersionStalenessDays()
            if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
                    && appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
                    && clientVersionStalenessDays != null
                    && clientVersionStalenessDays >= DAYS_FOR_FLEXIBLE_UPDATE) {
                try {
                    activity?.let { activity ->
                        if (!activity.isFinishing)
                            appUpdateManager.startUpdateFlowForResult(
                                    appUpdateInfo,
                                    AppUpdateType.FLEXIBLE,
                                    activity,
                                    REQUEST_CODE_APP_UPDATE)
                    }
                } catch (e: SendIntentException) {
                    FirebaseCrashlytics.getInstance().recordException(e)
                }
            } else if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED)
                showCompleteUpdateDialog()
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private fun onStop() {
        installStateUpdatedListener?.let { appUpdateManager.unregisterListener(it) }
    }

    companion object {
        const val REQUEST_CODE_APP_UPDATE = 11
        const val DAYS_FOR_FLEXIBLE_UPDATE = 1
    }
}
GoogleUpdater(this).apply { checkUpdate() }