android / architecture-components-samples

Samples for Android Architecture Components.
https://d.android.com/arch
Apache License 2.0
23.42k stars 8.29k forks source link

[Question] Using ViewModels in Service #137

Closed AkshayChordiya closed 7 years ago

AkshayChordiya commented 7 years ago

Hi,

Everything is great with ViewModel. They work as expected in the Activity and Fragment. Recently I have encountered an need to use the ViewModel in Service. I'm not sure how exactly to do that.

I found one answer on StackOverFlow: https://stackoverflow.com/questions/44708202/observe-livedata-from-foreground-service But I want to know what's the best practice to use ViewModel in Service.

florina-muntenescu commented 7 years ago

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.

ghost commented 6 years ago

My service has a UI, which it displays all the time. I using ViewModel be still wrong? Can you elaborate more on this? @florina-muntenescu

MotiBartov commented 6 years ago

How your Service negotiate with the UI?

ghost commented 6 years ago

@MotiBartov it's a https://github.com/google/hover floating tabs

shenghaiyang commented 5 years ago

How your Service negotiate with the UI?

Control RemoteViews

zhourenjun commented 5 years ago

class FCMessagingService : FirebaseMessagingService(), LifecycleOwner {

private var fcmTokenUpload: Boolean by Preference(Constant.FCM_TOKEN_UPLOAD, false)

private var customerId: String by Preference(Constant.CUSTOMER_ID, "")

private lateinit var mViewModel: UserViewModel

private val mDispatcher = ServiceLifecycleDispatcher(this)

override fun getLifecycle() = mDispatcher.lifecycle

override fun onCreate() {
    mDispatcher.onServicePreSuperOnCreate()

    mViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(application).create(UserViewModel::class.java)
    mViewModel.let(lifecycle::addObserver)
    mViewModel.apply {
        mFcmTokenUpload.observe(this@FCMessagingService, Observer {
            fcmTokenUpload = it
        })
    }
    super.onCreate()
}

override fun onStart(intent: Intent?, startId: Int) {
    mDispatcher.onServicePreSuperOnStart()
    super.onStart(intent, startId)
}

override fun onDestroy() {
    mDispatcher.onServicePreSuperOnDestroy()
    lifecycle.removeObserver(mViewModel)
    super.onDestroy()
}

/**
 * Called when message is received.
 */
override fun onMessageReceived(remoteMessage: RemoteMessage) {
    var msg = Messaging("", "", "", "0")
    //消息
    if (remoteMessage.data.isNotEmpty()) {
        remoteMessage.data.forEach {
            if (it.key == "data") {  // "data": "{\"id\":\"2611620\",\"type\":\"2\"}"
                try {
                    msg = Gson().fromJson(it.value, Messaging::class.java)
                } catch (e: JsonSyntaxException) {
                    e.printStackTrace()
                }
            }
        }
    }
    //通知
    remoteMessage.notification?.let {
        sendNotification(it.title, it.body, msg)
    }
}

//新token 发送给app的服务器,用于定向发送推送消息。
override fun onNewToken(token: String) {
    mViewModel.setRegId(customerId, token)
}

}

komal-diwakar commented 4 years ago

Can't use this - "mFcmTokenUpload.observe(this@FCMessagingService, Observer " because for observer, we need main thread to run. And FCM runs on worker thread.

imagineDev commented 3 years ago

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.

@florina-muntenescu What about the Screen class of Android Auto Navigation/ Parking apps? How can we use ViewModel there? Your thoughts?

EnnaKenT commented 2 years ago

@florina-muntenescu What about the Screen class of Android Auto Navigation/ Parking apps? How can we use ViewModel there? Your thoughts?

Same thing.

imagineDev commented 2 years ago

@florina-muntenescu What about the Screen class of Android Auto Navigation/ Parking apps? How can we use ViewModel there? Your thoughts?

Same thing.

Actually I realised that we don't need a View Model (from Android) for that case. Why did we used a ViewModel class from Android? Because it handled the Configuration changed automatically and can be shared within multiple fragments hosted on the same activity.

But that's not the case with Screen classes. They don't go through configuration changes. They do not host any sub view/ fragment that needs it to share the data. And hence it's not required for this case.

However to separate the presentation logic, if you want, you can Use an Android's provided View Model out of the box. Like this viewModel = ViewModelProvider(ViewModelStore(), viewModelFactory).get(HomeViewModel::class.java)

This is just to make sure that if you are re-using a View Model for both Screen and a Fragment/Activity. Else or you could just create your own class, name it to ViewModel. Initialise it on create method of the screen.