Intermodule (library projects) communication in android application
Asked Answered
E

2

21

In the below shown diagram, I am having 3 modules(as android library) which extends the base "common components module" and all this 3 modules will be added to a single android application. All 3 modules are independent modules but when it comes as an application, it would require to share some data, launch other module and requires more inter-communication.

So can anyone let me know how we can implement the "Data Sharing Layer" and "Navigation Controller" in this kind of architecture?

Example: Module1 -> Login, Module2 -> Profile Management etc and there could be "n" number of modules based on the application need.

enter image description here

Eosin answered 1/3, 2016 at 13:1 Comment(7)
Friends please don't downvote without telling a reason. Any suggestion would also be greatly appreciatedEosin
for data sharing you can use content providersKnowle
@ankitaggarwal, we can go for content providers in-case of exposing our application to other application but here its module to module.Eosin
@Eosin Why not include all modules in the main projects gradle? You can also include a specific module within the gradle of another module. This would allow each module to utilize specific classes from other modulesBrowder
@Eosin One example of this data sharing between modules is thisBrowder
Check out event bus: github.com/greenrobot/EventBus But be careful not to abuse it!Admittance
@SteveC. I could include a specific module within the gradle of another module, but in this case dependency of one module on the other increases. Just i want to remove the dependencies within the module and have them independent.Eosin
S
31

What you are looking for is basically a clean approach on how to communicate with other classes. There is not really a difference in whether or not they are in different modules.

The following sample describes how a LoginActivity could navigate to some profile activity. This is just a basic sample to be improved with what you actually need and intend to do!

  1. Define your interfaces

Write interfaces of what you need. Your Login should be able to open a profile page? Well this sounds like it needs a LoginNavigator!

interface LoginNavigator {
    void showProfile();
}

Include those interfaces in your shared components. There is not really a possibility to go without defining interfaces. You can make them more abstract or more fine grained, this is entirely up to you.

  1. Declare your dependencies

Remember how your Login needs a LoginNavigator? The real problem is on how to supply it to your class. You should have a look at dependency injection, since there are frameworks liks that (could) make this easier. For now, we define an interface for a common component, so that we can retrieve the dependencies we need.

interface NavigatorProvider {
    LoginNavigator provideNavigator();
}

You may guess it—this method is used to get the actual LoginNavigator that you can use to get the implementation of that interface. Usually you would just declare this dependency in the constructor, but since android is somewhat special you need to get it from somewhere yourself.

  1. Provide your dependencies

The easiest way to go is to just have your application implement this interface (or hold an object that does).

class MyApp extends Application implements NavigatorProvider {

    LoginNavigator provideNavigator() {
        return new LoginNavigator() {
            void showProfile() {
                // just some sample code. You should probably not use an
                // anonymous class
                startActivity(new Intent(this, MyProfileActivity.class));
            }
        };
    }
}

Again, you could also return an object that is implementing this interface. This is just a basic sample.

  1. Use the interface. (And don't care about the implementation)

Now the dependency injection is nearly complete. We have an interface that we need, we have some way to provide the dependency, all that's left is to get it and use it.

class LoginActivity extends Activity {

    LoginNavigator mNavigator;

    void onCreate() {
        // get the dependency
        mNavigator = ((NavigatorProvider) getApplicationContext()).provideNavigator();

        // use it where needed. (again, just sample code)
        findShowProfileView().setOnClickListener(new OnClickListener() {
            void onClick(View view) {
                mNavigator.showProfile();
            }
        });
    }
}

Now the dependency is provided, and ready to be used.


What this sample shows is how to basically use interfaces to decouple logic. You will still need some point of entry, since android does not allow to implement your own constructors—this is why the application class is used.

Sorensen answered 9/3, 2016 at 18:52 Comment(4)
Thanks for your detailed answer. Is it more scaleable, because in a real application we would have more modules and lots of data to be shared and lot of communication between modulesEosin
@Eosin having clean and documented interfaces scales pretty well most of the timeSorensen
Any idea about how you will separate networking or database, because let's say if you are using retrofit+gson+okhttp its good to use same object across modules, one way would be to add them as dependencies to every project. Same goes for Analytics, Database, Utils?Disenable
@DavidMedenjak very helpful answer .. any thought on this? https://mcmap.net/q/660204/-how-do-i-implement-navigation-between-android-library-modules-focusing-reusability-and-separation-of-concerns/4806942Clearcut
A
0

I found that solution using Local Broadcast which is implemented in Application Class and send event on Local Broadcast which is received in Application Class.

class AppApplication : Application() {
   
    override fun onCreate() {
        super.onCreate()
        registerBroadcast()
    }

    private fun startProfileActivity() {
        val intent = newIntent<MyProfileActivity>(this)
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        this.startActivity(intent)

    }

    private fun registerBroadcast() {
        LocalBroadcastManager.getInstance(this)
          .registerReceiver(broadCastReceiver,IntentFilter(BROADCAST_VIEW_PROFILE))
    }

    private fun unregisterBroadcast() {
        LocalBroadcastManager.getInstance(this)
            .unregisterReceiver(broadCastReceiver)
    }

    private val broadCastReceiver = object : BroadcastReceiver() {
        override fun onReceive(contxt: Context?, intent: Intent?) {
            when (intent?.action) {
                BROADCAST_VIEW_PROFILE -> {
                    startProfileActivity()
                }
            }
        }
    }

    override fun onTerminate() {
        super.onTerminate()
        unregisterBroadcast()
    }
} 

When you send the event in an Application like this

private fun viewProfileEventSend() {
    // Send Broadcast for view profile to `APP`
    val intent = Intent(BROADCAST_VIEW_PROFILE)
    LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(intent)
}

Because your module doesn't need to get the instance of Application or any interface.

Aggappe answered 2/9, 2021 at 10:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.