Circular dependency in Android - Should I worry about it?
Over my years working on Android apps I saw that many people wasn’t sure how to deal with circular dependency in Android. Some are not aware of it at all others are aware but don’t know how to deal with it.
Here some info about the JVM — In the Android platform we use Java to code and as most of you know Java code is complied to bytecode. This bytecode interpreted by each operating system and converts each bytecode instruction to the specific assembly instruction of the current hardware. Having the JVM layer is what makes Java genius (at least 20 years ago) since it enables for one software or a codebase to work with different kinds of hardwares and operating systems. In Android instead of JVM we had Dalvik and now ART which are the same concept.
Okay, after we figure out what is the JVM or Dalvik/ART are, how is it related to Circular dependency?
The answer is the GC — Garbage Collection. The GC is the algorithm that the JVM, manages the memory with. The way it works is directly related to dealing with circular dependency.
Let’s dive into how the GC works in very high level with code examples to understand what we need to consider while writing code in Android (same applies for Java).
There are few common Design patterns in Android and in general programing that makes us think about circular dependency. Let’s take a look on a basic implementation of a recyclerView adapter:
In this example, In order to get the layoutInflater, we are using the context that was given in the adapter’s constructor (I’m well aware that we can grab the context from the parent view, it’s just for sake of the example).
How it looks like from the outside Activity :
Okay this is where it is starting to be confusing, the MainAcitvity class has a ChatAdapter as a member and the ChatAdapter has the Context which is the MainActivity as a member…. so class A → B and B → A this situation AKA circular dependency.
So… do we have an issue? is that implementation will lead us to a memory leak? do we need to do stuff like nulling the adapter ?:
The answer is no.
The common believe is that the GC will not collect those two objects since there are pointing to each other. However, this is not how the GC works. The GC algorithm (in very high level) looks at the objects as they are in big graph, for every object it checks if there is path to the root and only if there is a path it will not collect this objects.
In our example — before the Activity will be destroyed it will look like that :
And after the activity will be destroyed it will look like that :
Since there is no path from the root object to both the MainAcivity object as well as to the ChatAdapter object, both objects will be collected by the GC once the activity will be destroyed.
So — go ahead and pass the context to the adapter, the View to the ViewModel, or feel free to implement any other circular dependencies design patters…. As long as there is no path to those objects after the Activity is destroyed, you are safe!