I have 2 different modules, module A and module B is library module. In module A, there is a button that will launch activity library module B when user click the button. How do i link the button to launch activity in module B?
You can pass the package name in the Intent like this:
Intent intent = new Intent(YourClassA.this, com.your.package.name.ClassB.class);
startActivity(intent);
There's two scenarios I see here:
Scenario #1:
Module A includes module B in its build.gradle file. If so, module A can directly start the Activity because the Activity is visible to it. This would be (in Kotlin):
button.setOnClickListener {
val intent = Intent(this, ActivityInModuleB::class.java)
// Possibly add extras here
startActivity(intent)
}
Scenario #2:
Module A does NOT include module B, but both are in the same project. In this case, the ActivityInModuleB class would not be visible to module A, so you could not import it and use it directly. In this case, there's a few possible solutions...
Option #1:
Use <activity-alias> in your AndroidManifest.xml for module B to associate ActivityInModuleB to a string. Then you can create the Intent in module A and call intent.setAction(actionStringInManifestGoesHere) and thus you can indirectly refer to this class.
I personally haven't used this approach, though I wanted to in a previous project. It's just that that project was already using option #2 and it would have taken a while to switch over.
Option #2:
Setup a LocalBroadcastReceiver in a module that includes both module A and module B. Then, each module can register themselves as handling broadcasts. When a broadcast is sent, an action is attached (similar to option #1). The broadcast receiver will call each module's handler and give it an opportunity to start the appropriate Activity, if it finds the associated action in its supported list. At the point a module starts an Activity it can return true to say it handled the action and your main module can decide to either short circuit calling any other modules' handlers OR let all modules have an opportunity.
This approach definitely works (it's used in an app with millions of installs). BUT, I think it's basically just recreating option #1, so probably is overkill.
Related
I try to call a module (Library) in my MainActivity class. How do I easily call it?
I am also trying to use:
implementation project(':app')
but it is not working--I get Gradle errors.
First off, NOBODY should call a function on an Activity. Because nobody should have an instance of the Activity (an instance of Context sure, but not the activity). Secondly, you can't have a cycle in your build graph. If module A depends on B, then B can't depend on A. Refactor your code so this isn't necessary.
We create a library which entry point is an Activity. There's a lot going on inside, and finally we need to return something to the main module. We want also to enter to this Activity from the notification. This can be done through the Indirect Activity.
Now there are 4 options for passing data back to the main module:
startActivtyForResult
Passing the Intent to be called after the end of the process in the module
StateFlow issue that will allow you to observe the process status in the module
Passing callbacks to the library and storing them somewhere in Singleton.
What would you choose?
I am working on application which is divided by features into modules.
App structure looks like so:
app (application)
MainActivity
MainApplication
featureOne (module)
FirstActivity
featureTwo (module)
SecondActivity
Feature modules cannot depend on each other, but I can edit them freely.
My goal is to navigate from FirstActivity to SecondActivity.
I cannot use startActivity(Intent(com.example.featureTwo.SecondActivity)), because SecondActivity class is not visible to FirstActivity(different independent module).
Question is what is the proper way to navigate from FirstActivity to SecondActivity?
I was thinking about using:
Broadcast - I would send broadcast from FristActivity and register broadcast receiver, in featureTwos manifest. From broadcastReceiver I would launch SecondActivity.
Deep Links - Similar to the broadcastReceiver.
Creating function in application class and an enum within app package containint activies I want to launch. I would call this function whenever I want to launch activity like so: launchActivityFromDifferentModule(EnumWithActivities.SecondActivity).
Which method should I use, which one I shouldn't and why?
have many approaches for lunching activity in another module
Reflection
Props: easily navigate to another class without define class in the app module.
Cons: reflection is running at runtime.
DeepLink
Props: create a unique link for any item in another module like openFragmentA, addCreditToUserAccount, etc.
Cons: not have a serious concern.
Broadcast
Props: declare determined activity in the app module (if have nav module setup inside).
Cons: need more time to change and define another module.
Conclusion
Deeplink is suitable for a dynamic feature (onDemand feature)
Broadcast is suitable for a main feature, permanently feature
Reflection is suitable when not confident for a feature like A/B test feature
The approach Google recommends by the moment is to use reflection to navigate between feature modules.
In my case, I have created a new navigation module to hosts the different classes of navigation. My App module depends on this module so every feature module can access the navigation.
I use a file with functions to instantiate Intent's through reflection:
private const val PACKAGE_NAME = "com.your_app_package_name"
private fun intentTo(className: String): Intent =
Intent(Intent.ACTION_VIEW).setClassName(PACKAGE_NAME, className)
internal fun String.loadIntentOrNull(): Intent? =
try {
Class.forName(this).run { intentTo(this#loadIntentOrNull) }
} catch (e: ClassNotFoundException) {
null
}
Note as the loadIntentOrNull String extension is internal, it will be only available in the navigation module.
Then you can create objects for each module to handle the navigation.
object SearchNavigation : Navigation {
private const val SEARCH = "com.your_search_activity_package"
override fun getIntent(): Intent? = SEARCH.loadIntentOrNull()
}
The Navigation interface just defines the getIntent method:
interface Navigation {
fun getIntent(): Intent?
}
Then you can inject this Navigation object in every module as your feature module depends on the app module, and at the same time, it depends on the navigation module.
Following your structure, it would be something like this
app (application)
MainActivity
MainApplication
featureOne (module)
FirstActivity
featureTwo (module)
SecondActivity
navigation (module)
featureOneNavigation (object)
featureTwoNavigation (object)
The app module will depend on the navigation module. featureOne and featureTwo will depend on app (because they are feature modules) and will have access to the navigation.
You can also avoid creating a new module for the navigation and implement this just in the app module.
This method can be also applied to instantiate Fragments, so you can have your host activity with a DrawerLayout in your app module, and each of its Fragments in a different module.
Add your modules in the build.gradle file
android {
...
}
dependencies {
..
implementation project(':featureOne')
implementation project(':featureTwo')
}
Note:
Libraries/modules should be developed in a way that they are independent, and they should be divided by functionality.
I'm currently experimenting with creating my own libraries (aar) and I've come to the point that I have a library with an activity in it up and running in my project. But I have one small problem that I can't solve: how do I send a String from my Mainactivity to the activity that I created in my library?
I know that if I was working with activities that belong to the same project I could just create an intent in activity 1, add a putExtra with the String, start the activity and dig up that intent in activity 2. But the activity in my library doesn't know the activity in my project exists, so that doesn't work.
I could put the info in my SharedPreferences, but I'd like to avoid that.
Any help? I've been searching the web and I've found a solution for the other way around, but sadly that's useless to me :-)
The activity in your library doesn't have to "know" the first one... It has just to check if the intent contains the extra you sent:
if(getIntent().getStringExtra("yourStringExtraName") != null){
//Do your stuff here
}else{
//Do stuff when there isn't your string
}
Have a Android technique question for those seasoned vets out there... I want to create a library that dynamically opens an activity. Furthermore, I want the project to be able to contain the activity and then pass this to the library. (This is all based around C2DM)
So, my project 'hotdogs' will have a reference to the library, and will tell it to open the activity 'TodaysToppings' and the library will open up the activity 'TodayToppings'. My other project 'Weather' will also extend the same library and tell it to open the activity 'TodaysForecast' and the library will open the activity 'TodaysForecast'.
Does that make sense?
Idea is simple. You can pass package name and action if necessary, or if it's main then Intent.ACTION_MAIN. So, your library can use Context to start new activity based on package name and action to open a specific activity. What about activity, it can be placed inside any other application, no need to pass it somewhere.
This is an old question but I came across this while trying to save the problem myself. I figured out a simpler way to do this without having to configure any strings or passing in any parameters.
String packageName = getPackageName();
Intent notificationIntent = getPackageManager().getLaunchIntentForPackage(packageName);
Now you have the intent you can add on extras, etc.