我想在活动中使用静态方法从服务类更新视图,因此当我在随播对象中创建方法时,现在将允许在随播对象中继承视图类
这是代码示例
class MainActivity : AppCompatActivity() {
companion object {
private const val MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE: Int = 0x01
fun updateUI(product: Product, activity: Context) {
/*Error Line*/
titleMain.text = product.title
}
}
}
服务等级
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
MainActivity.updateUI(product,this)
}
我认为这不是正确的方法。还有其他解决方案吗?
先谢谢。
答案 0 :(得分:1)
我想在活动中使用静态方法从服务类更新视图,因此当我在随播对象中创建方法时,现在将允许在随播对象中继承视图类
那么您已经知道,伴随对象是静态的。静态方法,变量等无法访问非静态变量。
这是一个例子:
class Something(var x: Int){
// The companion object is static
companion object{
fun someFun(){
x = 3;//Unresolved reference: x
}
}
}
在Kotlin中,找不到x。 Java在解释它方面做得更好:
private int x;//getter and setter
private static void someFun(){
x = 3;//Non-static field cannot be referenced from static context
}
Kotlin中也是如此,但是处理方式却有所不同。但是,这一点仍然适用:您不能从静态类,方法,字段或其他任何内容中访问非静态变量。
为解决此问题,即使嵌套了静态对象,也无法从静态对象访问titleMain
。它必须是非静态的,您才能访问它。
在继续讨论可能的解决方案之前,我想解释一下为什么在活动方面根本不使用静态方法。
为了访问活动中的非静态字段,您需要活动的静态实例。或对此事的看法。但是,其中的 全部 中都有上下文。上下文字段绝不能是静态的,如果尝试,IntelliJ / Android Studio会警告您。它可能导致内存泄漏,这将是最大的问题。它还使即时运行不可用,但是除非您实际使用它,否则这不是问题。参见this Stack Overflow post。
现在,对于解决方案:使用回调。它比使用静态方法复杂,但不涉及内存泄漏
您没有包含那么多代码,所以我大部分都是从头开始编写的。它仍然应该为您提供入门指南。
首先,您需要向服务添加一个接口,其中包含所有回调方法。可以是一个,也可以是100。根据需要声明任意数量,可以包含或不包含返回值和参数。
这是一个示例服务类。有评论解释一切。
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);
}
}
最后是活动。除了扩展活动(此处为AppCompatActivity
)之外,它还实现了回调接口。
此外,它实现了ServiceConnection
。 ServiceConnection
也可以是内部类,也可以声明为字段。
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")
}
}