From d6329a1ab688c1ec0bbf11f90411c41d3c359ec4 Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 8 Jun 2020 19:46:55 +0200 Subject: [PATCH] Start looking at memory leaks --- vector/build.gradle | 2 + .../core/epoxy/LayoutManagerStateRestorer.kt | 11 +++-- .../riotx/features/home/HomeActivity.kt | 7 +++- .../UnknownDeviceDetectorSharedViewModel.kt | 41 +++++++++++++------ .../home/room/list/RoomListFragment.kt | 4 +- .../RoomMemberProfileFragment.kt | 3 +- .../roomprofile/RoomProfileFragment.kt | 3 +- 7 files changed, 51 insertions(+), 20 deletions(-) diff --git a/vector/build.gradle b/vector/build.gradle index 1eac3106ea..628aa961c7 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -396,6 +396,8 @@ dependencies { // Plant Timber tree for test testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1' + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3' + androidTestImplementation 'androidx.test:core:1.2.0' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test:rules:1.2.0' diff --git a/vector/src/main/java/im/vector/riotx/core/epoxy/LayoutManagerStateRestorer.kt b/vector/src/main/java/im/vector/riotx/core/epoxy/LayoutManagerStateRestorer.kt index 3594bd23ff..44db760bf7 100644 --- a/vector/src/main/java/im/vector/riotx/core/epoxy/LayoutManagerStateRestorer.kt +++ b/vector/src/main/java/im/vector/riotx/core/epoxy/LayoutManagerStateRestorer.kt @@ -25,12 +25,17 @@ import java.util.concurrent.atomic.AtomicReference private const val LAYOUT_MANAGER_STATE = "LAYOUT_MANAGER_STATE" -class LayoutManagerStateRestorer(private val layoutManager: RecyclerView.LayoutManager) : Restorable, DefaultListUpdateCallback { +class LayoutManagerStateRestorer(layoutManager: RecyclerView.LayoutManager) : Restorable, DefaultListUpdateCallback { + var layoutManager: RecyclerView.LayoutManager? = null private var layoutManagerState = AtomicReference() + init { + this.layoutManager = layoutManager + } + override fun onSaveInstanceState(outState: Bundle) { - val layoutManagerState = layoutManager.onSaveInstanceState() + val layoutManagerState = layoutManager?.onSaveInstanceState() outState.putParcelable(LAYOUT_MANAGER_STATE, layoutManagerState) } @@ -41,7 +46,7 @@ class LayoutManagerStateRestorer(private val layoutManager: RecyclerView.LayoutM override fun onInserted(position: Int, count: Int) { layoutManagerState.getAndSet(null)?.also { - layoutManager.onRestoreInstanceState(it) + layoutManager?.onRestoreInstanceState(it) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt index b6e3cbcd76..97ac5f933b 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt @@ -55,7 +55,7 @@ import kotlinx.android.synthetic.main.merge_overlay_waiting_view.* import timber.log.Timber import javax.inject.Inject -class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { +class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory { private lateinit var sharedActionViewModel: HomeSharedActionViewModel @@ -66,6 +66,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { @Inject lateinit var vectorPreferences: VectorPreferences @Inject lateinit var popupAlertManager: PopupAlertManager @Inject lateinit var shortcutsHandler: ShortcutsHandler + @Inject lateinit var unknownDeviceViewModelFactory: UnknownDeviceDetectorSharedViewModel.Factory private val drawerListener = object : DrawerLayout.SimpleDrawerListener() { override fun onDrawerStateChanged(newState: Int) { @@ -79,6 +80,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { injector.inject(this) } + override fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel { + return unknownDeviceViewModelFactory.create(initialState) + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager, vectorPreferences.areNotificationEnabledForDevice()) diff --git a/vector/src/main/java/im/vector/riotx/features/home/UnknownDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/UnknownDeviceDetectorSharedViewModel.kt index 1a0d9baf15..00700868f1 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/UnknownDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/UnknownDeviceDetectorSharedViewModel.kt @@ -16,12 +16,16 @@ package im.vector.riotx.features.home +import com.airbnb.mvrx.ActivityViewModelContext import com.airbnb.mvrx.Async +import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext +import com.squareup.inject.assisted.Assisted +import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.NoOpMatrixCallback import im.vector.matrix.android.api.extensions.orFalse import im.vector.matrix.android.api.session.Session @@ -37,6 +41,8 @@ import im.vector.riotx.core.platform.EmptyViewEvents import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModelAction import im.vector.riotx.features.settings.VectorPreferences +import im.vector.riotx.features.widgets.WidgetViewModel +import im.vector.riotx.features.widgets.WidgetViewState import io.reactivex.Observable import io.reactivex.functions.Function3 import timber.log.Timber @@ -53,16 +59,32 @@ data class DeviceDetectionInfo( val currentSessionTrust: Boolean ) -class UnknownDeviceDetectorSharedViewModel( - session: Session, - private val vectorPreferences: VectorPreferences, - initialState: UnknownDevicesState) +class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState, + session: Session, + private val vectorPreferences: VectorPreferences) : VectorViewModel(initialState) { sealed class Action : VectorViewModelAction { data class IgnoreDevice(val deviceIds: List) : Action() } + @AssistedInject.Factory + interface Factory { + fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel + } + + companion object : MvRxViewModelFactory { + + @JvmStatic + override fun create(viewModelContext: ViewModelContext, state: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel? { + val factory = when (viewModelContext) { + is FragmentViewModelContext -> viewModelContext.fragment as? Factory + is ActivityViewModelContext -> viewModelContext.activity as? Factory + } + return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface") + } + } + private val ignoredDeviceList = ArrayList() init { @@ -84,7 +106,7 @@ class UnknownDeviceDetectorSharedViewModel( session.rx().liveMyDeviceInfo(), session.rx().liveCrossSigningPrivateKeys(), Function3 { cryptoList, infoList, pInfo -> -// Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") + // Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") // Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}") infoList .filter { info -> @@ -106,7 +128,7 @@ class UnknownDeviceDetectorSharedViewModel( ) .distinctUntilChanged() .execute { async -> -// Timber.v("## Detector trigger passed distinct") + // Timber.v("## Detector trigger passed distinct") copy( myMatrixItem = session.getUser(session.myUserId)?.toMatrixItem(), unknownSessions = async @@ -147,11 +169,4 @@ class UnknownDeviceDetectorSharedViewModel( super.onCleared() } - companion object : MvRxViewModelFactory { - - override fun create(viewModelContext: ViewModelContext, state: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel? { - val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() - return UnknownDeviceDetectorSharedViewModel(session, VectorPreferences(viewModelContext.activity()), state) - } - } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt index f4db464e6a..767799ad67 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/list/RoomListFragment.kt @@ -72,6 +72,7 @@ class RoomListFragment @Inject constructor( private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel private val roomListParams: RoomListParams by args() private val roomListViewModel: RoomListViewModel by fragmentViewModel() + private lateinit var stateRestorer: LayoutManagerStateRestorer override fun getLayoutResId() = R.layout.fragment_room_list @@ -126,6 +127,7 @@ class RoomListFragment @Inject constructor( modelBuildListener = null roomListView.cleanup() roomController.listener = null + stateRestorer.layoutManager = null createChatFabMenu.listener = null super.onDestroyView() } @@ -190,7 +192,7 @@ class RoomListFragment @Inject constructor( private fun setupRecyclerView() { val layoutManager = LinearLayoutManager(context) - val stateRestorer = LayoutManagerStateRestorer(layoutManager).register() + stateRestorer = LayoutManagerStateRestorer(layoutManager).register() roomListView.layoutManager = layoutManager roomListView.itemAnimator = RoomListAnimator() roomListView.setRecycledViewPool(sharedViewPool) diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt index 28734af0ad..e7df7e842e 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -63,7 +63,7 @@ class RoomMemberProfileFragment @Inject constructor( private val fragmentArgs: RoomMemberProfileArgs by args() private val viewModel: RoomMemberProfileViewModel by fragmentViewModel() - private lateinit var appBarStateChangeListener: AppBarStateChangeListener + private var appBarStateChangeListener: AppBarStateChangeListener? = null override fun getLayoutResId() = R.layout.fragment_matrix_profile @@ -135,6 +135,7 @@ class RoomMemberProfileFragment @Inject constructor( override fun onDestroyView() { matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener) roomMemberProfileController.callback = null + appBarStateChangeListener = null matrixProfileRecyclerView.cleanup() super.onDestroyView() } diff --git a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt index 58c8fead32..617818dd3f 100644 --- a/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/roomprofile/RoomProfileFragment.kt @@ -67,7 +67,7 @@ class RoomProfileFragment @Inject constructor( private lateinit var roomProfileSharedActionViewModel: RoomProfileSharedActionViewModel private val roomProfileViewModel: RoomProfileViewModel by fragmentViewModel() - private lateinit var appBarStateChangeListener: AppBarStateChangeListener + private var appBarStateChangeListener: AppBarStateChangeListener? = null override fun getLayoutResId() = R.layout.fragment_matrix_profile @@ -147,6 +147,7 @@ class RoomProfileFragment @Inject constructor( super.onDestroyView() matrixProfileAppBarLayout.removeOnOffsetChangedListener(appBarStateChangeListener) matrixProfileRecyclerView.cleanup() + appBarStateChangeListener = null } override fun invalidate() = withState(roomProfileViewModel) { state ->