I'm using Dagger1 and I have a list of Jokes. In my AwesomeJokeModule I provide a List. The list is provided by JokeDataLayer.getJokeCache(). The thing is, if the Cache isn't build up yet, the getJokeCache() method hits the DB getting a huge list of jokes. This could take a while, and while Injecting my Jokes into my Activity, this can cause a slow down since the Jokes are a member of my Activity. What's the best way to inject a member into something like an Activity asynchronously?
Some things I've thought of, was to return an empty list right away if the cache isn't built yet, and then somehow try to communicate that the cache has been updated? But it just feels like I'm circumventing Dagger/DI. Any advice or ways to do this?
This is where the Lazy<T> is for. Consider using LAZY INJECTION
class GridingCoffeeMaker {
#Inject Lazy<Grinder> lazyGrinder;
public void brew() {
while (needsGrinding()) {
// Grinder created once on first call to .get() and cached.
lazyGrinder.get().grind();
}
}
}
This lazyGrinder object will only be initialized when you need to use it.
Related
Currently, I have a database manager class that handles all operations to the database like this:
class DatabaseManager(val context: Context) {
private val db = Firebase.firestore
//Other functions, etc.
}
It makes use of the context passed in by different activities to perform functions to the database. The thing is, every single activity that requires database functions have to instantiate this manager class first, then call the functions. I would like to make use of the Singelton design pattern to make it such that all the activities will only use a single instance of the class. I believe kotlin's objects can do this, however I also need to be able to pass in the context of the activities into this manager class. Any assistance is appreciated, thank you!
I would recommend not doing that. The problem with Singletons is that they make code hard to test, you can't fake out the database. And for a database this is a particularly bad problem, as setting up all the right fake data can be painful. Instead, take a look at injection. It can do the same thing (make a single instance shared between everyone who needs it), but it manages that global state rather than having the classes themselves manage it via a static reference, passing it in (generally via the constructor) to whoever needs it. This makes it easy to provide an alternative or mock database when needed for testing. Injection used to be a bit painful to set up, but Hilt makes it a lot easier these days.
So currently I have a Dao with a function that emits a Flow<>
#Query("SELECT * FROM ${Constants.Redacted}")
fun loadAllContacts(): Flow<List<Redacted>>
I am calling this from a repository like so
val loadAllContacts: Flow<List<Redacted>> = contactDao.loadAllContacts()
I am injecting the repository into the viewModel's constructor, and then at the top of my viewModel I have a val like so
val contacts: LiveData<List<Redacted>> = contactRepository.loadAllContacts.asLiveData()
Which is being observed in my Activity like so
viewModel.contacts.observe(this) { contacts ->
viewModel.onContactsChange(contacts)
}
My thinking is that the Flow is converted to a LiveData, and then I can observe this LiveData from my activity and kick off this function to actually update the viewModel upon the data being updated.
For now onContactsChange just looks like
fun onContactsChange(list: List<Redacted>) {
Timber.i("VIEW UPDATE")
}
The problem is that I only see this Timber log upon opening the activity, and never again. I verified that data IS going into my database, and I verified that an insert occurred successfully while the activity & viewModel are open. But I never see the log from onContactsChange again. When I close the activity, and reopen it, I do see my new data, so that is another reason I know my insert is working correctly.
I would like to add that I am using a single instance (singleton) of my repository, and I think I can verify this by the fact that I can see my data at all, at least when the view is first made.
Figured it out:
Note: If your app runs in a single process, you should follow the singleton design pattern when instantiating an AppDatabase object. Each RoomDatabase instance is fairly expensive, and you rarely need access to multiple instances within a single process.
If your app runs in multiple processes, include enableMultiInstanceInvalidation() in your database builder invocation. That way, when you have an instance of AppDatabase in each process, you can invalidate the shared database file in one process, and this invalidation automatically propagates to the instances of AppDatabase within other processes.
It's a little bit hard to follow your question, but I think I see the overall problem with your Flow object not updating the way you want it too.
Following this quick tutorial, it seems that first you should declare your Flow object inside your Repository the same way you're already doing
val loadAllContacts: Flow<List<Redacted>> = contactDao.loadAllContacts()
and have your VM 'subscribe' to it by using the collect coroutine which would then allow you to dump all this data into a MutableLiveData State
data class YourState(..)
val state = MutableLiveData<YourState>()
init {
contactRepository.loadAllContacts().collect {
if (it.isNotEmpty()) {
state.postValue(YourState(
...
)
}
}
}
that your Activity/Fragment could then observe for changes
viewModel.state.observe(.. { state ->
// DO SOMETHING
})
P.S. The tutorial also mentions that because of how Dao's work, you might be getting updates for even the slightest of changes, but that you can use the distinctUntilChanged() Flow extension function to get more specific results.
I have this code which provides the logic of this solution, but I'm aware that collecting an instance from within the constructor is dangerous.
Does anyone know a better solution?
The goal is to collect all instances that are extending this class. My preferred solution is the one the uses as little resources as possible (libraries, cpu, ram) although I know that there will always be a tradeoff.
I've tried something with reflection, annotation, classpath search but non of them seemed to be the silver bullet.
class Parent{
private companion object{
var childs = arrayListOf<Parent>()
}
constructor(){
childs.add(this)
}
}
If the intention is to collect all instances you should anyway somehow get those instances upon their creation (will it be the constructor or factory method, if any). Also you should somehow store them (maybe LinkedList is a better choice). So your approach seems reasonable.
But remember that within this approach these instances will be linked from a static place, which means that they wouldn't be garbage collected. If you don't want that, you could store them using a WeakReference (more precisely a collection of WeakReference<Parent>'s).
I don't know if this is a stupid question. This may defeat the purpose of LiveData/ViewModel.
Can I make the LiveData static? My reason is I have a listener from a Service which updates the information. So I need to have a way from a service to "set/change" the LiveData.
I used to do following and it works:
1. Service changes the DB
2. ViewModel listens to the DB change
3. UI updates from the liveData change
I found this way is too slow. To increase the performance, I want something like:
1. Service changes the class object directly
2. ViewModel listens to the the class object changes
3. UI updates from the liveData change
In order to achieve what I want, either I need to make the MutableLiveData static or make the ViewModel class to share the same instance of ViewModel between Activities.
Is this good idea?
public class MyViewModel extends AndroidViewModel {
// Note: this MutableLiveData is static
private static MutableLiveData<MyModel> mutableLiveData;
public MyViewModel(#NonNull Application application) {
super(application);
}
LiveData<MyModel> getLiveDataList() {
if (mutableLiveData == null) {
mutableLiveData = new MutableLiveData<>();
loadDataFromDb();
}
return mutableLiveData;
}
private void loadDataFromDb() {
// load data from DB
// mutableLiveData.setValue(MyModelFromDb); // Omit the real implementation
}
// Note: this method is static
public static void setData(MyModel newData) {
mutableLiveData.setValue(newData);
}
#Override
protected void onCleared() {
super.onCleared();
}
}
The whole point of ViewModel from Android Jetpack (as opposed to other versions) is for the ViewModel to be lifecycle aware and perform magic like destroying itself when observer is destroyed (activity/fragment), or surviving configuration changes (for example, orientation) without initialising itself all over again thereby making it much easier to deal with issues related to configuration changes.
So if you made the ViewModel or LiveData static you would actually beat their purpose and most likely leak ViewModel's data, though the need to do this is understandable. So this requires you to engineer your way around it, and the first way you mentioned is probably the best way you can do it. I don't understand why you have an issue with the first solution. The way I see it, it provides the best user experience:
You init ViewModel in your fragment or activity in onCreate and add an Observer to the data.
If database already has some data, your observer will receive it instantly and UI will be updated with existing data instantly.
Service makes the API request and changes the DB
DB changes triggers an update to the data in ViewModel
Observer refreshes received data and you pass this to your views/adapters
UI updates with latest data with some nice animations that indicate addition/removal of items.
From what I can see it cant get better than this. Since your question is from months ago, I am curious to know what you ended up doing?
I think if MyViewModel will have lots of LiveData fields it will grow with large amount of getters and setters. And what even worst, as for me, you will break the testablity of your code, because if you will create a new instance of MyViewModel you will expect that your LiveData objects are stateless at this point of time, but as it's a static object you don't know in what exactly state it is after simple creation.
As well static methods can't be overriden. And about fields: if you will want to have common field, suppose errorMessage, in class A and class B while both of them extend class C(which contains your common field) you can have unexpected behavior. On the other hand you can duplicate this code in other classes(what is bad).
The memory issue: if a large number of static variables/methods are used. Because they will not be GC until program ends.
But it just my opinion.
I’m new to Dagger 2. I have this scenario, I wan't to inject an object across my app (in presenters, in api)
I do not have a way to provide it initially. It is not created till after authentication at some stage in my app.
From the documentation http://google.github.io/dagger/
I see Lazy loading might be a way to solve this e.g
#Inject
Lazy<Grinder> lazyGrinder;
and then get the value like this using:
lazyGrinder.get().grind();
My questions are:
Can I safely swap the object after this with a new one?
Are there any other recommended ways to do this?
Thanks
This isn't a good match for Lazy. Lazy is a great way to delay expensive object initialization, but it implies some semantics that you don't want or need, particularly regarding the "safely swap" behavior you want.
To put it simply, Lazy is a Provider wrapper that memoizes locally:
If you never call get, Dagger never creates the object in question.
The first call to get creates and stores the object instance.
The second call to get returns the same instance, and so on forever, regardless of whether the object was marked as Singleton.
This makes Lazy an excellent choice for an expensive object that would otherwise be a field (but may never be used). However, if the reference is likely to change (as your will), Lazy will simply be confusing: It will store the value at first use and never locally update, so multiple out-of-date copies might be floating around in your application regardless of what the "right" value is at any given time.
To borrow the use of Grinder from your example, better solutions include:
Using a #Provides method that returns a field in a Module, which can be updated later. You'll need to inject Provider<Grinder> for every long-lived object instance, because injected references to Grinder alone won't update. This still might be the best bet if you have a lot of short-lived objects.
The reference is implicitly singleton, but is not annotated as such, because you're controlling the instance yourself. Dagger will call your getGrinder method frequently.
#Module public class YourModule {
private Grinder grinder;
public void setGrinder(Grinder grinder) {
this.grinder = grinder;
}
#Provides public Grinder getGrinder() {
return grinder;
}
}
/* elsewhere */
YourModule module = new YourModule();
YourComponent component = DaggerYourComponent.builder()
.yourModule(module)
.build();
/* ... */
module.setGrinder(latestAndGreatestGrinder);
As EpicPandaForce mentioned in the comments, create/bind a singleton GrinderHolder, GrinderController, or AtomicReference object that provides the current instance and allows for updating. That way it's impossible to inject a Grinder directly, but easy and obvious to inject the object that fetches the current correct Grinder. If your singleton GrinderHolder implementation doesn't create the Grinder until the first time you ask for it, then you have effectively created a Lazy singleton on your own.
If you aren't able to provide the object at the time of Component creation, don't add it to your Component graph! That is asking for confusing graph dependencies and inconsistency. A better solution to what you are considering is a #Subcomponent approach, which allows you to create a new component which inherits the dependencies from the parent, but also adds new one. Here's an example:
#Component
interface RegularComponent {
#AppInstanceId String appInstanceId(); // unique per app install; not related to logging in
AuthenticatedComponent newAuthenticatedComponent();
}
#Subcomponent
interface AuthenticatedComponent {
Set<Friend> friends();
#AccountId String accountId();
}
Here, the #AccountId in the subcomponent could use the appInstanceId to provide the account ID (if it needed to) since the Subcomponent shares dependencies with its parent component.
If you need to supply state to your modules for the subcomponent (with the accountId, auth token, etc) feel free to pass it in as a parameter to the #Module and store it in a private final field. You can read more on how to supply subcomponent modules in the documentation.