Populate list with instantiated classes dynamically with Kotlin on Android - android

I have an abstract class and all classes in some package derive from it. Is there a way to create a list that dynamically instantiates all these classes that reside in some package when using Kotlin on Android?
Here is an example:
com.example.service
BaseService
com.example.service.emailservice
GmailService
OutlookService
All classes in com.example.service.emailservice derive from BaseService abstract class that resides in com.example.service. I want to create a list that contains GmailService and OutlookService objects. I could instantiate them manually and add them to a list, but in future I may add new service, lets say YandexService, which should appear in list too. This requires manual instantiation again. Is there a way to automatically instantiate classes that reside in some package?

There are two mayor ways to do it.
The first one - easier and dirtier one called Reflection.
You may find a lot of examples in java. Not so much in Kotlin though, but from what I have read here it is more than possible.
Usage of Reflection though, is not recommended in production, and generally considered as a bad approach to fulfill something.
The second way is Annotation Processors - this way is way harder. It is considered a clean way to do such tasks though and it is a general standard for code generating techniques. This way you can do all kinds of magic if you put your mind to it. Here is nice article about how to do it.
Generally I would recommend to use semiautomatic approach.
For example:
In your base service with init method
abstract class BaseService {
abstract fun init(): BaseService
}
Make all your services implement this BaseService
class Service : BaseService() {
override fun init(): BaseService {
return Service()
}
}
...
And then just create a list of classes
val services = listOf(Service(), Service1(), Service2())
and to init them do
services.forEach { it.init() }
This is not very different from what you have and may require some logical and architectural changes in your Services and App overall, but it won't be dirty and it won't require tremendous learning curve and time expenses.
Hope it helps.

use this library
add to gradle.build
implementation 'org.reflections:reflections:0.9.11'
then in your package com.example.service create a class from where you will be creating your Services, let's call it ServiceFactory here is implementation:
package com.example.service
import org.reflections.Reflections
class ServiceFactory {
init {
val reflections = Reflections(javaClass.`package`.name)
val subTypes = reflections.getSubTypesOf(BaseService::class.java)
val yourServices = subTypes.map { it.getConstructor().newInstance() }
yourServices.forEach { println(it.javaClass.name) }
}
}
fun main() {
ServiceFactory()
}
out:
com.example.service.emailservice.OutlookService
com.example.service.emailservice.GmailService
Something you were looking for ?
EDIT: of course you can instantiate this services from different place, but in that case you need to hardcode Reflections("com.example.service")

Related

Passing custom object between activities

I'm following this tutorial on how to use MQTT with Android Studio. In it, they created an MQTTClient class that uses the MQTTAndroidClient library. I want to pass the MQTTClient class from one activity to another. Any suggestions how I might do this? I'm new to Android dev and I'm trying to negotiate serializable/parcelable tools without much know-how. Thanks!
P.S. I'm developing in Kotlin
Passing complex classes between activities is generally a bad idea. For that kind of usage you should use a Singleton and store it in your Application class or something like this.
I don't recommend you to pass the entire MQTTClient through Activities.
I'd suggest you to read this Dependency-Injection manual is more or less what you need, normally you'd use a dependency injection library/framework to do what you want but since is complex to set-up most of them I'd follow the link I've linked before.
Sample code :
// Container of objects shared across the whole app
class AppContainer {
val mqttClient = MQTTClient() //<-- Initialisation
}
Then create a custom Application
class MyApplication : Application() {
// Instance of AppContainer that will be used by all the Activities of the app
val appContainer = AppContainer()
}
Do not forget to add it to manifest.xml with the name attribute.
And then from your Activity you need this MQTTClient you use :
val appContainer = (application as MyApplication).appContainer
val mqttClient = appContainer.mqttClient
What #Ben-J suggested is also a good point, to create a Singleton in kotlin you can use the object keyboard.

Dagger bound multiple times : Trying to create a fake implementation

