Kotlin:使用在随播对象中创建的功能更新视图

时间:2018-08-13 10:14:55

标签: android kotlin

我想在活动中使用静态方法从服务类更新视图,因此当我在随播对象中创建方法时,现在将允许在随播对象中继承视图类

这是代码示例

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)
}

我认为这不是正确的方法。还有其他解决方案吗?

先谢谢。

1 个答案:

答案 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)之外,它还实现了回调接口。

此外,它实现了ServiceConnectionServiceConnection也可以是内部类,也可以声明为字段。

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")
    }

}