简体   繁体   English

如何在 Jetpack Compose 中关闭主线程?

[英]How to do work off the main thread in Jetpack Compose?

In a Jetpack Compose app I have a LazyVerticalGrid of thumbnails, each of which needs to be drawn in a bitmap-backed Canvas at compose time.在 Jetpack Compose 应用程序中,我有一个 LazyVerticalGrid 缩略图,每个缩略图都需要在撰写时绘制在位图支持的 Canvas 中。

The thumbnails draw correctly if I simply draw them in DrawScope of the Canvas, but the user experience is poor.如果我只是在 Canvas 的 DrawScope 中绘制缩略图,则可以正确绘制缩略图,但用户体验很差。 When the user scrolls the LazyVerticalGrid, there is a lot of jank as each thumbnail draws itself.当用户滚动 LazyVerticalGrid 时,每个缩略图都会自行绘制,因此会出现很多卡顿现象。

I had thought that Jetpack Compose composed in background threads when needed, but it all seems to be happening on the main thread, leading to the severe jank, even on the latest phones.我原以为 Jetpack Compose 在需要时在后台线程中组合,但这一切似乎都发生在主线程上,导致严重的卡顿,即使在最新的手机上也是如此。

I can solve the jank problem by drawing onto the Canvas's underlying bitmap on another thread, using LaunchedEffect withContext(IO).我可以通过使用 LaunchedEffect withContext(IO) 在另一个线程上绘制到 Canvas 的底层位图来解决卡顿问题。 But the problem is, Compose doesn't know to recompose the Canvas when the bitmap is drawn, so I often end up with half-drawn thumbnails.但问题是,Compose 不知道在绘制位图时重新组合 Canvas,所以我经常以半绘制的缩略图结束。

Is there a way to do work off the main thread and then recompose once that work is done?有没有一种方法可以在主线程之外完成工作,然后在完成工作后重新组合?

Here is the janky code (edited for brevity), followed by the non-janky version that doesn't always recompose when the drawing is complete:这是卡住的代码(为简洁起见进行了编辑),然后是在绘图完成后并不总是重新组合的非卡住版本:

   val imageBitmap = remember {Bitmap.createBitmap(515, 618, Bitmap.Config.ARGB_8888)}
   val bitmapCanvas = remember { android.graphics.Canvas(imageBitmap) }
   ElevatedCard() {
      Canvas() {
         bitmapCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
         penStrokes.forEach {
           inker.drawEvent(it)
         }
         this.drawImage(imageBitmap.asImageBitmap())
       }
   }

non-janky but still not right非简陋但仍然不正确

   val imageBitmap = remember {Bitmap.createBitmap(515, 618, Bitmap.Config.ARGB_8888)}
   val bitmapCanvas = remember { android.graphics.Canvas(imageBitmap) }
   LaunchedEffect(Unit) {
      withContext(IO) {
         bitmapCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
         penStrokes.forEach {
           inker.drawEvent(it)
         }
      }
   }
   ElevatedCard() {
      Canvas() {
         this.drawImage(imageBitmap.asImageBitmap())
       }
   }

In the end I used the common "invalidate++" kludge that people seem to use to force Jetpack Compose to redraw on command.最后,我使用了人们似乎用来强制 Jetpack Compose 根据命令重绘的常见“invalidate++”kludge。 It has a bit of code smell to it, I think, but it does the trick.我认为它有一点代码味道,但它确实有效。

val imageBitmap = remember {Bitmap.createBitmap(515, 618, 
     Bitmap.Config.ARGB_8888)}
val bitmapCanvas = remember { android.graphics.Canvas(imageBitmap) }
var invalidate by remember { mutableStateOf(0) }


LaunchedEffect(Unit) {
  withContext(IO) {
     bitmapCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
     penStrokes.forEach {
       inker.drawEvent(it)
     }
     invalidate++
  }
}

ElevatedCard() {
   Canvas() {
      invalidate.let {
           this.drawImage(imageBitmap.asImageBitmap())
      }
   }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM