-
Notifications
You must be signed in to change notification settings - Fork 174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SES-1936] Fix memory leaks #1493
Changes from 1 commit
75e53c8
c7c0519
90f0cae
c1d82cc
31f4de2
c0128b8
2d7f23a
3533548
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,7 +22,7 @@ class HomeViewModel @Inject constructor(private val threadDb: ThreadDatabase): V | |
|
||
private val executor = viewModelScope + SupervisorJob() | ||
private var lastContext: WeakReference<Context>? = null | ||
private var updateJobs: MutableList<Job> = mutableListOf() | ||
private val updateJobs: MutableList<Job> = mutableListOf() | ||
|
||
private val _conversations = MutableLiveData<List<ThreadRecord>>() | ||
val conversations: LiveData<List<ThreadRecord>> = _conversations | ||
|
@@ -31,6 +31,15 @@ class HomeViewModel @Inject constructor(private val threadDb: ThreadDatabase): V | |
|
||
fun tryUpdateChannel() = listUpdateChannel.trySend(Unit) | ||
|
||
override fun onCleared() { | ||
super.onCleared() | ||
|
||
for (job in updateJobs) { | ||
job.cancel() | ||
} | ||
updateJobs.clear() | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary? Jobs are run in this scope: so I would've thought they'd be canceled when the VM scope is over. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right, I didn't see It is interesting it shows up in the memory reference graph though so I took a closer look at the implementation of @CheckResult
fun ContentResolver.observeQuery(
uri: Uri,
projection: Array<String>? = null,
selection: String? = null,
selectionArgs: Array<String>? = null,
sortOrder: String? = null,
notifyForDescendants: Boolean = false
): Flow<Query> {
val query = ContentResolverQuery(this, uri, projection, selection, selectionArgs, sortOrder)
return flow {
emit(query)
val channel = Channel<Unit>(CONFLATED)
val observer = object : ContentObserver(mainThread) {
override fun onChange(selfChange: Boolean) {
channel.offer(Unit)
}
}
registerContentObserver(uri, notifyForDescendants, observer)
try {
for (item in channel) {
emit(query)
}
} finally {
unregisterContentObserver(observer)
}
}
} I believe the clean up code is not set up correctly, as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me come up with something |
||
|
||
fun getObservable(context: Context): LiveData<List<ThreadRecord>> { | ||
// If the context has changed (eg. the activity gets recreated) then | ||
// we need to cancel the old executors and recreate them to prevent | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -240,19 +240,22 @@ class PathActivity : PassphraseRequiredActionBarActivity() { | |
dotViewLayoutParams.addRule(CENTER_IN_PARENT) | ||
dotView.layoutParams = dotViewLayoutParams | ||
addView(dotView) | ||
Handler().postDelayed({ | ||
postDelayed({ | ||
performAnimation() | ||
}, dotAnimationStartDelay) | ||
} | ||
|
||
private fun performAnimation() { | ||
expand() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Handler().postDelayed({ | ||
collapse() | ||
Handler().postDelayed({ | ||
performAnimation() | ||
}, dotAnimationRepeatInterval) | ||
}, 1000) | ||
if (isAttachedToWindow) { | ||
expand() | ||
|
||
postDelayed({ | ||
collapse() | ||
postDelayed({ | ||
performAnimation() | ||
}, dotAnimationRepeatInterval) | ||
}, 1000) | ||
} | ||
} | ||
|
||
private fun expand() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,10 @@ | ||
package org.session.libsession.avatars | ||
|
||
import android.content.Context | ||
import com.bumptech.glide.load.Key | ||
import java.security.MessageDigest | ||
|
||
class PlaceholderAvatarPhoto(val context: Context, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
val hashString: String, | ||
class PlaceholderAvatarPhoto(val hashString: String, | ||
val displayName: String): Key { | ||
|
||
override fun updateDiskCacheKey(messageDigest: MessageDigest) { | ||
messageDigest.update(hashString.encodeToByteArray()) | ||
messageDigest.update(displayName.encodeToByteArray()) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to restart if it's reattached?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No we don't, I did check it still works. Originally I had the same thought as I didn't know whether putting a view into background will count as a detach but it seems like the window still exists in the background.
Just for a fun reference, this is an answer I got from chatgpt:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, still a little brittle to stop on remove, and not start again if a dev was
There's another issue:
ConActivityV2#showVoiceMessageUI
callsInputBarRecordingView#show
callsupdateTimer()
So every time we start a new voice recording we add another one of these async-recursive calls.
Maybe we can address that too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm ok I can try to address the "what if the dev wants to re-use the view but keep animation running" scenario, I was been lazy.
I'm not sure about your second question,
updateTimer
doesn't seem to "add" anything?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updateTimer
posts a delayed call toupdateTimer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see what you mean: a duplicated call into
show
will have started another timer, which will do nothing but increase the frequency of timer update?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, every time you record a message, we'll create a new "async-recursive" loop.