我正在将java类重写为kotlin,将其回调函数用suspend函数替换。这是我的Java代码:
@IgnoreExtraProperties
public class DeviceType {
public String manufacturer;
public String marketName;
public String model;
public DeviceType(String manufacturer, String marketName, String model) {
this.manufacturer = manufacturer;
this.marketName = marketName;
this.model = model;
}
public DeviceType(){}
public DeviceType(Context context) {
DeviceName.with(context).request(new DeviceName.Callback() {
@Override
public void onFinished(DeviceName.DeviceInfo info, Exception error) {
if (error == null) {
manufacturer = info.manufacturer;
marketName = info.marketName;
model = info.model;
} else
Log.e("DeviceType: ", error.getMessage());
}
});
}
@Override
public String toString() {
if (model == null) {
return "No device type recognized!";
} else {
if (marketName.equals(model))
return manufacturer + " " +marketName;
else
return manufacturer + " " +marketName+ " (" +model+ ")";
}
}
DeviceName
类属于库AndroidDeviceNames。
下面是我在Kotlin中的新代码:
@IgnoreExtraProperties
data class DeviceType(
var manufacturer: String? = null,
var marketName: String? = null,
var model: String? = null
) {
constructor(context: Context) : this(
context.deviceType()?.manufacturer,
context.deviceType()?.marketName,
context.deviceType()?.model
)
override fun toString(): String {
val stringSuffix =
if (marketName == model)
""
else
" ($model)"
return model?.let { "$manufacturer $marketName$stringSuffix" }
?: "No device type recognized!"
}
}
/**
* return DeviceType "from" UI Context
*/
fun Context.deviceType(): DeviceType? = runBlocking {
withContext(Dispatchers.IO) {
/*
delay(1000L)
DeviceType("Nokia","Banana","R2D2")
^
This works!
*/
DeviceName
.with(this@deviceType)
.awaitWith(this@deviceType)
// ^ that doesn't!
}
}
suspend fun DeviceName.Request.awaitWith(context: Context): DeviceType? = suspendCoroutine { cont ->
DeviceName.with(context).request { info, error ->
if (error == null) {
cont.resume(DeviceType(
info.manufacturer,
info.marketName,
info.model
))
} else
cont.resumeWithException(Throwable(error.message))
.let {
Log.e(
"FirebaseUserData",
"DeviceName.Request.awaitWith(): $error.message"
)
}
}
}
在deviceType().toString())
中执行MainActivity
将在runBlocking()
函数中进行无限循环。
基本的问题当然是“为什么我的awaitWith()
的实现不起作用?”,但我也很感兴趣,如果我应该为异常处理提供其他解决方案,请在kotlin和协程中迈出第一步,因为我阅读了“协程可能会隐藏异常”。
还有一个问题:
Dispatcher.IO
可以吗? DeviceName
从Google API json查询获取数据。
我是否也应该将该调度程序类型用于与Firebase DB相关的协程?
答案 0 :(得分:0)
首先,响应问题的标题,循环正在发生,因为构造函数正在调用Context.deviceType()
,而DeviceName.Request.awaitWith
再次调用了构造函数:
cont.resume(DeviceType(
info.manufacturer,
info.marketName,
info.model
))
Context.deviceType()
本身返回一个DeviceType
,但是您希望使用它来配置初始化中的每个属性。每个DeviceType的属性初始化都会实例化一个DeviceType
,每个属性会实例化另一个DeviceType
,依此类推。...
使用Dispatcher.IO
是可以的,甚至在进行IO操作(如网络)时也可以使用runBlocking
,但您并没有完全使用它。
## Assume we are on Thread (A)
fun Context.deviceType(): DeviceType? = runBlocking { ## Still in thread (A)
withContext(Dispatchers.IO) { ## Execute in an IO thread pool, but (A) is waiting
DeviceName
.with(this@deviceType)
.awaitWith(this@deviceType)
} ## Returns to thread (A)
} # Resumes Thread (A)
调用将阻止当前线程。您使用的方式是这样的:
AlertDialog.Builder mBuilder = new AlertDialog.Builder(getActivity());
View mView=getActivity().getLayoutInflater().inflate(R.layout.custom_layout, null);
mBuilder.setView(mView);
Button close = (Button) mView.findViewById(R.id.close);
Button OK = (Button) mView.findViewById(R.id.ok);
close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//close the dialog
hideDialog1();
}
});
OK.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//do something
}
});
dialog = mBuilder.create();
dialog.setCanceledOnTouchOutside(false);
dialog.show();
因此,尽管这有点在IO调度程序中运行,但调用线程将被阻塞,直到执行完成为止,从而使其同步且无关紧要。
答案 1 :(得分:0)
实际上,我的目标是在非协程环境中查看deviceType()
函数的输出。无论如何,此函数都将在其他暂停函数或协程范围中使用。
这是DeviceType
类,其公共函数没有其他构造函数:
@IgnoreExtraProperties
data class DeviceType(
var manufacturer: String? = null,
var marketName: String? = null,
var model: String? = null
) {
override fun toString(): String {
val stringSuffix =
if (marketName == model)
""
else
" ($model)"
return model?.let { "$manufacturer $marketName$stringSuffix" }
?: "No device type recognized!"
}
}
fun Context.deviceTypeByRunBlocking(): DeviceType? = runBlocking {
withContext(Dispatchers.IO) {
DeviceName
.with(this@deviceTypeNoSuspend)
.awaitWith(this@deviceTypeNoSuspend)
}
}
suspend fun Context.deviceType(): DeviceType? =
DeviceName
.with(this@deviceType)
.awaitWith(this@deviceType)
private suspend fun DeviceName.Request.awaitWith(context: Context): DeviceType? =
suspendCoroutine { cont ->
DeviceName.with(context).request { info, error ->
if (error == null) {
cont.resume(
DeviceType(
info.manufacturer,
info.marketName,
info.model
)
//.also{Log.d("TAG","Inside awaitWith(): $it")}
)
} else
cont.resumeWithException(Throwable(error.message))
.let {
Log.e(
"TAG",
"DeviceName.Request.awaitWith(): $error.message"
)
}
}
}
主要活动:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch { Log.d("MainActivity", "${this@MainActivity.deviceType()}") }
//^ this works
Log.d("MainActivity", "${this.deviceTypeByRunBlocking()}")
//^ this still does not, loops in joinBlocking(), isComplete = false
}
}
我知道不建议使用GlobalScope,但是对于我来说,进行测试很合适。