I have been quite inquisitive on the reason why an abstract Dao method is used to access the methods in the the Dao interface in android room database
it is not just the Room, Retrofit and other libraries use this pattern too, it is called Programming to an Interface. Instead of just creating a concrete implementation you just specify the stuff you want to do and they provide you with an implementation that will behave as you requested.
For further study you can check this article: https://tuhrig.de/programming-to-an-interface/
Related
Please someone explain me why when we use dependency injection and initialize the object in the module the provides function return type is interface but in body function we return the actual object. This is example
#Provides
#Singleton
fun providePrefsManager(#ApplicationContext context: Context): PrefsManager {
return PrefsManagerImpl(context)
}
Why here we return PrefsManager instead of PrefsManagerImpl ?
Code to the interface, not the implementation. #Provides methods provide according to their return values, so if you return PrefsManagerImpl, Dagger will only know how to inject PrefsManagerImpl. By returning PrefsManager, Dagger lets you inject PrefsManager directly, so the injecting class doesn't need to be aware of PrefsManagerImpl or any other implementation at all.
More specifically to dependency injection: The concept behind dependency injection is that, for the class you're writing, it's the caller or DI framework that controls which instance or implementation your class receives. This is an "inversion of control" compared to a self-contained class that maintains complete control of which classes or dependencies it uses.
As such, the class you're writing should be as general as possible when specifying its dependencies, which gives you flexibility about which implementations you can supply.
For example: If you need a sort algorithm, it would defeat the flexibility of dependency injection if you always asked specifically for a hypothetical MyBinarySortImpl; instead, you should make your request more general, such as injecting an interface like BinarySorter or Sorter (both also hypothetical). Your caller or dependency injection framework can still supply a MyBinarySortImpl, but by being as general as possible you also free your caller to supply a WellTestedNativeBinarySortImpl or a VeryFastRadixSortImpl. If your implementation needs to be a binary sort, you can specify that, and if it doesn't you can leave it general.
In your specific case, your #Provides method provides a binding of PrefsManager; the implementation happens to be a PrefsManagerImpl. However, the class that consumes PrefsManager is asserting that it doesn't need anything specific to PrefsManagerImpl, it can work when it only uses the interface as described through PrefsManager. That injecting class can now be tested using a FakePrefsManager or a mocking-framework-created mock(PrefsManager), or even a wrapper you could write like LoggingPrefsManager(PrefsManagerImpl(context)). By operating through interfaces rather than implementations, it keeps your options open.
The Room persistence library defines databases to provide its Daos.
#Database(...)
abstract class DbImpl : RoomDatabase() {
abstract val daoImpl: DaoImpl
}
How could I provide all DAOs in Dagger, without the need to provide these manually with a Module?
#Provides
fun provideDaoImpl(
db: DbImpl,
) = db.daoImpl
You can declare the Room database as a component dependency, thus implicitly provide any declared DAOs from it.
The benefit of this approach is that you save some boilerplate as you don't need the #Provides methods wrapping every DAO. The downside is that the Room database now needs to be created along with your component to link it as a dependency which may incur unnecessary work at app-startup.
I'd stick with the #Provides methods and the boilerplate module. Your DAOs hopefully don't change that much that this would be hard to maintain and it gives you the benefit of being able to create the database lazily when needed. Just make sure that you avoid scoping your DAO wrapper methods, as Room already does some internal double-locking.
In Android sample codes/codelabs, Daos inside RoomDatabase are defined as
abstract fun genericDao(): GenericDao
and when you need to access the dao's methods, you call
database.genericDao().genericFun()
Why is it implemented like this, and not like
abstract val genericDao: GenericDao
database.genericDao.genericFun()
? Is it wrong to do it the second way?
Link to Codelab
Ok, i checked Decompiled bytecode and the only difference is that first implementation is named as
subscriptionStatusDao()
and the second one as
getSubscriptionStatusDao()
So basically, no difference at all.
I have seen this implementation in Room database.
There is an abstract class AppDatabase -
#Database(entities = {Task.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract TaskDao taskDao();
}
But when you create an Object of this AppDatabase class, you do something like this -
AppDatabase appDatabase =
Room.databaseBuilder(context, AppDatabase.class, "something").build();
My questions are -
How can you pass an Abstract class directly like this without the Override methods defined ?
Like, normally we don't use and pass abstract classes like that, if you do something like this in IDE, it throws an error.
Which design pattern is this and how Room handles this type of things internally ?
1) The idea of using abstract classes is to have a sort of contract between the developer and room. We use abstract classes (or interfaces) because the implementation of those Dao methods will not be provided by us the developers but by Room itself.
2) It is a Builder design pattern, this design pattern is generally used when we have many options for how we'd like to create our final object and the pattern provides a more maintainable api for doing so. The example you provide is just a basic initialization of the database, but we can actually set many params whilst building the database class. For instance we can add the following option when building the database in order to tell it to delete everything and start again in case our database schema changes:
.fallbackToDestructiveMigration()
How Rooms handles thing internally is a bit of a hard question, but in general terms it is an abstraction layer of SQL apis provided by android itself, it will use your contracts (abstract classes or interfaces) for the Daos and the Database in order to create implementations for all of those abstract methods defined in those classes. Once you've setup everything and built your project the first time, Room will generate a bunch of _Impl classes that will implement those abstract methods. For instance, if you have a UserDao, it will generate a UserDao_Impl class that extends (or implements if you've used an interface) the original UserDao and it will provide those implementations. What it does internally will depend on the method, but it's basically using the SQLite api provided by Android.
Im very statisfied with SugarOrm for Android, but I ran into an issue. I'm using it with GSON for Json serializations and I want to get rid of SugarRecord's id attribute. I know I should use #Table annotation and later exclude specific field from serialization using #Expose, but after annotating class with #Table I cannot use .save(), delete(),... methods on the object, as it is the case extending SugarRecord. I don't know how to persist objects using #Table annotation.
I found the documentation here very limited.
The document hasn't been updated for the annotation based persistence yet. The methods save(), delete() will be available as static methods on SugarRecord class.
So instead of doing this:
object.save()
You'd be doing this:
SugarRecord.save(object)
Check out some tests here to understand better.
https://github.com/satyan/sugar/tree/master/example/src/test/java/com/example/sugartest