Get application context from a secondary module - android

I am working on a project that has one main module(here I have the activities and the controllers..) and some secondary modules where I have some calendar and other implementations.
In the main module I have a Application singleton class where I store the application context and I can get the app context statically from everywhere within my main module.
The question is how can I make another application class in the secondary module? Currently I am using circular dependencies between the main module and the module where I want the app context, and I don't like too much to use this approach.

#David Wasser wrote:
Why can't the code in the secondary modules call MainApp.getInstance() to get the application context? Obviously the secondary module is dependent on the main module so I don't see how this is a circular dependency.
If not, then pass the singleton application context from the main module to the secondary modules (either as a parameter in method calls or as a parameter in the constructor of the component in the secondary module. Then you won't have code in the secondary modules calling MainApp.getInstance(). In any case you cannot have another application class as there is only one application class.

Related

How to pass android Activity Context to Koin Module?

I checked few post but haven't found any query related to my problem.
My use case:
I have Dependency in my Activity which are dependent on the Activities Context and should not be allowed to be given Application Context as that can lead to Memory leak's.
single { (activity: Activity) ->
MyDependency(activity)
}
if use above solution in my case then it can solve the case for one Dependency but then my activity code will be to responsible to send param for each dependency which need's activity's context (beating the use case of dependency injection)
Is it possible to make some specific koin module's dependent on android activity context while other's on application context?
if i use androidContext(this) in my Application Class while starting koin then all module's requiring the Context will get() application Context.

How to navigate to activity located in different module

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.

Injecting mock presenters using Dagger2

My goal is to write instrumentation tests where the class under test is an Activity which has a mock presenter injected into it. I'd also like the presenter to have the same life as the activity that holds it. Finally, the mocks presenter that gets injected in the activity should also be able to be referenced from the test class so that the mock can be setup, verified etc... as part of the test.
Project setup:
I'm using gradle flavors to break the project into source sets that can be used for the different types of tests I'm trying to write. I have a mockRepo flavor where I have Dagger components and modules that create a "real" instance of all of my dependencies except for my repositories which I mock. I have an endToEnd flavor where I inject real instances of every dependency. The last flavor I'm attempting to add is a mockPresenter flavor which creates mock instances of the presenters so that I can truly isolate my views and ensure that when an activity's lifecycle methods run that the correct methods are called on the presenter.
The mockRepo and endToEnd flavors were pretty straightforward to write because in both cases I was able to extend the application class for testing and was able to swap out different dagger components in the application class which made it very easy to inject a mock repository.
The mockPresenter flavor is giving me problems. The issue is that I'm unable to inject a mock presenter into MainActivity without adding a method like
DaggerMainComponent getMainComponent() {
return mDaggerMainComponent
}
to my application class. I'd prefer not to do that because it seems like the application class shouldn't need to know about DaggerMainComponent at all and it's just a workaround.
Here's how the injection code for MainActivity currently looks:
#Inject
MainContract.Presenter mMainPresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMainComponent = DaggerMainComponent
.builder()
.appInjector(((MyApplication) getApplicationContext()).getAppComponent())
.mainModule(new MainModule())
.mainPresenterModule(new MainPresenterModule())
.build();
mMainComponent.inject(this);
mMainPresenter.setView(this);
}
What I like about doing things this way is that in onCreate I used the Dagger generated class called DaggerMainComponent to create an instance of component which guarantees that I will get a new Component which guarantees that I will get a new presenter every time onCreate runs. Also, I can get a reference to the component in onCreate and dereference it in onDestroy(). However, it also seems that it isn't possible to inject a mock MainPresenter because the only chance I have to do that is this here:
.mainPresenterModule(new MainPresenterModule())
MainPresenterModule only creates "real" instances of the presenter and if I were so use something like:
.mainPresenterModule(new MockPresenterModule())
then there would be no way to use a "real" version of the presenter for the production version of the app.
The workarounds that I've seen for this situation all seem to involve having a method on the application class that either return a component or a module. In the production version of the app the application class would return either a component that depends on modules that provide real instances or could even just supply the module itself. The test version of the app would have a test application that would extend the production application class and override some of the methods so when the test application was running it would return either the mock version of the component which would declare modules that would supply mocks or it would return the mock modules directly.
The other workaround I've seen uses an #ExposedForTesting annotation in the application class which seems like something I'd like to avoid.
Having the application class expose getter methods so that mock / real dependencies can be injected seems like a code smell to me because I can't see a real reason that the application class should know about the dependencies that should be injected into MainActivity. Also, it seems like you'd end up with one getter method per Activity or Fragment which would start to bloat the application class.
Is there a way to inject mocks into an Activity so that the application class doesn't play such a big part in providing the correct modules or components?

android.content.res.Resources$NotFoundException. Module, aar library

I have library module with string resource.
string.xml
<string name="lib_ver">1.0</string>
and method:
public static String getLibVersion(Context context){
return context.getResources().getString(R.string.lib_ver);
}
In my app application i include my module like aar library.
Everything work properly except 1 thing.
if i try to get lib version with lib method in activity class:
getLibVersion(getApplicationContext()) i get error:
android.content.res.Resources$NotFoundException: String resource ID
#0x7f02105b
But if i do in activity class, without call to library method:
getApplicationContext().getResources().getString(R.string.lib_ver)
There is no errors. Where is a problem? Thx.
just need to update gradle to 2.1.2
classpath 'com.android.tools.build:gradle:2.1.2'
getApplicationContext: Return the context of the single, global Application object of the current process. This generally should only be used if you need a Context whose lifecycle is separate from the current context, that is tied to the lifetime of the process rather than the current component. From android
That might be the reason that your ApplicationContext is throwing error! :)
More info:
Context and ApplicationContext are both instances of Context, but the application instance is tied to the lifecycle of the application, while the Activity instance is tied to the lifecycle of an Activity. Thus, they have access to different information about the application environment.
If you read the docs at getApplicationContext it notes that you should only use this if you need a context whose lifecycle is separate from the current context. This doesn't apply in either of your examples.
The Activity context presumably has some information about the current activity that is necessary to complete those calls. If you show the exact error message, might be able to point to what exactly it needs.
But in general, use the activity context unless you have a good reason not to.

Can an activity based application have no activity?

Assuming I have a shared activity class defined in a Library project, which does not change for any application using it and thus does not need to be subclassed, can I get a way with creating applications without subclassing this activity for them?
To better explain my question, say I have a single activity in a Library project:
public class LibActivity extends Activity {
...
}
And now I am creating an application using that Library project. Do I really need to create
public class AppActivity extends LibActivity {
// totally empty!
}
Only so that the application have its own activity to be referenced in its own AndroidManifest.xml?
Can I get a way with a minimalistic approach, in which I subclass the activity only if I need to modify the library's activity core behavior?
Here is the fully qualified answer:
Yes, an activity based application doesn't have to derive an activity from the library's activity. The application simply uses the library's activity verbatim, unmodified.
Yes, I can get a way with a minimalistic approach, in which I subclass the activity only if I need to modify the library's activity core behavior.
I have been able to verify this with an AndroidManifest.xml that is identical in both the library and the application. It would be interesting to see whether some of this redundancy can be eliminated. I will experiment with this and report back.
UPDATE: Sure enough, it is possible to create a perfectly running application in which the only activity is defined in the library and the library's AndroidManifest.xml doesn't have any <application> or <activity>! This is possible if the application's AndroidManifest.xml has them.
You can reference library Activity classes directly from your application AndroidManifest.xml. Just specify the fully qualified name like so android:name="com.example.LibActivity"

Categories

Resources