Access viewModel inside a service in android
Asked Answered
O

3

24

how we can initialize the viewModel in a service. in a fragment, we do that with Kotlin delegate "by activityViewModels". or we can do it with the ViewModelProvider().get. but as far as I find out we can not do it in service because we need a "ViewModelStoreOwner" like activity or fragment.

so is that even a best practice to initialize a ViewModel in a service?

my project here.

Ordonez answered 11/7, 2020 at 11:9 Comment(3)
"because I need to call the repository class from my service" -- you do not need a viewmodel to do that.Pseudoscope
@Pseudoscope yeah you're rightOrdonez
You don't need a view model to call the repository from an activity or fragment either. View model is a "business logic and state holder". If you need to access business logic and state from your service in a way that's shared with a service...Aholla
L
17

It is not recommended using a ViewModel in a service. You could call your repository from your service itself.

https://github.com/android/architecture-components-samples/issues/137#issuecomment-327854042

The ViewModel should be used closely with an Activity or a Fragment, so it's destined to live in the UI layer of your application. Therefore, I don't recommend using the ViewModel in a Service. Create a different class, that would be used in the Service and, if needed, in the ViewModel. Like this you ensure the separation of concerns and avoid giving the ViewModel more responsibilities than needed.

Lemke answered 11/7, 2020 at 11:22 Comment(2)
In one single way viewModel should be in the service - dwaing overlay. In this case ussually is used foreground service + ui + viewModel + repositories AND developer repeat MVVM/MVI pattern inside the serviceBanas
Remember that quote is just one random guy with an opinion, that IMHO is poorly articulated. Separation of what concerns? A view model is purely state, data and operations. There's no reason why you wouldn't want to share that with non-UI code. Whether or not there are technical limitations that don't make this reasonable is another question however.Aholla
S
7

define a base service, then you can using ViewModel like activity/fragment

public class LifecycleAndViewStoreOwnerService extends LifecycleService implements ViewModelStoreOwner, HasDefaultViewModelProviderFactory {

    final ViewModelStore mViewModelStore = new ViewModelStore();
    ViewModelProvider.Factory mFactory;

    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
                if (source.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
                    mViewModelStore.clear();
                    source.getLifecycle().removeObserver(this);
                }
            }
        });
    }

    @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        return mFactory != null ? mFactory : (mFactory = new ViewModelProvider.AndroidViewModelFactory(getApplication()));
    }
}

extend the above class, show an overlay window in service

public class MyLifecycleService extends LifecycleAndViewStoreOwnerService {

    private static final String TAG = "MyLifecycleService";

    @Override
    public void onCreate() {
        super.onCreate();
        final OverlayWindowBinding binding = OverlayWindowBinding.inflate(LayoutInflater.from(this));
        binding.setViewModel(new ViewModelProvider(this).get(ViewModel.class));
        binding.setLifecycleOwner(this);

        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        getSystemService(WindowManager.class).addView(binding.getRoot(), layoutParams);

        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
                if (source.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {
                    getSystemService(WindowManager.class).removeViewImmediate(binding.getRoot());
                    source.getLifecycle().removeObserver(this);
                }
            }
        });
    }

    public static class ViewModel extends AndroidViewModel {
        public ViewModel(@NonNull Application application) {
            super(application);
        }
    }
}
Slaveholder answered 23/1, 2021 at 11:13 Comment(0)
B
1

I'll allow myself to insert my five cents: I use jetpack compose for overlay drawing from foreground service + hilt (yeah, it's a modern way). So, I hoped that hiltViewModel-compose would work, but it didn't ;) In the result I became to create a viewModel manually, like activityViewModel. This is an example of my vision, possible somebody will find it helpful:

abstract class AdvancedLifecycleService : LifecycleService(), SavedStateRegistryOwner, ViewModelStoreOwner {
  protected abstract val savedStateRegistryController: SavedStateRegistryController
    
  override val savedStateRegistry: SavedStateRegistry
    get() = savedStateRegistryController.savedStateRegistry
    
  override val viewModelStore: ViewModelStore = ViewModelStore()
    
  @Inject
  lateinit var overlayViewModelProvider: Provider<OverlayViewModel>
    
  override fun onCreate() {
    super.onCreate()
    savedStateRegistryController.performRestore(null)
    lifecycle.addObserver(object : LifecycleEventObserver {
      override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
          viewModelStore.clear()
          source.lifecycle.removeObserver(this)
        }
      }
    })
    
    val a = ViewModelProvider(
      store = viewModelStore,
      factory = ForegroundViewModelFactory(
        overlayViewModelProvider = overlayViewModelProvider,
      ),
      defaultCreationExtras = CreationExtras.Empty
    )[OverlayViewModel::class.java]
    val b = ViewModelProvider(
      store = viewModelStore,
      factory = ForegroundViewModelFactory(
       overlayViewModelProvider = overlayViewModelProvider,
     ),
     defaultCreationExtras = CreationExtras.Empty
   )[OverlayViewModel::class.java]
   println("a == b: ${a == b}") // true
 }
    
 private class ForegroundViewModelFactory(
 private val overlayViewModelProvider: Provider<OverlayViewModel>,) : ViewModelProvider.Factory {
  override fun <T : ViewModel> create(modelClass: Class<T>): T {
    if (modelClass.isAssignableFrom(OverlayViewModel::class.java)) {
      return overlayViewModelProvider.get() as T
    } else {
      throw IllegalArgumentException("Unknown ViewModel class")
    }
  }
}
}
Banas answered 9/1 at 11:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.