我正在创建一个Android的Kotlin应用程序.我使用suspendCoroutine将回调API转换为suspend function.目前,我有以下代码.


while(true){
   newSuspendFunction().let{ value ->
     //do some simple work
   }

   yield()
}

我的newSuspendFunction美元就在

suspend fun newSuspendFunction() {
  suspendCoroutine { cont ->
     InterfaceWithCallback { value ->
        cont.resume(value)
     }.let { myInterfaceWithCallback ->
        registerInterfaceWithCallback(myInterfaceWithCallback)
     }
  }
}

问题是,这是引入运行时开销并在每次调用函数时重新实例化InterfaceWithCallback,还是在内存中的某个地方创建一个单例并执行优化?

Edit: 我需要转换一个多镜头API,所以callbackFlow在这里是一个有效的解决方案.

推荐答案

每次调用newSuspendFunction都会创建InterfaceWithCallback的一个新实例,因为lambda捕获的延续cont每次调用suspendCoroutine时都是不同的.

话虽如此,但这并不意味着这种开销会很大.对于垃圾回收器来说,生存期较短的对象可能不是大问题.您需要衡量这是否真的会对性能产生可衡量的影响.

此外,您的循环中不需要yield(),因为newSuspendFunction已经是一个实际挂起的挂起函数.

最后但同样重要的是,为什么要打这.let个电话?我发现在可能的时候避免嵌套块更具可读性.您可以使用变量而不是let:)


编辑:现在,你的用例似乎需要一个Flow而不是一个挂起函数,因为你的回调被反复调用.您可以使用callbackFlow来包装基于回调的API:

// replace Int with whatever type you're dealing with
fun someUsefulName(): Flow<Int> = callbackFlow {
    val callback = InterfaceWithCallback { value ->
        trySendBlocking(value)
    }
    registerInterfaceWithCallback(callback)

    // suspends until the flow collector is cancelled, and unregisters
    awaitClose {
        // there should be some kind of 'unregister' counterpart
        unregisterInterfaceWithCallback(callback)
    }
}

作为参考,这里是你的newSuspendFunction的反编译字节码,在这里你可以看到new MainJvmKt$newSuspendFunction$2$1(cont):

@Nullable
public static final Object newSuspendFunction(@NotNull Continuation $completion) {
    SafeContinuation var2 = new SafeContinuation(IntrinsicsKt.intercepted($completion));
    Continuation cont = (Continuation)var2;
    int var4 = false;
    MainJvmKt$newSuspendFunction$2$1 var5 = new MainJvmKt$newSuspendFunction$2$1(cont);
    InterfaceWithCallback myInterfaceWithCallback = (InterfaceWithCallback)var5;
    int var7 = false;
    registerInterfaceWithCallback(myInterfaceWithCallback);
    Object var10000 = var2.getOrThrow();
    if (var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
        DebugProbesKt.probeCoroutineSuspended($completion);
    }

    return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : Unit.INSTANCE;
}

@Metadata(
   mv = {1, 9, 0},
   k = 3,
   d1 = {"\u0000\u000e\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\b\n\u0000\u0010\u0000\u001a\u00020\u00012\u0006\u0010\u0002\u001a\u00020\u0003H\n¢\u0006\u0002\b\u0004"},
   d2 = {"<anonymous>", "", "value", "", "callback"}
)
final class MainJvmKt$newSuspendFunction$2$1 implements InterfaceWithCallback {
   // $FF: synthetic field
   final Continuation $cont;

   public final void callback(int value) {
      Continuation var2 = this.$cont;
      Integer var3 = value;
      Result.Companion var10001 = Result.Companion;
      var2.resumeWith(Result.constructor-impl(var3));
   }

   MainJvmKt$newSuspendFunction$2$1(Continuation var1) {
      this.$cont = var1;
   }
}

这里假设InterfaceWithCallback被声明为:

fun interface InterfaceWithCallback {
    fun callback(value: Int)
}

Android相关问答推荐

Android compose ,在图像中zoom 而不裁剪?

请求标头为空/无法通过拦截器获取

我怎样才能画一条线在喷气背包组成和有一个自定义的角落?

我无法连接到信号机

如何在使用 PointerInput 修改器时添加点击时的波纹效果

使用 async 向网络发出并行请求并在supervisorScope中处理它们

Android kotlin 中闪屏 API 执行完成后如何根据身份验证将用户导航到特定屏幕

如何在这段代码android jetpack compose中实现全屏滚动

当 EditText 用于在 android studio 中将字符串发送到 firebase 时,仅允许安全调用错误

在 Android 房间迁移中获取上下文

错误:无法安装应用程序:INSTALL_PARSE_FAILED_MANIFEST_MALFORMED (React-Native-Android-Studio)

需要在按钮 onclick 上从 string.xml 获取值. @Composable 调用只能在@Composable 函数的上下文中发生

在 MVVM Jetpack Compose 上添加依赖项时出现重复类错误

如何在 Delphi 和 Android 上避免 Indy Socket Error #13 Access denied 异常?

如果 auth 失败,如何显示 toast jetpack compose firebase if else @Composable 调用只能在 @Composable 函数的上下文中发生

在 Jetpack Compose 中使用 .observeAsState() 时,如何在更改 MutableLiveData 的值后开始执行一段代码?

可组合的可见性不随状态变化而变化

Android:为什么 ICICI iMobile Pay 应用程序在我的应用程序中显示root/jailbroken设备?

为什么我不能在屏幕外拿任何物体

如何在android studio 2021.1中使用谷歌库以外的库