Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Designing Modular Apps - Circular Dependency problem in navigation

Tags:

android

As you know, designing Android app as modules is one of the popular practices nowadays in the Android development world. But this trend comes with some challenges. One of them is Circular Dependency.

For example, I have a navigation module which opens HomeActivity from Home Feature module. Also, I have to open another activity such as ProductListActivity from products module.

Home feature must include navigation module and navigation module should include HomeFeature if i navigate between activities like the following:

val intent = Intent(activity, HomeActivity::class.java)

This'll cause circular dependency problem.

For a fastest solution to figure out this problem is creating intents like the following and build navigation system on this approach.

Intent(Intent.ACTION_VIEW).setClassName(PACKAGE_NAME, className)

So my questions are, what other possible problems we'll face off with this navigation approach? Are there another practises to handle navigation in modular android apps?

like image 737
savepopulation Avatar asked Jan 04 '19 10:01

savepopulation


People also ask

How do you resolve circular dependency issues?

There are a couple of options to get rid of circular dependencies. For a longer chain, A -> B -> C -> D -> A , if one of the references is removed (for instance, the D -> A reference), the cyclic reference pattern is broken, as well. For simpler patterns, such as A -> B -> A , refactoring may be necessary.

What's wrong with circular dependencies?

Circular dependencies also make code difficult to read and maintain over time, which opens the door to error-prone applications that are difficult to test. If circular dependencies proliferate an architecture, any changes to a single module will likely cause a large ripple effect of errors for others.

What is module circular dependency?

In software engineering, a circular dependency is a relation between two or more modules which either directly or indirectly depend on each other to function properly. Such modules are also known as mutually recursive.

What causes circular dependency?

A circular dependency occurs when two classes depend on each other. For example, class A needs class B, and class B also needs class A. Circular dependencies can arise in Nest between modules and between providers. While circular dependencies should be avoided where possible, you can't always do so.


1 Answers

Here is my solution for stiuation. This enables the use of explicit intents. You can also apply this approach to single activity application with navigation component with a little modification.

Here is navigation object for module B

object ModuleBNavigator {

    internal lateinit var navigationImpl: ModuleBContract

    fun setNavigationImpl(navigationImpl: ModuleBContract) {
        this.navigationImpl = navigationImpl
    }

    interface ModuleBContract {
        fun navigateModuleA(self: Activity, bundle: Bundle?)
    }
}

And here is module B Activity

class ModuleBActivity : Activity() {

    companion object {
        private const val BUNDLE = "BUNDLE"
        fun newIntent(context: Context, bundle: Bundle?) = Intent(context, ModuleBActivity::class.java).apply {
            putExtra(BUNDLE, bundle)
        }
    }
}

And here is app module class to inject navigation impl to module A navigation object

class ApplicationModuleApp : Application() {

    // Can also inject with a DI library
    override fun onCreate() {
        super.onCreate()
        ModuleBNavigator.setNavigationImpl(object : ModuleBNavigator.ModuleBContract {
            override fun navigateModuleA(self: Activity, bundle: Bundle?) {
                self.startActivity(ModuleBActivity.newIntent(self, bundle))
            }
        })
    }
}

And finally you can navigate from module A -> module B with provided implementation

class ModuleAActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // ... Some code 
        ModuleBNavigator.navigationImpl.navigateModuleA(this, Bundle())
        // .. Some code
    }
}

This approact avoids circler dependency and you don't have to use implicit intents anymore. Hope this helps.

like image 146
malik_cesur Avatar answered Oct 07 '22 15:10

malik_cesur