gpt4 book ai didi

java - 如何等到所有一系列嵌套的 CompletableFuture 都完成?

转载 作者:行者123 更新时间:2023-12-01 17:29:21 30 4
gpt4 key购买 nike

我有一个增强现实应用程序,其中 ARObject 是 POJO:

  class ARObject {
CompletableFuture<Texture> texture;
CompletableFuture<Material> material;
ModelRenderable renderable;

void setTexture(CompletableFuture<Texture> texture) {
this.texture = texture;
}

CompletableFuture<Texture> getTexture() {
return texture;
}

void setMaterial(CompletableFuture<Material> material) {
this.material = material;
}

CompletableFuture<Material> getMaterial() {
return material;
}
}

场景是实时合成的。在此过程中,需要构建 Texture 对象,然后构建基于 Texture 对象的 Material 对象。一旦 Material 准备就绪,就可以使用 ShapeFactory 生成实际的 AR 对象(作为 Renderable 的形式)。这意味着构建逻辑包含每个 AR 对象的两个相互嵌套的 CompletableFuture:

for (ARObject arObject : arObjects) {
Texture.Builder textureBuilder = Texture.builder();
textureBuilder.setSource(context, arObject.resourceId);
CompletableFuture<Texture> texturePromise = textureBuilder.build(); // Future #1
arObject.setTexture(texturePromise);
texturePromise.thenAccept(texture -> {
CompletableFuture<Material> materialPromise =
MaterialFactory.makeOpaqueWithTexture(context, texture); // Future #2
arObject.setMaterial(materialPromise);
});
}

完成场景构建的一种方法是等待所有 CompletableFuture 完成,然后才能进行 ShapeFactory 步骤。

我尝试在 Future 上使用 .get(),但这不仅会完全破坏异步调用提供的并行性,而且还会锁定启动应用程序,因为我认为它导致了 UI 线程的等待。

Arrays.stream(arObjectList).forEach(a -> {
try {
a.getTexture().get();
} catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "Texture CompletableFuture waiting problem " + e.toString());
}
});
Arrays.stream(arObjectList).forEach(a -> {
try {
a.getMaterial().get();
} catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "Material CompletableFuture waiting problem " + e.toString());
}
});

我将构建过程分解为多个函数,这些函数在调用链中相互调用。链如下:

  1. populateScene
  2. 纹理加载后
  3. afterTexturesSet
  4. waitForMaterials
  5. Material 加载后
  private void afterMaterialsLoaded() {
// Step 3: composing scene objects
// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());
for (ARObject arObject : arObjectList) {
try {
Material textureMaterial = arObject.getMaterial().get();

RunnableShapeBuilder shapeBuilder = new RunnableShapeBuilder(arObject, this, textureMaterial);
mainHandler.post(shapeBuilder);
}
catch (ExecutionException | InterruptedException e) {
Log.e(TAG, "Scene populating exception " + e.toString());
}
}
}

private Long waitForMaterials() {
while (!Stream.of(arObjectList).allMatch(arObject -> arObject.getMaterial() != null)) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
return 0L;
}

private void afterTexturesSet() {
boolean materialsDone = Stream.of(arObjectList).allMatch(arObject -> arObject.getMaterial() != null && arObject.getMaterial().isDone());
// If any of the materials are not loaded, then recurse until all are loaded.
if (!materialsDone) {
CompletableFuture<Texture>[] materialPromises =
Stream.of(arObjectList).map(ARObject::getMaterial).toArray(CompletableFuture[]::new);

CompletableFuture.allOf(materialPromises)
.thenAccept((Void aVoid) -> afterMaterialsLoaded())
.exceptionally(
throwable -> {
Log.e(TAG, "Exception building scene", throwable);
return null;
});
} else {
afterMaterialsLoaded();
}
}

private void afterTexturesLoaded() {
// Step 2: material loading
CompletableFuture materialsSetPromise = CompletableFuture.supplyAsync(this::waitForMaterials);
CompletableFuture.allOf(materialsSetPromise)
.thenAccept((Void aVoid) -> afterTexturesSet())
.exceptionally(
throwable -> {
Log.e(TAG, "Exception building scene", throwable);
return null;
});
}

/**
* Called when the AugmentedImage is detected and should be rendered. A Sceneform node tree is
* created based on an Anchor created from the image.
*/
@SuppressWarnings({"AndroidApiChecker", "FutureReturnValueIgnored"})
void populateScene() {
// Step 1: texture loading
boolean texturesDone = Stream.of(arObjectList).allMatch(arObject -> arObject.getTexture() != null && arObject.getTexture().isDone());
// If any of the textures are not loaded, then recurse until all are loaded.
if (!texturesDone) {
CompletableFuture<Texture>[] texturePromises =
Stream.of(arObjectList).map(ARObject::getTexture).toArray(CompletableFuture[]::new);

CompletableFuture.allOf(texturePromises)
.thenAccept((Void aVoid) -> afterTexturesLoaded())
.exceptionally(
throwable -> {
Log.e(TAG, "Exception building scene", throwable);
return null;
});
} else {
afterTexturesLoaded();
}
}

这有几个问题。首先:它仍然破坏了异步特性。在理想情况下,相应的 Material 纹理对将独立于其他对加载和生成。在这个最新版本中,执行流程中有很多交汇点,这与理想的独立场景不符。第二:我什至无法避免 waitForMaterials 步骤,其中我有丑陋的 Thread.sleep()。第三:代码总体上仍然失败,因为在最后一步,当最终从加载的纹理和 Material 构建形状时,我收到一个错误 java.lang.IllegalStateException: Must be call from the UI线程。。因此,我做了另一个改动:RunnableShapeBuilder。这样也不异常(exception),但是场景上仍然什么也没有显示,而代码却变得更加复杂。

  class RunnableShapeBuilder implements Runnable {
ARObject arObject;
AnchorNode parentNode;
Material textureMaterial;

RunnableShapeBuilder(ARObject arObject, AnchorNode parentNode, Material textureMaterial) {
this.arObject = arObject;
this.parentNode = parentNode;
this.textureMaterial = textureMaterial;
}

@Override
public void run() {
arObject.renderable = ShapeFactory.makeCube(
new Vector3(0.5f, 1, 0.01f),
new Vector3(0.0f, 0.0f, 0.0f),
textureMaterial
);
...
}
}

最佳答案

答案是:我不必等待这些嵌套的CompletableFuture。虽然我的场景变得更加复杂,但我认为我必须等到 3D Material 和纹理的构建完成。问题出在离题的地方:虽然我设置了 AR 对象的 anchor 。 AR 对象的 anchor 源自 HitTest ,我需要将 anchor 的父级设置为 AR 场景。最后一步在一些重构过程中丢失了,如果发生这种情况,没有任何警告您,AR 场景上什么也没有显示。

关注问题本身:我强烈建议不要尝试等待任何问题,因为这只会导致痛苦和磨难。寻求不同的解决方案。

关于java - 如何等到所有一系列嵌套的 CompletableFuture 都完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61153318/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com