Start looking at memory leaks

This commit is contained in:
ganfra 2020-06-08 19:46:55 +02:00
parent 0ba120356a
commit d6329a1ab6
7 changed files with 51 additions and 20 deletions

View file

@ -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'

View file

@ -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<Parcelable?>()
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)
}
}
}

View file

@ -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())

View file

@ -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<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) {
sealed class Action : VectorViewModelAction {
data class IgnoreDevice(val deviceIds: List<String>) : Action()
}
@AssistedInject.Factory
interface Factory {
fun create(initialState: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel
}
companion object : MvRxViewModelFactory<UnknownDeviceDetectorSharedViewModel, UnknownDevicesState> {
@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<String>()
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<UnknownDeviceDetectorSharedViewModel, UnknownDevicesState> {
override fun create(viewModelContext: ViewModelContext, state: UnknownDevicesState): UnknownDeviceDetectorSharedViewModel? {
val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession()
return UnknownDeviceDetectorSharedViewModel(session, VectorPreferences(viewModelContext.activity()), state)
}
}
}

View file

@ -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)

View file

@ -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()
}

View file

@ -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 ->