From cce8d6e62128ebf4d7e4e76526415e8728409b9e Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Tue, 5 Sep 2017 19:07:20 +0200 Subject: [PATCH] WorkerHandler survives config changes (#29) * Weak cache for threads * Add logs * Fix manifest --- .../cameraview/WorkerHandlerTest.java | 24 ++++++++++++++ .../cameraview/CameraController.java | 2 +- .../otaliastudios/cameraview/CameraView.java | 2 +- .../cameraview/WorkerHandler.java | 32 +++++++++++++++++-- demo/src/main/AndroidManifest.xml | 10 +++--- 5 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 cameraview/src/androidTest/java/com/otaliastudios/cameraview/WorkerHandlerTest.java diff --git a/cameraview/src/androidTest/java/com/otaliastudios/cameraview/WorkerHandlerTest.java b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/WorkerHandlerTest.java new file mode 100644 index 00000000..d0d52762 --- /dev/null +++ b/cameraview/src/androidTest/java/com/otaliastudios/cameraview/WorkerHandlerTest.java @@ -0,0 +1,24 @@ +package com.otaliastudios.cameraview; + + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class WorkerHandlerTest { + + @Test + public void testCache() { + WorkerHandler w1 = WorkerHandler.get("handler1"); + WorkerHandler w1a = WorkerHandler.get("handler1"); + WorkerHandler w2 = WorkerHandler.get("handler2"); + assertTrue(w1 == w1a); + assertFalse(w1 == w2); + } +} diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java index 81d6dd7e..0432fcbc 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java @@ -35,7 +35,7 @@ abstract class CameraController implements Preview.SurfaceCallback { mCameraCallbacks = callback; mPreview = preview; mPreview.setSurfaceCallback(this); - mHandler = new WorkerHandler("CameraViewController"); + mHandler = WorkerHandler.get("CameraViewController"); } //region Start&Stop diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java index 948c40f4..4e4de4ea 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java @@ -117,7 +117,7 @@ public class CameraView extends FrameLayout { mPreviewImpl = instantiatePreview(context, this); mCameraController = instantiateCameraController(mCameraCallbacks, mPreviewImpl); mUiHandler = new Handler(Looper.getMainLooper()); - mWorkerHandler = new WorkerHandler("CameraViewWorker"); + mWorkerHandler = WorkerHandler.get("CameraViewWorker"); // Views mGridLinesLayout = new GridLinesLayout(context); diff --git a/cameraview/src/main/utils/com/otaliastudios/cameraview/WorkerHandler.java b/cameraview/src/main/utils/com/otaliastudios/cameraview/WorkerHandler.java index 798d8366..4d7cd897 100644 --- a/cameraview/src/main/utils/com/otaliastudios/cameraview/WorkerHandler.java +++ b/cameraview/src/main/utils/com/otaliastudios/cameraview/WorkerHandler.java @@ -3,17 +3,43 @@ package com.otaliastudios.cameraview; import android.os.Handler; import android.os.HandlerThread; +import java.lang.ref.WeakReference; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; + /** * Class holding a background handler. - * Using setDaemon right now but a better approach would be to have - * start() and stop() callbacks here. TODO + * We want them to survive configuration changes if there's still job to do. */ class WorkerHandler { + private final static CameraLogger LOG = CameraLogger.create(WorkerHandler.class.getSimpleName()); + private final static ConcurrentHashMap> sCache = new ConcurrentHashMap<>(4); + + public static WorkerHandler get(String name) { + if (sCache.containsKey(name)) { + WorkerHandler cached = sCache.get(name).get(); + if (cached != null) { + HandlerThread thread = cached.mThread; + if (thread.isAlive() && !thread.isInterrupted()) { + LOG.w("get:", "Reusing cached worker handler.", name); + return cached; + } + } + LOG.w("get:", "Thread reference died, removing.", name); + sCache.remove(name); + } + + LOG.i("get:", "Creating new handler.", name); + WorkerHandler handler = new WorkerHandler(name); + sCache.put(name, new WeakReference<>(handler)); + return handler; + } + private HandlerThread mThread; private Handler mHandler; - public WorkerHandler(String name) { + private WorkerHandler(String name) { mThread = new HandlerThread(name); mThread.setDaemon(true); mThread.start(); diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index 4a44d44b..f3c04d1c 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -13,18 +13,18 @@ android:theme="@style/AppTheme"> + android:name=".MainActivity" + android:theme="@style/Theme.MainActivity" + android:configChanges="orientation|screenLayout|keyboardHidden"> - + - +