2

I have multiple viewmodels that access a single repository (one activity and the rest fragments).

AdminActivityViewModel

AdminListUsersViewModel

AdminUserTransactionsViewModel

... and a few more

My AdminRepo class has multiple constructors so that I can pass callback methods from the ViewModel

 public AdminRepo(Application application, AdminActivityCallback callback) {
        this.callback = callback;
        BaseApplication baseApplication = (BaseApplication) application;
        RetrofitClient client = baseApplication.getRetrofitClient();
        adminService = client.getRetrofit().create(AdminService.class);

        SharedPrefManager sharedPref = SharedPrefManager.getInstance(application);
        AuthHeader authHeader = new AuthHeader(
                sharedPref.getIdToken(),
                sharedPref.getIdClient(),
                sharedPref.getUserEmail()
        );
        client.setAuthHeader(authHeader);
    }
 public AdminRepo(Application application, AdminListUsersCallback callback) {
        //Exact same code in constructor as above ^
 }
 public AdminRepo(Application application, AdminUserTransactionsCallback callback) {
        //Exact same code in constructor as above ^
 }

And in each of the ViewModels I am creating an instance of the AdminRepo (which is probably a bad practice) but I don't know how I can improve this.

public class AdminActivityViewModel extends AndroidViewModel implements AdminActivityCallback

   public AdminActivityViewModel(@NonNull Application application) {
        super(application);
        repo = new AdminRepo(application, this);
}

How can I design my AdminRepo and ViewModels so that they share exactly one repository without creating an expensive AdminRepo class everytime?

I have considered making my AdminRepo class a singleton with a .getInstance() method, but I'm getting SO MANY contradicting posts on how Repository classes SHOULD NOT be static or a singleton, which makes me extremely confused on what I SHOULD do.

https://stackoverflow.com/a/7464235/11110509

DIRTY DAVE
  • 2,523
  • 2
  • 20
  • 83
  • Why do you think creating a new instance in each VM is bad? There is nothing wrong with that. If you dont have a reason to use a Singleton then dont create one, if you do have a reason to make it then go ahead. There is no real right answer to this it just depends on the case. That being said if you need that many different callbacks then you probably can break up that repo further into individual repos and I wouldnt pass Application to them either, pass the info you need so you dont have a dependency on Android System – tyczj Mar 25 '21 at 13:37
  • Because creating unnecessary objects is bad? In class they teach you that you should always reduce the number of object creations. I feel like you can reuse the ```AdminRepo``` class instead of creating a new one everytime. – DIRTY DAVE Mar 25 '21 at 13:52
  • Unnecessary work also creates unnecessary complexity, its not like you are creating hundreds of instances in a matter of seconds. They get garbage collected after you are done with them – tyczj Mar 25 '21 at 13:57
  • I've already created separate repository classes for ```User```, ```Login```, and ```Admin``` entities. If I were to split ```Admin``` up I would have to create 5 more extra repository classes, ```AdminListUsersRepo```, ```AdminListTransactionsRepo````, etc. Also why is having a dependency on the ```Application``` bad? I can just have one instance of SharedPreferences and Retrofit inside the lifecycle of the app and just retrieve those instances instead of creating a new one. – DIRTY DAVE Mar 25 '21 at 14:05

1 Answers1

2

If all view models live at the same time then and repository does not keep any state you could share the same instance of repository and provide that instance for each view model. However there's no place for magic here - if you create an instance of repository, you have to keep a reference to that, so you can pass the same object to other view models.

At first you need to make your view model accepting external dependencies (I\d recommend seeing dependency injection pattern)

YourViewModel( /* other dependencies ..., */ AdminRepo adminRepo) 

Once you have that, you need to create a view model factory. This post describes it nicely, but long story short: implement ViewModelProvider.Factory that will keep your repository instance and use it in ViewModelProvider to obtain an instance of your view model.

With this setup you will be in control what instance you create and how you create other view models. This could be automated though with the use of dependency injection framework (Koin, dagger, hilt). If you use Dagger or Hilt (recommended by Google), then you can leave your object lifecycle in hands of the framework by providing a proper annotation. Let's try this exaple:

@Singleton
class MyRepository { ...

@HiltViewModel
class MyViewModel {
   MyRepository repo;
   @Inject MyViewModel(MyRepository repo) { ...
   ...

This code will make your repository a singleton tied to your application lifecycle. Usually you don't want to do that, because the object will live in the memory even if you move to the screen that deosn't need that. But you could use @ActivityScoped so your repository lives as long the activity.

Now if those view models have different lifecycles and they don't overlap, it would be completely fine to create a new insance for each one of them. You wouldn't want to keep unnecessary objects in the memory while you don't need them anymore.

Luke
  • 2,539
  • 2
  • 23
  • 40