Extract function to execute code on a handler

Extract function to synchronously execute code on a handler, waiting for
the execution to complete.
This commit is contained in:
Romain Vimont
2025-11-28 22:31:26 +01:00
parent 6f9eb31d52
commit 7e66062086
2 changed files with 53 additions and 22 deletions

View File

@@ -1,6 +1,7 @@
package com.genymobile.scrcpy.opengl; package com.genymobile.scrcpy.opengl;
import com.genymobile.scrcpy.device.Size; import com.genymobile.scrcpy.device.Size;
import com.genymobile.scrcpy.util.Threads;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.opengl.EGL14; import android.opengl.EGL14;
@@ -15,6 +16,7 @@ import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.view.Surface; import android.view.Surface;
import java.util.concurrent.Callable;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
public final class OpenGLRunner { public final class OpenGLRunner {
@@ -80,31 +82,17 @@ public final class OpenGLRunner {
public Surface start(Size inputSize, Size outputSize, Surface outputSurface) throws OpenGLException { public Surface start(Size inputSize, Size outputSize, Surface outputSurface) throws OpenGLException {
initOnce(); initOnce();
// Simulate CompletableFuture, but working for all Android versions
final Semaphore sem = new Semaphore(0);
Throwable[] throwableRef = new Throwable[1];
// The whole OpenGL execution must be performed on a Handler, so that SurfaceTexture.setOnFrameAvailableListener() works correctly. // The whole OpenGL execution must be performed on a Handler, so that SurfaceTexture.setOnFrameAvailableListener() works correctly.
// See <https://github.com/Genymobile/scrcpy/issues/5444> // See <https://github.com/Genymobile/scrcpy/issues/5444>
handler.post(() -> {
try {
run(inputSize, outputSize, outputSurface);
} catch (Throwable throwable) {
throwableRef[0] = throwable;
} finally {
sem.release();
}
});
try { try {
sem.acquire(); Threads.executeSynchronouslyOn(handler, new Callable<Void>() {
} catch (InterruptedException e) { @Override
// Behave as if this method call was synchronous public Void call() throws Exception {
Thread.currentThread().interrupt(); run(inputSize, outputSize, outputSurface);
} return null;
}
Throwable throwable = throwableRef[0]; });
if (throwable != null) { } catch (Throwable throwable) {
if (throwable instanceof OpenGLException) { if (throwable instanceof OpenGLException) {
throw (OpenGLException) throwable; throw (OpenGLException) throwable;
} }

View File

@@ -0,0 +1,43 @@
package com.genymobile.scrcpy.util;
import android.os.Handler;
import java.util.concurrent.Callable;
import java.util.concurrent.Semaphore;
public final class Threads {
private Threads() {
// not instantiable
}
public static <T> T executeSynchronouslyOn(Handler handler, Callable<T> callable) throws Throwable {
// Simulate CompletableFuture, but working for all Android versions
final Semaphore sem = new Semaphore(0);
@SuppressWarnings("unchecked")
T[] resultRef = (T[]) new Object[1];
Throwable[] throwableRef = new Throwable[1];
handler.post(() -> {
try {
resultRef[0] = callable.call();
} catch (Throwable throwable) {
throwableRef[0] = throwable;
} finally {
sem.release();
}
});
try {
sem.acquire();
} catch (InterruptedException e) {
// Behave as if this method call was synchronous
Thread.currentThread().interrupt();
}
if (throwableRef[0] != null) {
throw throwableRef[0];
}
return resultRef[0];
}
}