I want to update view from my service class using static method inside my activity so when i have create method in companion object it will now allow to inherit view class inside companion object
As you already are aware then, companion objects are static. Static methods, variables, etc, cannot access non-static variables.
Here's an example:
class Something(var x: Int){
// The companion object is static
companion object{
fun someFun(){
x = 3;//Unresolved reference: x
}
}
}
In Kotlin, x isn't found. Java does a slightly better job at explaining it:
private int x;//getter and setter
private static void someFun(){
x = 3;//Non-static field cannot be referenced from static context
}
The same applies in Kotlin, but it handles it differently. The point, however, still applies: you cannot access non-static variables from a static class, method, field, or anything else.
To bring this back to your problem, you cannot access the non-static field titleMain
from a static object, even if it's nested. It has to be non-static for you to access it.
Before I go on to a possible solution, I want to explain why you shouldn't use static methods at all when it comes to activities.
In order for you to access a non-static field in the activity, you need a static instance of the activity. Or a view for that matter. However, all of these have a Context in them. Context fields should never be static, and IntelliJ/Android Studio will warn you if you try. It can cause memory leaks, which would be the biggest problem here. It also makes instant run unusable, but that's not a problem unless you actually use it. See this Stack Overflow post.
Now, for the solution: Use callbacks. It's more complicated than using static methods, but it doesn't involve memory leaks
You didn't include that much code, so I've written most of it from scratch. It should still give you some pointers on getting started.
First off, you'll need to add an interface to the service, containing all the callback methods. It could be one, or it could be 100. Declare as many as you need, with or without return values and arguments.
Here is an example Service class. There are comments explaining what everything does.
class SomeService : Service(){
private val binder = SomeServiceBinder()
private var callback: SomeServiceCallback? = null
// The rest of the service
/**
* In this method, you return the binder instance created earlier. It's necessary
* for the connection to the Activity
*/
override fun onBind(intent: Intent?): IBinder {
// Do something here if necessary
return binder;
}
/**
* Method containing example use of callbacks
*/
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// If a callback has been registered, call the callback
// Could be done in a separate thread, after some action, before some action,
// TL;DR: anywhere you'd like.
callback?.updateUI(product)
// The return value could of course be anything.
return START_STICKY
}
/**
* Register the callback, or null for removing it
*/
fun registerCallback(callback: SomeServiceCallback?){
this.callback = callback
}
/**
* The binder. Contains a `getService` method, returning the active instance of the service.
*/
inner class SomeServiceBinder : Binder() {
fun getService() = this@SomeService
}
/**
* Add methods to this as you need. They can have arguments, or not. They can have a return type, or not. It's up to you
*/
interface SomeServiceCallback{
fun updateUI(product: Product);
}
}
And finally, the activity. In addition to extending an Activity (here: AppCompatActivity
), it also implements the callback interface.
In addition, it implements ServiceConnection
. The ServiceConnection
could also be an inner class, or declared as a field.
class SomeActivity : AppCompatActivity(), SomeService.SomeServiceCallback, ServiceConnection{
/**
* Stored externally to help with shutdown
*/
lateinit var someServiceIntent: Intent
override fun callbackForSomething(product: Product) {
println("Service called activity!")
runOnUiThread{
titleMain.text = product.title;
}
}
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
/**
* The intent needs to be created here. if it's created outside, it might reference the context before it's
* been initialized, which makes it throw an NPE
*/
someServiceIntent = Intent(this, SomeService::class.java)
}
private fun connectToService(){
startService(someServiceIntent)// This is the normal part. You need this to start the service
// However, it doesn't register a ServiceConnection. In addition to that, you also need
// to call bindService:
bindService(someServiceIntent, this, Context.BIND_AUTO_CREATE)
}
private fun disconnect(){
//Equivalently, on shutdown, there's an extra call
// First stop the service
stopService(someServiceIntent)
// Then unbind it
unbindService(this)
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
println("The service is connected")
/**
* This uses an unsafe cast since there is just one service and one service binder. If you have multiple,
* use a when statement or something else to check the type
*/
val binder = service as SomeService.SomeServiceBinder? ?: return
binder.getService().registerCallback(this)
}
override fun onServiceDisconnected(name: ComponentName?) {
TODO("Do something here if you want")
}
}