Hi I'm trying to create a fake presenter of my activity I have my module let's call it Activity1module where I have set all of the presenter, use-case, everything and it works perfect, but when trying to create a screen which uses that exact activity with a fake presenter it says I've bound multiple times that presenter.
What I've did is :
#Module
abstract class Activity1Module{
#Binds
abstract fun providePresenter(impl: PresenterImpl) : Activity1Contract.Presenter
.....
}
Then I have created a new module FakeActivity1Module and it's like this :
#Module(includes = [Activity1Module::class])
abstract class FakeActivity1Module {
#Binds
abstract fun bindsFakePresenter(impl: FakePresenterImpl): Activity1Contract.Presenter
.....
}
But looks like it doesn't like this way, is there any way to use the fake one instead of the production one without creating #Named or touching production code?
Dagger doesn't have any capability to have one module override another's bindings directly. FakeActivity1Module and Activity1Module should include similar bindings but neither should be in the other's includes list.
You can, however, extract common bindings to an Activity1CommonModule (named as you'd like) and have both Activity1Module and FakeActivity1Module include that module. That would allow you to avoid repeating yourself as much as possible, at the conceptual cost of some indirection between files.
You can even include that Activity1CommonModule as a nested interface (or abstract or static class) within Activity1Module; you'd still need Activity1Module's includes to contain its own Activity1Module.Activity1CommonModule.class, but you'd have the benefit of centralizing all of Activity1Module's bindings in a single file, plus the benefit of easily seeing through diffs (git diff, p4 diff, etc) when a binding moves in or out of the common set.
Same class cannot be injected 2 times and it produce bound multiple times error. So for the FakeActivity1Module instead of create new injection. Try to override Activity1Module and apply your changes.
class FakeActivity1Module: Activity1Module {
overrides fun providePresenter(): Activity1Contract.Presenter = FakePresenterImpl()
}
And you use FakeActivity1Module into your component for testing and it should work as expected.

Inject into arbitrary Logic class that is not instanciated by Hilt

I'm currently migrating an app from anko-sqlite to room as anko has been discontinued a long time ago. In the process of doing so I introduced a repository layer and thought I would try to introduce dependency injection to get references to the repository instances in all my classes.
Prior to this I used a singleton instance that I just shoved into my application classes companion object and accessed that from everywhere.
I managed to inject my repositories into Fragments, Workmanager and Viewmodels. But I do now struggle a bit to understand how they forsaw we should handle this with arbitrary logic classes.
For instance my workmanager worker calls a class that instantiates a list of "jobs" and those need access to the repository. The worker itself actually bearly even does need access to the repositories as all the work is abstracted away from it.
How can I make something like this work
class Job(val someExtraArgINeed: Int) {
#Inject
lateinit var someRepository: SomeRepository // <= this should be injected when the job is instanciated with the constructor that already exists
}
For Activities and so forth we have special annotations #AndroidEntryPoint that makes this work. However, the documentation makes it unclear as to how we should get our instances from any other class that isn't an Android class. I understand that constructor injection is the recommended thing to use. But I do not think it would work here for me without an even more massive refactor required than this already would be.
If I understand your question correctly you can use hilt entry points like this
class CustomClass(applicationContext: Context) {
#EntryPoint
#InstallIn([/*hilt component that provide SomeRepository*/ApplicationComponent]::class)
interface SomeRepositoryEntryPoint {
fun provideSomeRepository(): SomeRepository
}
private val someRepository by lazy {
EntryPointAccessors.fromApplication(applicationContext, SomeRepositoryEntryPoint::class.java).provideSomeRepository()
}
fun doSomething() {
someRepository.doSomething()
}
}

What is the proper way to provide Context to a DataSource with Clean Architecture, MVVM and Koin?

The bounty expires in 2 days. Answers to this question are eligible for a +100 reputation bounty.
Ethansocal is looking for an answer from a reputable source:
I have this same problem, and would like to know what the best method is.
I am developing an Android application with Kotlin in which I need to get the current location of the mobile device. I've already found a way to do it in various examples, but I don't know how to integrate this logic according to Clean Architecture with MVVM.
In my architecture I have the following layers: Presentation, UseCase, Data, Domain and Framework. I have the presentation layer organized with the MVVM pattern. Also I use Koin for dependency injection.
I get all the data that my application needs from the DataSources that are in the Framework layer. For example, data obtained remotely or from a database, or data provided by the device (location).
Here is an example of the files involved in obtaining the location from the ViewModel:
ConfigurationViewModel (Presentation layer):
class ConfigurationViewModel(private val useCase: GetLocationUseCase) : ViewModel() {
fun onSearchLocationButtonClicked() = liveData<Resource<Location>>(Dispatchers.IO) {
emit(Resource.loading())
try {
emit(Resource.success(data = useCase.invoke(UseCase.None())))
} catch (exception: Exception) {
emit(Resource.error(message = exception.message))
}
}
GetLocationUseCase (Usecase layer):
class GetLocationUseCase(private val locationRepository: LocationRepository) :
UseCase<Location, UseCase.None>() {
override suspend fun invoke(params: None): Location = locationRepository.getLocation()
}
LocationRepositoryImpl (Data layer):
class LocationRepositoryImpl(private val locationDeviceDataSource: LocationDeviceDataSource) :
LocationRepository {
override suspend fun getLocation(): Location = locationDeviceDataSource.getLocation()
}
LocationDeviceDataSourceImpl (Framework layer):
class LocationDeviceDataSourceImpl(private val context: Context) : LocationDeviceDataSource {
override suspend fun getLocation(): Location =
LocationServices.getFusedLocationProviderClient(context).lastLocation.await()
}
As you can see, in LocationDeviceDataSourceImpl I need the context to get the last location. I don't know what is the best way to provide the context to the DataSource. I have seen several examples but I want to understand what is the best way to do it.
I have seen the following options:
Use AndroidViewModel, to provide the context of the application to the UseCase, to provide it to the Repository to finally provide it to the DataSource. But I am not sure if it is a suitable way, if it is safe and if it maintains the sense of architecture. Based on Alex's answer
The other option that I have seen is to inject the androidContext through Koin to the DataSource, which is another way to provide the context of the application. In this way it would not be necessary for the context to go through the ViewModel, the UseCase or the Repository. Based on maslick's answer
What would be the appropriate way to integrate this logic according to my architecture and why? Or is this problem because of my architecture?
Thanks a lot for your time.
I would simply inject the Context in the Framework layer!
and the reason for that is quite simple, Context class itself is a part of the Android framework, and your framework layer should autonomously be able to access such classes.
Also, note that you don't even need to have a wrapper class around Context when you inject it in your framework layer. Why? because you aren't going to unit test your framework layer classes. Why? because it would use a mix of Android framework classes or some 3rd party libraries(including other Android libs), that are a black box to us and as a software development principle:
We don't mock dependencies we don't own.

Dagger singleton vs Kotlin object

To define a singleton, should I use Kotlin object declaration or to make an ordinary Kotlin class and inject it using dagger? In my opinion the first option is definitely easier but there may be a reason to use dagger in this situation that I'm not aware of.
Option 1 (notice object keyword):
object SomeUtil {
// object state (properties)
fun someFunction(number: Long) {
// ...
}
}
Option 2 (notice class keyword):
class SomeUtil {
// object state (properties)
fun someFunction(number: Long) {
// ...
}
}
#Module
class AppModule {
#Provides
#Singleton
internal fun provideTheUtil() = SomeUtil()
}
class MainActivity : BaseActivity() {
#Inject internal lateinit var util: SomeUtil
}
UPDATE 2019-07-03
#Blackbelt said in comments that we should prefer option 2 for testability. But libraries like MockK can mock objects too. So do you still think option 2 is the preferred one?
You might want to reconsider the need of NumberFormatUtil being a singleton. It might be cheaper if you use #Reusable with Dagger or even a factory without any scope directly.
If NumberFormatUtil is fairly simple and only provides a few utility methods, no state and no need for mocking in tests, you could use an object implementation, maybe using #JvmStatic for Java-inter-operability. But then you could go for global utility (extension) functions as well:
package xyz
fun formatNumber(number: Long) {
// ...
}
fun Long.format() = formatNumber(this)
You should use option 2.
In software engineering, the singleton pattern is a software design
pattern that restricts the instantiation of a class to one "single"
instance. This is useful when exactly one object is needed to
coordinate actions across the system.
From: Singleton Pattern
So, a singleton is single instance in a scope. In case of Android, it is virtual machine instance running the app. In case you need custom scopes, you have to use option 2 only.
But, if you have only static methods inside the object you want to inject its better to keep them as global methods and even get rid of object. No need to inject anything. It is similar to a java class with only static methods (I mentioned this point as it is a usual way of creating Utility classes).
However, if the object also has some state. I would recommend going dagger way. The object way does not provide with dependency injection. It only creates a Singleton. Your purpose for using dagger is dependency injection.

Categories

Resources