{"id":6543,"date":"2019-11-28T18:22:21","date_gmt":"2019-11-28T18:22:21","guid":{"rendered":"https:\/\/cheesecakelabs.com\/blog\/?p=6543\/"},"modified":"2022-07-01T17:09:58","modified_gmt":"2022-07-01T17:09:58","slug":"android-architecture-components-viewmodel-leak","status":"publish","type":"post","link":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","title":{"rendered":"Android Architecture Components: you are probably leaking ViewModel and might not know why"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">The <\/span><a href=\"https:\/\/developer.android.com\/jetpack\/docs\/guide\"><span style=\"font-weight: 400;\">Guide to app architecture<\/span><\/a><span style=\"font-weight: 400;\"> suggests using ViewModel + Repository with LiveData in order to implement the data flow from Repository to ViewModel. If you use this approach or anything similar, there&#8217;s a high chance you could be leaking ViewModel. To help you understand and fix this issue, this article will briefly explain what a memory leak is, some of its implications, what happens when a ViewModel is leaked, why it happens and how to fix it for good.<\/span><\/p>\n<p><!--more--><\/p>\n<h2><span style=\"font-weight: 400;\">Memory leak and its implications<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Memory leak happens when for some reason an unused allocated memory cannot be released. If your application has an object that is no longer used, it should be released by the <\/span><a href=\"https:\/\/developer.android.com\/topic\/performance\/memory-overview#gc\"><span style=\"font-weight: 400;\">Garbage Collector<\/span><\/a><span style=\"font-weight: 400;\"> (GC) so that its allocated memory can be used again for another purpose. However if someone has a reference to this object and never clears it, the object won\u2019t ever be garbage collected during the application lifetime, which characterizes a memory leak. But how bad is it?<\/span><\/p>\n<p><span style=\"font-weight: 400;\">A limited amount of memory is provided to an application and it <\/span><a href=\"https:\/\/developer.android.com\/topic\/performance\/memory\"><span style=\"font-weight: 400;\">varies depending on how much RAM the device has<\/span><\/a><span style=\"font-weight: 400;\">. Having too much memory leaks can make the app deplete close to or all of its memory, which could lead to:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">Lags: with too much memory used, the GC might try to reclaim memory portions through a process that will stop the main thread from <\/span><a href=\"https:\/\/techbeacon.com\/app-dev-testing\/what-you-need-know-about-android-app-memory-leaks\"><span style=\"font-weight: 400;\">50ms to 100ms<\/span><\/a><span style=\"font-weight: 400;\">. Since it won\u2019t be able to release the memory that was leaked, this will probably happen many times, making the app quite laggy.<\/span><\/li>\n<li style=\"font-weight: 400;\"><a href=\"https:\/\/developer.android.com\/topic\/performance\/vitals\/anr\"><span style=\"font-weight: 400;\">ANR error<\/span><\/a><span style=\"font-weight: 400;\"> (Application Not Responding error): worse than lags, the app might freeze and become unusable.<\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">OOM error (Out of Memory error): your app will run out of memory and then crash.<\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">Unexpected behavior: if an object lives longer than it should and your app isn\u2019t well structured, this object can keep doing unintended things, which can cause unexpected issues on the app.<\/span><\/li>\n<\/ul>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/media.giphy.com\/media\/10dHotK4K8R0AM\/giphy.gif\"><\/p>\n<p><span style=\"font-weight: 400;\">As you can see, memory leaks can make things get really ugly. One of the most common things that beginner Android developers end up doing is passing Activity Context to another class and never removing its reference. But how about the ViewModel? You are probably thinking that you would never pass a ViewModel to another class, such as a Repository. Then how is a ViewModel leaked?<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Leaking a ViewModel<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">A pretty usual process that results in leaking a ViewModel is when it uses a Repository to make an async network request and wait for the response, only to do something else afterwards. Based on this info, think about the scenario on Image 1 where we have a MainActivity, a SecondaryActivity + SecondaryViewModel and a Repository that performs the network request. The MainActivity starts the SecondaryActivity which then asks the Repository to perform a request and waits for the response. However, before the request is finished, you go back to the MainActivity and it gets finished when you are there.&nbsp;<\/span><\/p>\n<p>By this time, since SecondaryActivity has been destroyed, SecondaryViewModel should be dead or garbage collected, meaning it shouldn\u2019t receive the request\u2019s response. However if you implement this flow poorly as in Image 1, the ViewModel won\u2019t be deleted and will still receive the response. Why does it happen?<\/p>\n<figure id=\"attachment_6544\" aria-describedby=\"caption-attachment-6544\" style=\"width: 782px\" class=\"wp-caption alignnone\"><img decoding=\"async\" class=\" wp-image-6544\" src=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/sequence.png\" alt=\"sequence-viewmodel-leaking\" width=\"782\" height=\"681\" srcset=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/sequence.png 1128w, https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/sequence-768x670.png 768w\" sizes=\"(max-width: 782px) 100vw, 782px\" \/><figcaption id=\"caption-attachment-6544\" class=\"wp-caption-text\">IMAGE 1 &#8211; Sequence Diagram of a flow that leaks a ViewModel<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">To illustrate a similar situation, let\u2019s reproduce this flow with a simulated network request with the code below.&nbsp;<\/span><\/p>\n<p>&nbsp;<\/p>\n<pre class=\"language-swift\"><code class=\"language-swift\"><b>object <\/b><span style=\"font-weight: 400;\">Repository {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>private var <\/b><b>requestCallback<\/b><span style=\"font-weight: 400;\">: (() -&gt; Unit)? = <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><i><span style=\"font-weight: 400;\">\/\/ simulate a network request but in reality just sets the callback for the response<\/span><\/i>\n\n<i><span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><\/i><b>fun <\/b><span style=\"font-weight: 400;\">performFakeRequest(callback: () -&gt; Unit) {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>requestCallback <\/b><span style=\"font-weight: 400;\">= callback<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;}<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><i><span style=\"font-weight: 400;\">\/\/ simulate when the network request finishes<\/span><\/i>\n\n<i><span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><\/i><b>fun <\/b><span style=\"font-weight: 400;\">finishFakeRequest() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>requestCallback<\/b><span style=\"font-weight: 400;\">?.invoke()<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;}<\/span>\n\n<span style=\"font-weight: 400;\">}<\/span>\n\n<b>class <\/b><span style=\"font-weight: 400;\">SecondaryViewModel : ViewModel() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>fun <\/b><span style=\"font-weight: 400;\">onPerformFakeRequestClicked() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.d(<\/span><b>\"SecondaryViewModel\"<\/b><span style=\"font-weight: 400;\">, <\/span><b>\"performing request from view model <\/b><b>$this<\/b><b>\"<\/b><span style=\"font-weight: 400;\">)<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Repository.performFakeRequest <\/span><b>{<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">Log.d(<\/span><b>\"SecondaryViewModel\"<\/b><span style=\"font-weight: 400;\">, <\/span><b>\"finished request and executed callback on view model <\/b><b>$this<\/b><b>\"<\/b><span style=\"font-weight: 400;\">)<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>}<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">}<\/span>\n\n<span style=\"font-weight: 400;\">}<\/span>\n\n<b>class <\/b><span style=\"font-weight: 400;\">MainActivity : AppCompatActivity() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>...<\/b>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>private fun <\/b><span style=\"font-weight: 400;\">setupView() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<\/span>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">endFakeRequestButton.setOnClickListener <\/span><b>{<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">Repository.finishFakeRequest()<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>}<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">}<\/span>\n\n<span style=\"font-weight: 400;\">}<\/span><\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">In this small code (full code is <\/span><a href=\"https:\/\/github.com\/diegohkd\/ViewModelLeak\/tree\/master\/leak\/src\/main\/java\/example\/com\/viewmodelleak\"><span style=\"font-weight: 400;\">here<\/span><\/a><span style=\"font-weight: 400;\">) SecondaryViewModel asks the Repository to perform the request and wait for the response through a callback, whereas MainActivity has a button to simulate when the request ends.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">After reproducing the steps from Image 1, let\u2019s analyze the memory allocation using the <\/span><a href=\"https:\/\/developer.android.com\/studio\/profile\/android-profiler\"><span style=\"font-weight: 400;\">Profiler Tool<\/span><\/a><span style=\"font-weight: 400;\">:<\/span><\/p>\n<figure id=\"attachment_6545\" aria-describedby=\"caption-attachment-6545\" style=\"width: 989px\" class=\"wp-caption alignnone\"><img decoding=\"async\" class=\" wp-image-6545\" src=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/SecondaryViewModel.png\" alt=\"Screenshot of Profiler Tool showing leaked SecondaryViewModel\" width=\"989\" height=\"456\" srcset=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/SecondaryViewModel.png 1136w, https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/SecondaryViewModel-768x354.png 768w\" sizes=\"(max-width: 989px) 100vw, 989px\" \/><figcaption id=\"caption-attachment-6545\" class=\"wp-caption-text\">IMAGE 2 &#8211; Screenshot of Profiler Tool showing leaked SecondaryViewModel<\/figcaption><\/figure>\n<p><span style=\"font-weight: 400;\">As you can see under <\/span><i><span style=\"font-weight: 400;\">Live Allocation-&gt;Class Name-&gt;app heap<\/span><\/i><span style=\"font-weight: 400;\"> section, SecondaryViewModel is still allocated. If you click the button from MainActivity that simulates when the request finishes, you\u2019ll notice that the SecondaryViewModel callback will be triggered even though it should be dead. You can try to <\/span><a href=\"https:\/\/developer.android.com\/studio\/profile\/memory-profiler\"><span style=\"font-weight: 400;\">force garbage collection<\/span><\/a><span style=\"font-weight: 400;\"> but it won\u2019t work. SecondaryViewModel will never be garbage collected because it\u2019s been leaked. But how did it happen? Maybe from the SecondaryViewModel$onPerformFakeRequestClicked$1 on Image 2 you already guessed whose fault it is: the lambda expression used as the callback.&nbsp;<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Callback\u2026 the villain?<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">There are many ways to implement the communication between ViewModel and Repository. You could <\/span><a href=\"https:\/\/developer.android.com\/reference\/android\/arch\/lifecycle\/LiveData#observeforever\"><span style=\"font-weight: 400;\">observe forever<\/span><\/a><span style=\"font-weight: 400;\"> a LiveData or use <\/span><a href=\"https:\/\/proandroiddev.com\/clean-easy-new-how-to-architect-your-app-part-4-livedata-transformations-f0fd9f313ec6\"><span style=\"font-weight: 400;\">LiveData transformations<\/span><\/a><span style=\"font-weight: 400;\"> inside the ViewModel, subscribe to observable events using <\/span><a href=\"http:\/\/reactivex.io\/\"><span style=\"font-weight: 400;\">Rx<\/span><\/a><span style=\"font-weight: 400;\">, use callbacks with a Listener interface or lambda expression. Either way, you have to be careful not to leak the ViewModel.&nbsp;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Unless you are making a synchronous request in ViewModel, it\u2019s hard to think of how&nbsp; ViewModel would receive data asynchronously without passing its reference to whoever will trigger the callback. It might not be clear to us, but that\u2019s exactly what happens when we use a callback with lambda expression or something similar.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">When creating a lambda expression, you can access everything that is in the scope of the enclosing class inside, right? <\/span><span style=\"font-weight: 400;\">This is possible because the lambda expression has an implicit reference to the enclosing class. <\/span><span style=\"font-weight: 400;\">And guess what, that\u2019s the source of our leak.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">When SecondaryViewModel calls performFakeRequest(callback: () -&gt; Unit), <\/span><span style=\"font-weight: 400;\">an implicit reference to the ViewModel is passed to the callback<\/span><span style=\"font-weight: 400;\"> and a reference to the same callback is passed to the Repository, which keeps this reference in its lifetime. Do you know what that means? Yep, the ViewModel will live until the app is terminated. Since Repository is a singleton class it won\u2019t be garbage collected during the application lifetime. Consequently, the same will happen for the callback and the ViewModel.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">If the Repository wasn\u2019t a Singleton, it probably wouldn\u2019t be that bad, but it would still be a memory leak because the ViewModel reference would persist for as long as the request doesn\u2019t finish, meaning ViewModel also might not be garbage collected when it should &#8211; which is also a memory leak. This would be especially bad if instead of a one shot request, the non-singleton Repository keeps receiving some data until you cancel it. So, the right way to fix this is by clearing all ViewModel.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Avoiding leaks<\/span><\/h2>\n<p><img decoding=\"async\" class=\"aligncenter\" src=\"https:\/\/media.giphy.com\/media\/l3q2MDnkLri1t7i5a\/giphy.gif\"><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">The basic idea to fix this memory leak is pretty simple. In our sample code, when the request is finished, the reference to the callback should be cleared (which would then clear the reference to the ViewModel when the callback is garbage collected). In addition to that, when the ViewModel is no longer used, i.e. when it calls the onCleared() function, <\/span><span style=\"font-weight: 400;\">any reference to it should also be cleared.<\/span><span style=\"font-weight: 400;\">. Here are some suggestions on how to achieve this:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">Have a Repository function that clears the reference to the callback. The downside is when you have many callbacks co-existing in the same Repository and&nbsp;you have to clear a specific one. In this case you\u2019ll probably need to map callbacks to ViewModels somehow.<\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">If you are using Rx or anything similar that can unsubscribe to data stream, you just need to unregister from it by disposing the <\/span><a href=\"https:\/\/www.raywenderlich.com\/3983802-working-with-rxjava-disposables-in-kotlin\"><span style=\"font-weight: 400;\">Disposable<\/span><\/a><span style=\"font-weight: 400;\"> whenever it is necessary (e.g. when onCleared() is called).<\/span><\/li>\n<li style=\"font-weight: 400;\"><span style=\"font-weight: 400;\">Similar to Rx\u2019s Disposable, you can implement your own class that is responsible for unsubscribing from a data stream and clearing callback references. For example, if you implement a request that returns Retrofit\u2019s <\/span><a href=\"https:\/\/square.github.io\/retrofit\/2.x\/retrofit\/retrofit2\/Call.html\"><span style=\"font-weight: 400;\">Call<\/span><\/a><span style=\"font-weight: 400;\">, this class can take care of clearing the request and callbacks like this:<\/span><\/li>\n<\/ul>\n<pre class=\"language-swift\"><code class=\"language-swift\"><b>import <\/b><span style=\"font-weight: 400;\">retrofit2.Call<\/span>\n\n<b>class <\/b><span style=\"font-weight: 400;\">Request&lt;<\/span><span style=\"font-weight: 400;\">T<\/span><span style=\"font-weight: 400;\">&gt;(<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>call<\/b><span style=\"font-weight: 400;\">: Call&lt;<\/span><span style=\"font-weight: 400;\">T<\/span><span style=\"font-weight: 400;\">&gt;,<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>success<\/b><span style=\"font-weight: 400;\">: ((<\/span><span style=\"font-weight: 400;\">T<\/span><span style=\"font-weight: 400;\">) -&gt; Unit)?,<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>failure<\/b><span style=\"font-weight: 400;\">: ((Throwable?) -&gt; Unit)? = <\/span><b>null<\/b>\n\n<span style=\"font-weight: 400;\">) {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>fun <\/b><span style=\"font-weight: 400;\">dispose() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>call<\/b><span style=\"font-weight: 400;\">.cancel()<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>success <\/b><span style=\"font-weight: 400;\">= <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><b>failure <\/b><span style=\"font-weight: 400;\">= <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">}<\/span>\n\n<span style=\"font-weight: 400;\">}<\/span><\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">I implemented a very simple example of this Request class that fixes the ViewModel leak in our sample code. Since there isn\u2019t really any network request being performed, the Request only contains the callback. Then, when SecondaryViewModel calls onClear(), you just need to dispose the request and that\u2019s it, the implicit ViewModel reference can be garbage collected. You can check the full code <\/span><a href=\"https:\/\/square.github.io\/retrofit\/2.x\/retrofit\/retrofit2\/Call.html\"><span style=\"font-weight: 400;\">here<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<pre class=\"language-swift\"><code class=\"language-swift\"><b>class <\/b><span style=\"font-weight: 400;\">Request(<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>callback<\/b><span style=\"font-weight: 400;\">: (() -&gt; Unit)?<\/span>\n\n<span style=\"font-weight: 400;\">) {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>fun <\/b><span style=\"font-weight: 400;\">dispose() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>callback <\/b><span style=\"font-weight: 400;\">= <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">}<\/span>\n\n<span style=\"font-weight: 400;\">}<\/span>\n\n<b>object <\/b><span style=\"font-weight: 400;\">Repository {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>private var <\/b><b>request<\/b><span style=\"font-weight: 400;\">: Request? = <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><i><span style=\"font-weight: 400;\">\/\/ simulate a network request, but in reality just sets the callback for the response<\/span><\/i>\n\n<i><span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><\/i><b>fun <\/b><span style=\"font-weight: 400;\">performFakeRequest(callback: () -&gt; Unit): Request =<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Request(callback).<\/span><i><span style=\"font-weight: 400;\">apply <\/span><\/i><b>{<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><b>request <\/b><span style=\"font-weight: 400;\">= <\/span><b>this<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><b>}<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><i><span style=\"font-weight: 400;\">\/\/ simulate when the network request finishes<\/span><\/i>\n\n<i><span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><\/i><b>fun <\/b><span style=\"font-weight: 400;\">finishFakeRequest() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>request<\/b><span style=\"font-weight: 400;\">?.<\/span><b>callback<\/b><span style=\"font-weight: 400;\">?.invoke()<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;}<\/span>\n\n<span style=\"font-weight: 400;\">}<\/span>\n\n<b>class <\/b><span style=\"font-weight: 400;\">SecondaryViewModel : ViewModel() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>var <\/b><b>request<\/b><span style=\"font-weight: 400;\">: Request? = <\/span><b>null<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;fun <\/b><span style=\"font-weight: 400;\">onPerformFakeRequestClicked() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Log.d(<\/span><b>\"SecondaryViewModel\"<\/b><span style=\"font-weight: 400;\">, <\/span><b>\"performing request from view model <\/b><b>$this<\/b><b>\"<\/b><span style=\"font-weight: 400;\">)<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>request <\/b><span style=\"font-weight: 400;\">= Repository.performFakeRequest <\/span><b>{<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">Log.d(<\/span><b>\"SecondaryViewModel\"<\/b><span style=\"font-weight: 400;\">, <\/span><b>\"finished request and executed callback on view model <\/b><b>$this<\/b><b>\"<\/b><span style=\"font-weight: 400;\">)<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>}<\/b>\n\n<b>&nbsp;&nbsp;&nbsp;<\/b><span style=\"font-weight: 400;\">}<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;<\/span><b>override fun <\/b><span style=\"font-weight: 400;\">onCleared() {<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>super<\/b><span style=\"font-weight: 400;\">.onCleared()<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/span><b>request<\/b><span style=\"font-weight: 400;\">?.dispose()<\/span>\n\n<span style=\"font-weight: 400;\">&nbsp;&nbsp;&nbsp;}<\/span>\n\n<span style=\"font-weight: 400;\">}<\/span><\/code><\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">Last but not least, remember when I mentioned \u201cunexpected behavior\u201d as one of the consequences of memory leak? Let\u2019s say your app needs to perform a sequence of very important operations related to saving data locally and all of them should be completed. If you implement it in a way that your ViewModel will sequentially ask the Repository to perform all operations, and if suddenly the ViewModel calls onClear() in the middle of this process, the ViewModel shouldn\u2019t yet be garbage collected so that it can finish all operations. As stated by Jos\u00e9 Alc\u00e9rreca, this critical logic shouldn\u2019t be in the ViewModel. It should ask the Repository to do all operations only once and forget about it.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">\u201c<\/span><a href=\"https:\/\/medium.com\/androiddevelopers\/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54\"><span style=\"font-weight: 400;\">Don\u2019t put logic in the ViewModel that is critical to saving clean state or related to data. Any call you make from a ViewModel can be the last one.<\/span><\/a><span style=\"font-weight: 400;\">\u201d&nbsp;<\/span><\/p>\n<p>&nbsp;<\/p>\n<h2><span style=\"font-weight: 400;\">Wrapping up<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Leaking ViewModel might not be as bad as leaking Activity Context, which is probably why most people are not even aware they are leaking it and why there isn\u2019t much information about it on the Internet. But on this article you have seen that it sure can cause many problems if you are not careful enough. Nowadays most people uninstall apps after the very first bug or issue they find, and memory leak can cause many of those.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Even though this article is based on the architecture suggested by the <\/span><a href=\"https:\/\/developer.android.com\/jetpack\/docs\/guide\"><span style=\"font-weight: 400;\">Guide to app architecture<\/span><\/a><span style=\"font-weight: 400;\">, there are many situations that can lead to memory leak whether you are using MVVM, MVP, MVC, or no architecture at all. Memory Leak has nothing to do with an architecture. You should try to always avoid them as they can be very harmful.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">I hope you find this article useful. I tried to keep it simple and leave references with further information about the subject.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">The sample code that I used is available at <\/span><a href=\"https:\/\/github.com\/diegohkd\/ViewModelLeak\"><span style=\"font-weight: 400;\">ViewModelLeak<\/span><\/a><span style=\"font-weight: 400;\">.<br \/>\n<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-weight: 400;\">References<\/span><\/p>\n<p><a href=\"https:\/\/android.jlelse.eu\/memory-leak-patterns-in-android-4741a7fcb570\"><span style=\"font-weight: 400;\">https:\/\/android.jlelse.eu\/memory-leak-patterns-in-android-4741a7fcb570<\/span><\/a><\/p>\n<p><a href=\"https:\/\/livebook.manning.com\/book\/kotlin-in-action\/chapter-5\/1\"><span style=\"font-weight: 400;\">https:\/\/livebook.manning.com\/book\/kotlin-in-action\/chapter-5\/1<\/span><\/a><\/p>\n<p><a href=\"https:\/\/www.baeldung.com\/java-8-lambda-expressions-tips\"><span style=\"font-weight: 400;\">https:\/\/www.baeldung.com\/java-8-lambda-expressions-tips<\/span><\/a><\/p>\n<p><a href=\"https:\/\/medium.com\/tompee\/idiomatic-kotlin-lambdas-and-sam-constructors-fe2075965bfb\"><span style=\"font-weight: 400;\">https:\/\/medium.com\/tompee\/idiomatic-kotlin-lambdas-and-sam-constructors-fe2075965bfb<\/span><\/a><\/p>\n<p><a href=\"https:\/\/proandroiddev.com\/everything-you-need-to-know-about-memory-leaks-in-android-d7a59faaf46a\"><span style=\"font-weight: 400;\">https:\/\/proandroiddev.com\/everything-you-need-to-know-about-memory-leaks-in-android-d7a59faaf46a<\/span><\/a><\/p>\n<p><a href=\"https:\/\/techbeacon.com\/app-dev-testing\/what-you-need-know-about-android-app-memory-leaks\"><span style=\"font-weight: 400;\">https:\/\/techbeacon.com\/app-dev-testing\/what-you-need-know-about-android-app-memory-leaks<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/topic\/performance\/memory\"><span style=\"font-weight: 400;\">https:\/\/developer.android.com\/topic\/performance\/memory<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/studio\/profile\/android-profiler\"><span style=\"font-weight: 400;\">https:\/\/developer.android.com\/studio\/profile\/android-profiler<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/studio\/profile\/memory-profiler\"><span style=\"font-weight: 400;\">https:\/\/developer.android.com\/studio\/profile\/memory-profiler<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/reference\/android\/arch\/lifecycle\/LiveData#observeforever\"><span style=\"font-weight: 400;\">https:\/\/developer.android.com\/reference\/android\/arch\/lifecycle\/LiveData#observeforever<\/span><\/a><\/p>\n<p><a href=\"https:\/\/proandroiddev.com\/clean-easy-new-how-to-architect-your-app-part-4-livedata-transformations-f0fd9f313ec6\"><span style=\"font-weight: 400;\">https:\/\/proandroiddev.com\/clean-easy-new-how-to-architect-your-app-part-4-livedata-transformations-f0fd9f313ec6<\/span><\/a><\/p>\n<p><a href=\"http:\/\/reactivex.io\/\"><span style=\"font-weight: 400;\">http:\/\/reactivex.io\/<\/span><\/a><\/p>\n<p><a href=\"https:\/\/www.raywenderlich.com\/3983802-working-with-rxjava-disposables-in-kotlin\"><span style=\"font-weight: 400;\">https:\/\/www.raywenderlich.com\/3983802-working-with-rxjava-disposables-in-kotlin<\/span><\/a><\/p>\n<p><a href=\"https:\/\/square.github.io\/retrofit\/2.x\/retrofit\/retrofit2\/Call.html\"><span style=\"font-weight: 400;\">https:\/\/square.github.io\/retrofit\/2.x\/retrofit\/retrofit2\/Call.html<\/span><\/a><\/p>\n<p><a href=\"https:\/\/medium.com\/androiddevelopers\/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54\"><span style=\"font-weight: 400;\">https:\/\/medium.com\/androiddevelopers\/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54<\/span><\/a><\/p>\n<p><a href=\"https:\/\/developer.android.com\/topic\/libraries\/architecture\/viewmodel\"><span style=\"font-weight: 400;\">https:\/\/developer.android.com\/topic\/libraries\/architecture\/viewmodel<\/span><\/a><\/p>\n<p><span style=\"font-weight: 400;\">&nbsp;<\/span><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Guide to app architecture suggests using ViewModel + Repository with LiveData in order to implement the data flow from Repository to ViewModel. If you use this approach or anything similar, there&#8217;s a high chance you could be leaking ViewModel. To help you understand and fix this issue, this article will briefly explain what a [&hellip;]<\/p>\n","protected":false},"author":65,"featured_media":6549,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[432],"tags":[15],"class_list":["post-6543","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","tag-tag-mobile"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Android: you&#039;re probably leaking ViewModel and might not know why<\/title>\n<meta name=\"description\" content=\"A briefly explain what a memory leak is, some of its implications, what happens when a ViewModel is leaked, why it happens and how to fix it for good.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Android: you&#039;re probably leaking ViewModel and might not know why\" \/>\n<meta property=\"og:description\" content=\"A briefly explain what a memory leak is, some of its implications, what happens when a ViewModel is leaked, why it happens and how to fix it for good.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\" \/>\n<meta property=\"og:site_name\" content=\"Cheesecake Labs\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/cheesecakelabs\" \/>\n<meta property=\"article:published_time\" content=\"2019-11-28T18:22:21+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-07-01T17:09:58+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"720\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Cheesecake Labs\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@cheesecakelabs\" \/>\n<meta name=\"twitter:site\" content=\"@cheesecakelabs\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\"},\"author\":{\"name\":\"Diego Oliveira\"},\"headline\":\"Android Architecture Components: you are probably leaking ViewModel and might not know why\",\"datePublished\":\"2019-11-28T18:22:21+00:00\",\"dateModified\":\"2022-07-01T17:09:58+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\"},\"wordCount\":1805,\"image\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\",\"keywords\":[\"Mobile\"],\"articleSection\":[\"Engineering\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\",\"name\":\"Android: you're probably leaking ViewModel and might not know why\",\"isPartOf\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\",\"datePublished\":\"2019-11-28T18:22:21+00:00\",\"dateModified\":\"2022-07-01T17:09:58+00:00\",\"author\":{\"@type\":\"person\",\"name\":\"Diego Oliveira\"},\"description\":\"A briefly explain what a memory leak is, some of its implications, what happens when a ViewModel is leaked, why it happens and how to fix it for good.\",\"breadcrumb\":{\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage\",\"url\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\",\"contentUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png\",\"width\":2000,\"height\":720},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cheesecakelabs.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Android Architecture Components: you are probably leaking ViewModel and might not know why\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#website\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/\",\"name\":\"Cheesecake Labs\",\"description\":\"Nearshore outsourcing company for Web and Mobile design and engineering services, and staff augmentation for startups and enterprises..\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cheesecakelabs.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"name\":\"Diego Oliveira\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cheesecakelabs.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/06\/diego-300x300.jpg\",\"contentUrl\":\"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/06\/diego-300x300.jpg\",\"caption\":\"Diego Oliveira\"},\"description\":\"10 years of experience in Marketing and Sales in the Technology sector. My main purpose is help, support and structure efficient operations and also develop independent and multidisciplinary teams.\",\"url\":\"https:\/\/cheesecakelabs.com\/blog\/autor\/diegooliveira\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Android: you're probably leaking ViewModel and might not know why","description":"A briefly explain what a memory leak is, some of its implications, what happens when a ViewModel is leaked, why it happens and how to fix it for good.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","og_locale":"en_US","og_type":"article","og_title":"Android: you're probably leaking ViewModel and might not know why","og_description":"A briefly explain what a memory leak is, some of its implications, what happens when a ViewModel is leaked, why it happens and how to fix it for good.","og_url":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","og_site_name":"Cheesecake Labs","article_publisher":"https:\/\/www.facebook.com\/cheesecakelabs","article_published_time":"2019-11-28T18:22:21+00:00","article_modified_time":"2022-07-01T17:09:58+00:00","og_image":[{"width":2000,"height":720,"url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","type":"image\/png"}],"author":"Cheesecake Labs","twitter_card":"summary_large_image","twitter_creator":"@cheesecakelabs","twitter_site":"@cheesecakelabs","twitter_misc":{"Written by":null,"Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#article","isPartOf":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/"},"author":{"name":"Diego Oliveira"},"headline":"Android Architecture Components: you are probably leaking ViewModel and might not know why","datePublished":"2019-11-28T18:22:21+00:00","dateModified":"2022-07-01T17:09:58+00:00","mainEntityOfPage":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/"},"wordCount":1805,"image":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage"},"thumbnailUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","keywords":["Mobile"],"articleSection":["Engineering"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","url":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/","name":"Android: you're probably leaking ViewModel and might not know why","isPartOf":{"@id":"https:\/\/cheesecakelabs.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage"},"image":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage"},"thumbnailUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","datePublished":"2019-11-28T18:22:21+00:00","dateModified":"2022-07-01T17:09:58+00:00","author":{"@type":"person","name":"Diego Oliveira"},"description":"A briefly explain what a memory leak is, some of its implications, what happens when a ViewModel is leaked, why it happens and how to fix it for good.","breadcrumb":{"@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#primaryimage","url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","contentUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2019\/11\/Android_architecture1.png","width":2000,"height":720},{"@type":"BreadcrumbList","@id":"https:\/\/cheesecakelabs.com\/blog\/android-architecture-components-viewmodel-leak\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cheesecakelabs.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Android Architecture Components: you are probably leaking ViewModel and might not know why"}]},{"@type":"WebSite","@id":"https:\/\/cheesecakelabs.com\/blog\/#website","url":"https:\/\/cheesecakelabs.com\/blog\/","name":"Cheesecake Labs","description":"Nearshore outsourcing company for Web and Mobile design and engineering services, and staff augmentation for startups and enterprises..","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cheesecakelabs.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","name":"Diego Oliveira","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cheesecakelabs.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/06\/diego-300x300.jpg","contentUrl":"https:\/\/ckl-website-static.s3.amazonaws.com\/wp-content\/uploads\/2017\/06\/diego-300x300.jpg","caption":"Diego Oliveira"},"description":"10 years of experience in Marketing and Sales in the Technology sector. My main purpose is help, support and structure efficient operations and also develop independent and multidisciplinary teams.","url":"https:\/\/cheesecakelabs.com\/blog\/autor\/diegooliveira\/"}]}},"_links":{"self":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/6543","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/users\/65"}],"replies":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/comments?post=6543"}],"version-history":[{"count":1,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/6543\/revisions"}],"predecessor-version":[{"id":10183,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/posts\/6543\/revisions\/10183"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/media\/6549"}],"wp:attachment":[{"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/media?parent=6543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/categories?post=6543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cheesecakelabs.com\/blog\/wp-json\/wp\/v2\/tags?post=6543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}