无法从协程 withContext(Dispatchers.Main.imidiate){} 更新 class 变量的值
Unable to update value of class variable from coroutine withContext(Dispatchers.Main.imidiate){}
我正在开发一个简单的程序,其中启动绑定服务并在服务内启动协程以每秒生成随机数,当单击获取数字按钮时,数字显示在文本视图中。这里的问题是我无法更新随机数(每次打印默认值)这是我的代码
主要Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import androidx.appcompat.app.AppCompatActivity
import com.example.androidservice.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var serviceIntent: Intent
private lateinit var binding: ActivityMainBinding
private lateinit var mService: MyService
private var serviceConnected = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
serviceIntent = Intent(this, MyService::class.java)
binding.startService.setOnClickListener {
if (!serviceConnected) {
startService(serviceIntent)
bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)
}
}
binding.stopService.setOnClickListener {
if (serviceConnected) {
unbindService(connection); stopService(serviceIntent)
}
}
binding.stopRandom.setOnClickListener { if (serviceConnected) mService.stopCount() }
binding.getNumber.setOnClickListener {
if (serviceConnected) binding.displayNumber.text = mService.getNumber()
}
}
private val connection: ServiceConnection=object :ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder=service as MyService.MyBinding
mService=binder.getService()
serviceConnected=true
}
override fun onServiceDisconnected(name: ComponentName?) {
serviceConnected=false
}
}
}
服务class
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import kotlinx.coroutines.*
class MyService: Service() {
private var binder= MyBinding() as IBinder
private lateinit var job:Job
private var random1 =9
class MyBinding: Binder() {
fun getService():MyService{ return MyService() }
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
override fun onDestroy() {
super.onDestroy()
Log.i("Stop Self invoked","stopSelf()")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("In service thread id= ",Thread.currentThread().name)
job= CoroutineScope(Dispatchers.IO).launch {
while (true) {
val random = (1..100).random()
Log.i("Random number is = ", random.toString())
// setValueToMainThread(random)
withContext(Dispatchers.Main.immediate){ // here is the problem code
this@MyService.random1=random
}
delay(1000)
}
}
return super.onStartCommand(intent, flags, startId)
}
fun stopCount(){ job.cancel()}
fun getNumber():String {
return random1.toString()
}
}
主要Activity XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/display_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Number"
android:textSize="32sp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.109" />
<com.google.android.material.button.MaterialButton
android:id="@+id/start_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Service"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.448" />
<com.google.android.material.button.MaterialButton
android:id="@+id/stop_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop Service"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.485"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.543" />
<com.google.android.material.button.MaterialButton
android:id="@+id/stop_random"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop Random"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.509"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.36" />
<com.google.android.material.button.MaterialButton
android:id="@+id/get_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="get number"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.657" />
</androidx.constraintlayout.widget.ConstraintLayout>
在调试时我发现 random1 的值正在改变,但是当从 onClick 调用 getNumber()
时,MyService class 的实例不一样。我不知道是什么问题。我创建了一个静态变量来保存 MyService 的实例,然后在协程中调用它。但是有没有其他方法可以更新 random1?
的值
您启动的服务实例和您用来取号的服务实例不同,因为
fun getService():MyService{ return MyService() }
getService()
将 return 每次您调用 MyService
class 的新实例。
将您的 MyBinding
class 更改为方法中的内部 class 和 return MyService
实例。
inner class MyBinding: Binder() {
fun getService():MyService{ return this@MyService }
}
我正在开发一个简单的程序,其中启动绑定服务并在服务内启动协程以每秒生成随机数,当单击获取数字按钮时,数字显示在文本视图中。这里的问题是我无法更新随机数(每次打印默认值)这是我的代码
主要Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import androidx.appcompat.app.AppCompatActivity
import com.example.androidservice.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var serviceIntent: Intent
private lateinit var binding: ActivityMainBinding
private lateinit var mService: MyService
private var serviceConnected = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
serviceIntent = Intent(this, MyService::class.java)
binding.startService.setOnClickListener {
if (!serviceConnected) {
startService(serviceIntent)
bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)
}
}
binding.stopService.setOnClickListener {
if (serviceConnected) {
unbindService(connection); stopService(serviceIntent)
}
}
binding.stopRandom.setOnClickListener { if (serviceConnected) mService.stopCount() }
binding.getNumber.setOnClickListener {
if (serviceConnected) binding.displayNumber.text = mService.getNumber()
}
}
private val connection: ServiceConnection=object :ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val binder=service as MyService.MyBinding
mService=binder.getService()
serviceConnected=true
}
override fun onServiceDisconnected(name: ComponentName?) {
serviceConnected=false
}
}
}
服务class
import android.app.Service
import android.content.Intent
import android.os.Binder
import android.os.IBinder
import android.util.Log
import kotlinx.coroutines.*
class MyService: Service() {
private var binder= MyBinding() as IBinder
private lateinit var job:Job
private var random1 =9
class MyBinding: Binder() {
fun getService():MyService{ return MyService() }
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
override fun onDestroy() {
super.onDestroy()
Log.i("Stop Self invoked","stopSelf()")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i("In service thread id= ",Thread.currentThread().name)
job= CoroutineScope(Dispatchers.IO).launch {
while (true) {
val random = (1..100).random()
Log.i("Random number is = ", random.toString())
// setValueToMainThread(random)
withContext(Dispatchers.Main.immediate){ // here is the problem code
this@MyService.random1=random
}
delay(1000)
}
}
return super.onStartCommand(intent, flags, startId)
}
fun stopCount(){ job.cancel()}
fun getNumber():String {
return random1.toString()
}
}
主要Activity XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/display_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Number"
android:textSize="32sp"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.109" />
<com.google.android.material.button.MaterialButton
android:id="@+id/start_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Service"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.448" />
<com.google.android.material.button.MaterialButton
android:id="@+id/stop_service"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop Service"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.485"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.543" />
<com.google.android.material.button.MaterialButton
android:id="@+id/stop_random"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop Random"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.509"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.36" />
<com.google.android.material.button.MaterialButton
android:id="@+id/get_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="get number"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.657" />
</androidx.constraintlayout.widget.ConstraintLayout>
在调试时我发现 random1 的值正在改变,但是当从 onClick 调用 getNumber()
时,MyService class 的实例不一样。我不知道是什么问题。我创建了一个静态变量来保存 MyService 的实例,然后在协程中调用它。但是有没有其他方法可以更新 random1?
您启动的服务实例和您用来取号的服务实例不同,因为
fun getService():MyService{ return MyService() }
getService()
将 return 每次您调用 MyService
class 的新实例。
将您的 MyBinding
class 更改为方法中的内部 class 和 return MyService
实例。
inner class MyBinding: Binder() {
fun getService():MyService{ return this@MyService }
}