To avoid activity leaks when using threads, handlers, etc, I always wrap the Activity, View, Context, etc., in a WeakReference, so:
val textViewRef = WeakReference(textView)
thread {
.....
val view = textViewRef.get() ?: return#thread
.....
}
For coroutines, do we need to do also the same? I mean to always wrap with WeakReferences to avoid leaks?
Related
Libraries like ExoPlayer require that all calls to it are on the main thread. In this case, I have a getter that fetches a property from ExoPlayer, but it SHOULD be from the main thread.
I tried using Handler, but there is no way to wait for it...
val currentTrackIndex: Int
get() {
var result = -1
handler.post {
result = exoPlayer.currentIndex
}
return result
}
... the result would always be -1.
The only way I found that works is to use runBlocking. However, this doesn't seem like it should be used for production:
val currentTrackIndex: Int
get() {
return runBlocking {
return#runBlocking exoPlayer.currentIndex
}
}
Is there a better way to block the main thread inside getters?
NOTE: All of these calls are inside a Service.
NOTE 2: Even though I need to block the main thread (which might raise red flags), getting the property is as instantaneous as it can be. ExoPlayer is just strange in how it operates.
Create an application class, make a static Context field in it and then use Context.getMainLooper() in your Handler, I think it will solve your problem.
To change the thread in a function I use either CoroutineScope or withContext. I don't know's the difference, but with CourineScope I can also use a handler.
Examples:
private fun removeViews(){
CoroutineScope(Main).launch(handler){
gridRoot.removeAllViews()
}
}
private suspend fun removeViews(){
withContext(Main){
gridRoot.removeAllViews()
}
}
I call this function from a coroutine that works on background thread (IO). Is any more appropriate than the other?
These two are actually radically different and you just happen to have a use case where you don't experience the difference:
CoroutineScope(Main).launch(handler){
This launches a concurrent coroutine that goes on independently.
withContext(Main){
This is a function that completes only when the code inside it completes, and returns its result. This is the way you should be doing it.
The first approach, with CoroutineScope, has another deficiency in that it circumvents structured concurrency. You create an ad-hoc coroutine scope that has no parent and thus won't be automatically cleaned up if it takes a longer time to complete and your GUI is dropped (user navigates away from the current Activity).
You should actually never use the CoroutineScope(Main) idiom, I don't think there's a single instance where it would be appropriate. If you explicitly want to avoid structured concurrency, it is still better and cleaner to write
GlobalScope.launch(Main + handler) {
and has pretty much the same effect.
If you want a concurrent coroutine that fits into structured concurrency, use
fun CoroutineScope.removeViews() {
launch {
gridRoot.removeAllViews()
}
}
Note I removed the handler argument, a child coroutine ignores it because it forwards any failures to its parent coroutine, which is exactly what you want. The parent coroutine should have an exception handler installed.
Technically both are same but when it comes to use case both are different and has big impact on the different use cases so be careful while using them
Coroutine Scope:
CoroutineScope is a starting Point of Coroutine. CoroutineScope can have more than one coroutine within itself, which makes coroutine hierarchy.
Lets think, Parent has more than one children. Think CoroutineScope is a parent and this parent can have more than one child which are also coroutines. These childrens are known as job
private val coroutineScope = CoroutineScope()
coroutineScope(IO).launch{
val childOne = launch(Main){}
val childTwo = launch(Main){}
}
see that childOne and childTwo? why we need these? because we can't directly cancel the coroutine there is no such way the coroutine can be cancelled directly, either the coroutine gets completed or it gets failed. But what if we wanna cancel it? in such cases we need job. But thing to be notice here these job children are totally associated with parent. And Parent is (IO) and childrens are (Main), this parent is started in IO Disptacher but when it comes to those childrens they are gonna switch to (Main) and do their thing but the parent will still be at (IO) switching the Dispatcher of childrens not gonna effect parent.
But what happens if something wrong happens to either of the children,
in that case we will watch this summit:
https://www.youtube.com/watch?v=w0kfnydnFWI
This summit about coroutine exception and cancellation. watch it, its amazing...
withContext:
What is withContext?
withContext should be inside any Coroutine or suspend fun because withContext itself is a suspending function.
withContext is use to switch the context in different situation
but how?
suspend fun fetchFromNetworkAndUpdateUI() {
withContext(IO){
println("Some Fake data from network")
}
withContext(Main){
//updating Ui
//setting that Data to some TextView etc
}
}
see the code, we are fetching the data asynchronously from network cause we don't wanna block the MainThread and then we switch the context, why? cause we can't update UI related stuff in IoDispatcher that's we have change the context to main with withContext(main){} and update the UI.
and there are other use cases like liveData, we are fetching the value using retrofit using IoDispatcher then in next step we have to set it to the liveData by using withContext(main){} cause we can't observe liveData's value in background thread.
yeah, I hope this helps. comment if there is any question.
From the Antonio Leiva article about coroutines:
The coroutine context is a set of rules and configurations that define
how the coroutine will be executed
withContext is a function that allows you to easily change the context of a suspending function, in order to be sure that that function is executed in a particular thread (E.g. Thread from IO pool). To do so you can force a suspending function to execute its body within a particular thread pool, for example:
suspend fun getAuthenticationStatus(): AuthenticationStatus = withContext(Dispatchers.IO) {
when (val result = repository.getAuthenticationStatus()) {
is Result.Success -> result.data
is Result.Error -> AuthenticationStatus.Unauthorized
}
}
This way, even if you're calling this suspending function from a UI scope (MainScope), you are 100% sure that the suspending function is executed in a worker thread and you can update the UI with the returned result in the main thread, such as:
MainScope().launch {
userIdentityVM.getAuthenticationStatus().run {
when (this) {
is AuthenticationStatus.Authenticated -> {
// do something
}
is AuthenticationStatus.Unauthorized -> {
// do something else
}
}
}
}
To sum up, by using withContext you can make your suspending function "Main Safe".
The difference between scope and context is basically the intended purpose.
To launch a coroutine you normally use launch coroutine builder, defined as an extension function on CoroutineScope.
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
// ...
): Job
The context specified as parameter on the coroutine scope is merged to coroutine scope by plus operator and takes precedence on the "default" context specified by coroutine scope. This way you can execute the code in a "parent" context. To go deep I suggest you this article by Roman Elizarov (Team Lead for Kotlin libraries #JetBrains).
So, I have this function:
inline fun runInLoop(intervalInMillis : Long = 1_000, crossinline function : suspend () -> Unit) {
CoroutineScope(Dispatchers.Main).launch {
while (true) {
function.invoke()
delay(intervalInMillis)
}
}
}
And I use it like so:
Time.runInLoop(10_000L) {
binding.timeCreatedTextView.text = mContext.getString(R.string.time_created,
Time.unixToRelativeTime(item.timeCreated))
}
Basically, I use this function to loop every one second to refresh "timeCreatedTextView"s with DateUtils.getRelativeTimeSpanString(), and that is used the most in RecyclerView rows, so multiple infinite loops will be running on a coroutine in the background.
My question is, is anything that I'm doing potential source for memory leaks or high memory usages, since I'm using DateUtils to get the relative time, or doing string formatting every one second for however long the view is on screan?
You are creating a custom CoroutineScope. That is fine, but then it is your job to indicate when that scope is no longer needed, so it stops running your infinite-loop coroutines. Right now, you do not appear to be doing that.
A better solution is to use a CoroutineScope tied to the lifetime of your UI. Since you are using a DialogFragment, the viewLifecycleScope extension property on Fragment would be a likely choice. Then, your coroutines will be cleaned up when the fragment is destroyed. You might use that scope directly, or you might still create your own custom scope, but with viewLifecycleScope as a parent, so you can control individual timers (cancelling them if they are no longer needed) while still getting lifecycle awareness.
I am using an Observable field in a ViewModel. When the Observable field gets updated, I change the UI visibility.
This can be done either done by
object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
}
}
remove the callback in ondestroy.
or
directly mapping in XML like #{} using two-way binding.
Now the question is how do I remove the listener if using two-way binding? I know the Livedata can be a replacement for this.
I am not sure regarding which memory leak you are talking.
Memory leak in Java occur when one object exists long period of time and it contains strong references to other objects that should not be used anymore, thus should be destroyed by GC, but still persist because of that strong reference.
In Android specifically memory leaks usually occur when some long lasting object stores strong reference to an Activity (or in some cases Fragment). All the other memory leaks in android are not so impactful(except the ones with bitmaps - but it is a completely different topic)
So let us return to the data binding with an ObservableField and its callbacks inside the ViewModel or two way data binding via #={}. In most of the cases there will be no memory leak in both of those cases. To understand why - you will need to understand how does Android framework operates with UI and also understand now does view data binding works. So what happens when you are creating a callback via either ObservableField and callback or with #={}
When you write
val someField: ObservabaleField = ObservableFiled<String>("someText")
val someCallback = object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
}
}
someField.addOnPropertyChangedCallback(someCallback)
// and in the layout
android:text="#={viewModel.someField}"
In the generated file it does something like this
androidx.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, viewModelSomeFieldGet);
#Override
protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
switch (localFieldId) {
case 0 :
//...
return onChangeViewModelSomeOtherStuff(object, fieldId);
case 1 :
return onChangeViewModelSomeField((androidx.databinding.ObservableField<java.lang.String>) object, fieldId);
}
return false;
}
As you can see there is no context neither activity or fragment leaks since there is no strong reference to them stored anywhere. There is no references to context, activity or fragment in your ViewModel either(I hope!). Moreover it works the other way around - ui stores link to the ViewModel in the binding implementation thus our ViewModel may be leaking. It is rear case since the UI of an Activity or a Fragment usually gets destroyed along with its ActivityBindingImpl or FragmentBindingImpl bindings but...
To be sure you have manual way to clear references: in either Activity' onDestroy or Fragment' onDestroyView call
clearFindViewByIdCache()
binding.unbind()
binding = null
// if you store view link in your viewModel(which is bad and may cause leaks) this is the perfect place to nullify it
viewModel.view = null
Also to handle binding auto clearing you may use AutoClearedValue
the actual usage may look like(if you don't care about its type)
override var binding: ViewDataBinding? by autoCleared()// that is all - no need of onDestroy or onDestroyView
Edit
If you want to manually unregister all the callbacks from your ObservableFields you can do it. The best way to do it is in onCleared() method of ViewModel. You should call observableField.removeOnPropertyChangedCallback(callback) to handle the stuff. It will look like this considering ObservableField and callback declarations above:
class MyViewModel: ViewModel{
//ObservableField and callback declarations
...
override void onCleared(){
someField.removeOnPropertyChangedCallback(someCallback)
}
}
Edit end
This all things I've just described ensures absence of memory leaks while using ObservableFields and view data bindings. It is all about a correct implementation. Of course you can implement it with leaks, but you can implement it without ones.
Comment if something is still unclear - I will try to expand the answer.
A bit more info about Fragment dependent leaks here
Hope it helps.
You can do that using removeOnPropertyChangedCallback function in ViewModel class. Here is how your ViewModel would look like:
abstract class ObservableViewModel(app: Application): AndroidViewModel(app), Observable {
#delegate:Transient
private val mCallBacks: PropertyChangeRegistry by lazy { PropertyChangeRegistry() }
override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
mCallBacks.add(callback)
}
override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback) {
mCallBacks.remove(callback)
}
fun notifyChange() {
mCallBacks.notifyChange(this, 0)
}
fun notifyChange(viewId:Int){
mCallBacks.notifyChange(this, viewId)
}
}
removeOnPropertyChangedCallback never gets called?
This actually does
get called, eventually and periodically, by the Data Binding framework
to clean listeners that have been collected. It’s likely however, that
your ViewModel will still have some callbacks registered when it is
destroyed, and this is okay. The Data Binding framework uses weak
references for the observers and it’s not required that they be
unregistered before the ViewModel is destroyed. This won’t cause any
memory leaks.
With that said, if you rotate the phone rapidly, several times in a
row, while on the same screen. You’ll notice
ObservableViewModel.addOnPropertyChangedCallBack is called several
times and if you look inside the source for
android.databinding.ViewDataBinding, you’ll see the observer count
does rise each time.
This is where the periodic removal comes in. If you use the app long
enough, rotate a few times, and have a breakpoint set on
ObservableViewModel.removeOnPropertyChangedCallback. You’ll see that
it is called periodically to clean up old observers and if you look up
the call stack you can find more detail about where that comes from,
how it’s triggered, etc.
You can track more at: https://caster.io/lessons/android-mvvm-pattern-with-architecture-component-viewmodels.
Hope this help you!!
Using lifecycle-viewmodel-ktx and lifecycle-livedata-ktx and given the following example:
ViewModel implementation:
class AutocompletionViewModel: ViewModel() {
fun getAutocompletion(inputString: CharSequence?) = liveData {
delay(10)
emit("$inputString DUMMY AUTOCOMPLETION")
}
}
Fragment part:
val viewModel by viewModels<AutocompletionViewModel>()
/* Acquiring EditText*/
editText.addTextChangedListener(object: TextWatcher{
override fun afterTextChanged(editable: Editable?) {
viewModel.getAutocompletion(editable).observe(viewLifecycleOwner, Observer { editable?.append(it) })
}
/* Other TextWatcher method implementations*/
})
Would this code cause a memory leak if the user types text into the EditText?
I assume that with every text change a new LiveData object with a stong referenced Observer is created (and will be alive until the fragment is destroyed). Nevertheless, a similar example was shown by the official docs: https://developer.android.com/topic/libraries/architecture/coroutines#livedata
Yes, there is memory leak:
I attached Android Studios memory profiler and executed AutocompletionViewModel.getAutocompletion 10 thousand times. Regardless of a garbage collection, Observer and LiveData objects remain still in memory:
Conclusion for this case: LiveData object should be backed by a property and Observers should only be attached once
You can't call it a memory leak, you can call it a bad design or disadvantage of this pattern.
But it's not a type of memory leaks.
Memory leak could happened with following actions:
Assign an activity or fragment or listener reference in application lifecycle or static 'companion' object or singleton.
for more info check this article