[go: nahoru, domu]

Merge changes I7078a750,I9d1fd206,Icebddffd into androidx-master-dev

* changes:
  Remove the callbackHandler from ThreadConfig.
  Use the background Handler to process the image
  Add an Executor as part of setAnalyzer API.
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
index 387a960..3602ea9 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/ImageAnalysisTest.java
@@ -43,6 +43,7 @@
 import androidx.camera.core.ImageAnalysisConfig;
 import androidx.camera.core.ImageProxy;
 import androidx.camera.core.UseCase.StateChangeListener;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
@@ -145,7 +146,7 @@
 
         Analyzer initialAnalyzer = useCase.getAnalyzer();
 
-        useCase.setAnalyzer(mMockAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
 
         Analyzer retrievedAnalyzer = useCase.getAnalyzer();
 
@@ -163,7 +164,7 @@
         useCase.updateSuggestedResolution(suggestedResolutionMap);
         useCase.addStateChangeListener(mMockListener);
 
-        useCase.setAnalyzer(mMockAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
 
         verify(mMockListener, times(1)).onUseCaseActive(useCase);
     }
@@ -176,7 +177,7 @@
         suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
         useCase.updateSuggestedResolution(suggestedResolutionMap);
         useCase.addStateChangeListener(mMockListener);
-        useCase.setAnalyzer(mMockAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mMockAnalyzer);
         useCase.removeAnalyzer();
 
         verify(mMockListener, times(1)).onUseCaseInactive(useCase);
@@ -197,9 +198,8 @@
     private void analyzerAnalyzesImagesWithMode(ImageReaderMode imageReaderMode)
             throws InterruptedException, CameraInfoUnavailableException {
         final int imageFormat = ImageFormat.YUV_420_888;
-        ImageAnalysisConfig config =
-                new ImageAnalysisConfig.Builder().setImageReaderMode(
-                        imageReaderMode).setCallbackHandler(mHandler).build();
+        ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().setImageReaderMode(
+                imageReaderMode).build();
         ImageAnalysis useCase = new ImageAnalysis(config);
         Map<String, Size> suggestedResolutionMap = new HashMap<>();
         suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
@@ -209,7 +209,7 @@
         mInstrumentation.runOnMainSync(new Runnable() {
             @Override
             public void run() {
-                useCase.setAnalyzer(mAnalyzer);
+                useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
             }
         });
 
@@ -229,13 +229,12 @@
     @Test
     @UiThreadTest
     public void analyzerDoesNotAnalyzeImages_whenCameraIsNotOpen() throws InterruptedException {
-        ImageAnalysisConfig config =
-                new ImageAnalysisConfig.Builder().setCallbackHandler(mHandler).build();
+        ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().build();
         ImageAnalysis useCase = new ImageAnalysis(config);
         Map<String, Size> suggestedResolutionMap = new HashMap<>();
         suggestedResolutionMap.put(mCameraId, DEFAULT_RESOLUTION);
         useCase.updateSuggestedResolution(suggestedResolutionMap);
-        useCase.setAnalyzer(mAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
         // Keep the lifecycle in an inactive state.
         // Wait a little while for frames to be analyzed.
         mAnalysisResultsSemaphore.tryAcquire(5, TimeUnit.SECONDS);
@@ -254,10 +253,9 @@
         final int imageFormat = ImageFormat.YUV_420_888;
         final Size[] sizes = {SECONDARY_RESOLUTION, DEFAULT_RESOLUTION};
 
-        ImageAnalysisConfig config =
-                new ImageAnalysisConfig.Builder().setCallbackHandler(mHandler).build();
+        ImageAnalysisConfig config = new ImageAnalysisConfig.Builder().build();
         ImageAnalysis useCase = new ImageAnalysis(config);
-        useCase.setAnalyzer(mAnalyzer);
+        useCase.setAnalyzer(CameraXExecutors.newHandlerExecutor(mHandler), mAnalyzer);
 
         for (Size size : sizes) {
             Map<String, Size> suggestedResolutionMap = new HashMap<>();
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
index dc29aaa..e472f28 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/SensorOrientedMeteringPointFactoryTest.java
@@ -22,8 +22,6 @@
 
 import android.app.Instrumentation;
 import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Rational;
 
 import androidx.camera.core.AppConfig;
@@ -114,7 +112,6 @@
                         .setLensFacing(CameraX.LensFacing.BACK)
                         .setTargetAspectRatio(AspectRatio.RATIO_4_3)
                         .setTargetName("ImageAnalysis")
-                        .setCallbackHandler(new Handler(Looper.getMainLooper()))
                         .build();
         ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
         mInstrumentation.runOnMainSync(new Runnable() {
@@ -139,7 +136,6 @@
                         .setLensFacing(CameraX.LensFacing.BACK)
                         .setTargetAspectRatio(AspectRatio.RATIO_4_3)
                         .setTargetName("ImageAnalysis")
-                        .setCallbackHandler(new Handler(Looper.getMainLooper()))
                         .build();
         ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
 
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
index d1c2c3f..d9f5a60 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/UseCaseCombinationTest.java
@@ -23,8 +23,6 @@
 import android.Manifest;
 import android.app.Instrumentation;
 import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
 
 import androidx.camera.core.AppConfig;
 import androidx.camera.core.CameraX;
@@ -36,6 +34,7 @@
 import androidx.camera.core.ImageProxy;
 import androidx.camera.core.Preview;
 import androidx.camera.core.PreviewConfig;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.lifecycle.MutableLiveData;
@@ -144,7 +143,7 @@
             @Override
             public void run() {
                 CameraX.bindToLifecycle(mLifecycle, mPreview, mImageAnalysis);
-                mImageAnalysis.setAnalyzer(mImageAnalyzer);
+                mImageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mAnalysisResult.observe(mLifecycle,
                         createCountIncrementingObserver());
                 mLifecycle.startAndResume();
@@ -171,7 +170,7 @@
             @Override
             public void run() {
                 CameraX.bindToLifecycle(mLifecycle, mPreview, mImageAnalysis, mImageCapture);
-                mImageAnalysis.setAnalyzer(mImageAnalyzer);
+                mImageAnalysis.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mAnalysisResult.observe(mLifecycle,
                         createCountIncrementingObserver());
                 mLifecycle.startAndResume();
@@ -192,7 +191,6 @@
                 new ImageAnalysisConfig.Builder()
                         .setLensFacing(DEFAULT_LENS_FACING)
                         .setTargetName("ImageAnalysis")
-                        .setCallbackHandler(new Handler(Looper.getMainLooper()))
                         .build();
         mImageAnalyzer =
                 new ImageAnalysis.Analyzer() {
diff --git a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
index 0a7309f..a67fbe6 100644
--- a/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
+++ b/camera/camera-camera2/src/androidTest/java/androidx/camera/camera2/impl/Camera2ImplCameraXTest.java
@@ -45,6 +45,7 @@
 import androidx.camera.core.ImageCapture;
 import androidx.camera.core.ImageCaptureConfig;
 import androidx.camera.core.ImageProxy;
+import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.camera.testing.CameraUtil;
 import androidx.camera.testing.fakes.FakeLifecycleOwner;
 import androidx.lifecycle.MutableLiveData;
@@ -143,7 +144,7 @@
                 ImageAnalysis useCase = new ImageAnalysis(builder.build());
 
                 CameraX.bindToLifecycle(mLifecycle, useCase);
-                useCase.setAnalyzer(mImageAnalyzer);
+                useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mAnalysisResult.observe(mLifecycle, mockObserver);
 
                 mLifecycle.startAndResume();
@@ -184,8 +185,8 @@
 
                 CameraX.bindToLifecycle(mLifecycle, useCase, useCase2);
 
-                useCase.setAnalyzer(mImageAnalyzer);
-                useCase2.setAnalyzer(mImageAnalyzer2);
+                useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
+                useCase2.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer2);
                 mAnalysisResult.observe(mLifecycle, mockObserver);
                 mAnalysisResult2.observe(mLifecycle, mockObserver2);
 
@@ -216,7 +217,7 @@
                         .setSessionCaptureCallback(sessionCaptureCallback);
                 ImageAnalysis useCase = new ImageAnalysis(configBuilder.build());
                 CameraX.bindToLifecycle(mLifecycle, useCase);
-                useCase.setAnalyzer(mImageAnalyzer);
+                useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mAnalysisResult.observe(mLifecycle, createCountIncrementingObserver(observedCount));
 
                 mLifecycle.startAndResume();
@@ -259,7 +260,7 @@
             @Override
             public void run() {
                 CameraX.bindToLifecycle(mLifecycle, useCase);
-                useCase.setAnalyzer(mImageAnalyzer);
+                useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 mLifecycle.startAndResume();
             }
         });
@@ -362,7 +363,7 @@
                 @Override
                 public void run() {
                     CameraX.bindToLifecycle(mLifecycle, useCase);
-                    useCase.setAnalyzer(mImageAnalyzer);
+                    useCase.setAnalyzer(CameraXExecutors.mainThreadExecutor(), mImageAnalyzer);
                 }
             });
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
index 80649f8..06f9ed9 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysis.java
@@ -17,8 +17,6 @@
 package androidx.camera.core;
 
 import android.media.ImageReader;
-import android.os.Handler;
-import android.os.Looper;
 import android.util.Log;
 import android.util.Size;
 import android.view.Display;
@@ -62,8 +60,8 @@
     private static final int NON_BLOCKING_IMAGE_DEPTH = 4;
 
     final AtomicReference<Analyzer> mSubscribedAnalyzer;
+    final AtomicReference<Executor> mAnalyzerExecutor;
     final AtomicInteger mRelativeRotation = new AtomicInteger();
-    final Handler mHandler;
     private final ImageAnalysisConfig.Builder mUseCaseConfigBuilder;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     final ImageAnalysisBlockingAnalyzer mImageAnalysisBlockingAnalyzer;
@@ -86,18 +84,15 @@
         // Get the combined configuration with defaults
         ImageAnalysisConfig combinedConfig = (ImageAnalysisConfig) getUseCaseConfig();
         mSubscribedAnalyzer = new AtomicReference<>();
-        mHandler = combinedConfig.getCallbackHandler(null);
-        if (mHandler == null) {
-            throw new IllegalStateException("No default mHandler specified.");
-        }
+        mAnalyzerExecutor = new AtomicReference<>();
         setImageFormat(ImageReaderFormatRecommender.chooseCombo().imageAnalysisFormat());
         // Init both instead of lazy loading to void synchronization.
         mImageAnalysisBlockingAnalyzer = new ImageAnalysisBlockingAnalyzer(mSubscribedAnalyzer,
                 mRelativeRotation,
-                mHandler);
+                mAnalyzerExecutor);
         mImageAnalysisNonBlockingAnalyzer = new ImageAnalysisNonBlockingAnalyzer(
                 mSubscribedAnalyzer, mRelativeRotation,
-                mHandler, config.getBackgroundExecutor(
+                mAnalyzerExecutor, config.getBackgroundExecutor(
                 CameraXExecutors.highPriorityExecutor()));
     }
 
@@ -184,15 +179,17 @@
     /**
      * Removes a previously set analyzer.
      *
-     * <p>This is equivalent to calling {@code setAnalyzer(null)}.  This will stop data from
-     * streaming to the {@link ImageAnalysis}.
+     * <p>This will stop data from streaming to the {@link ImageAnalysis}.
      *
      * @throws IllegalStateException If not called on main thread.
      */
     @UiThread
     public void removeAnalyzer() {
         Threads.checkMainThread();
-        setAnalyzer(null);
+        mAnalyzerExecutor.set(null);
+        if (mSubscribedAnalyzer.getAndSet(null) != null) {
+            notifyInactive();
+        }
     }
 
     /**
@@ -266,8 +263,7 @@
      * Sets an analyzer to receive and analyze images.
      *
      * <p>Setting an analyzer will signal to the camera that it should begin sending data. The
-     * stream of data can be stopped by setting the analyzer to {@code null} or by calling {@link
-     * #removeAnalyzer()}.
+     * stream of data can be stopped by calling {@link #removeAnalyzer()}.
      *
      * <p>Applications can process or copy the image by implementing the {@link Analyzer}.  If
      * frames should be skipped (no analysis), the analyzer function should return, instead of
@@ -276,18 +272,18 @@
      * <p>Setting an analyzer function replaces any previous analyzer.  Only one analyzer can be
      * set at any time.
      *
-     * @param analyzer of the images or {@code null} to stop data streaming to
-     *                 {@link ImageAnalysis}.
+     * @param executor The executor in which the
+     * {@link ImageAnalysis.Analyzer#analyze(ImageProxy, int)} will be run.
+     * @param analyzer of the images.
      * @throws IllegalStateException If not called on main thread.
      */
     @UiThread
-    public void setAnalyzer(@Nullable Analyzer analyzer) {
+    public void setAnalyzer(@NonNull Executor executor, @NonNull Analyzer analyzer) {
         Threads.checkMainThread();
+        mAnalyzerExecutor.set(executor);
         Analyzer previousAnalyzer = mSubscribedAnalyzer.getAndSet(analyzer);
         if (previousAnalyzer == null && analyzer != null) {
             notifyActive();
-        } else if (previousAnalyzer != null && analyzer == null) {
-            notifyInactive();
         }
     }
 
@@ -385,8 +381,8 @@
     /**
      * Interface for analyzing images.
      *
-     * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Analyzer)} to receive
-     * images and perform custom processing by implementing the
+     * <p>Implement Analyzer and pass it to {@link ImageAnalysis#setAnalyzer(Executor, Analyzer)}
+     * to receive images and perform custom processing by implementing the
      * {@link ImageAnalysis.Analyzer#analyze(ImageProxy, int)} function.
      */
     public interface Analyzer {
@@ -428,7 +424,6 @@
     public static final class Defaults implements ConfigProvider<ImageAnalysisConfig> {
         private static final ImageReaderMode DEFAULT_IMAGE_READER_MODE =
                 ImageReaderMode.ACQUIRE_LATEST_IMAGE;
-        private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
         private static final int DEFAULT_IMAGE_QUEUE_DEPTH = 6;
         private static final Size DEFAULT_TARGET_RESOLUTION = new Size(640, 480);
         private static final Size DEFAULT_MAX_RESOLUTION = new Size(1920, 1080);
@@ -440,7 +435,6 @@
             ImageAnalysisConfig.Builder builder =
                     new ImageAnalysisConfig.Builder()
                             .setImageReaderMode(DEFAULT_IMAGE_READER_MODE)
-                            .setCallbackHandler(DEFAULT_HANDLER)
                             .setImageQueueDepth(DEFAULT_IMAGE_QUEUE_DEPTH)
                             .setDefaultResolution(DEFAULT_TARGET_RESOLUTION)
                             .setMaxResolution(DEFAULT_MAX_RESOLUTION)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
index 74b18c4..6d1a3c3 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisAbstractAnalyzer.java
@@ -16,8 +16,7 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
-
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
@@ -34,16 +33,16 @@
     // Member variables from ImageAnalysis.
     private final AtomicReference<ImageAnalysis.Analyzer> mSubscribedAnalyzer;
     private final AtomicInteger mRelativeRotation;
-    final Handler mUserHandler;
+    final AtomicReference<Executor> mUserExecutor;
 
     // Flag that reflects the state of ImageAnalysis.
     private AtomicBoolean mIsClosed;
 
     ImageAnalysisAbstractAnalyzer(AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
-            AtomicInteger relativeRotation, Handler userHandler) {
+            AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor) {
         mSubscribedAnalyzer = subscribedAnalyzer;
         mRelativeRotation = relativeRotation;
-        mUserHandler = userHandler;
+        mUserExecutor = userExecutor;
         mIsClosed = new AtomicBoolean(false);
     }
 
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
index 1283f8d..43e3a65 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisBlockingAnalyzer.java
@@ -16,8 +16,7 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
-
+import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -30,8 +29,8 @@
 
     ImageAnalysisBlockingAnalyzer(
             AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
-            AtomicInteger relativeRotation, Handler userHandler) {
-        super(subscribedAnalyzer, relativeRotation, userHandler);
+            AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor) {
+        super(subscribedAnalyzer, relativeRotation, userExecutor);
     }
 
     @Override
@@ -40,18 +39,23 @@
         if (image == null) {
             return;
         }
-        try {
-            mUserHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        analyzeImage(image);
-                    } finally {
-                        image.close();
+        Executor executor = mUserExecutor.get();
+        if (executor != null) {
+            try {
+                executor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            analyzeImage(image);
+                        } finally {
+                            image.close();
+                        }
                     }
-                }
-            });
-        } catch (RuntimeException e) {
+                });
+            } catch (RuntimeException e) {
+                image.close();
+            }
+        } else {
             image.close();
         }
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
index 98b8629..926557d4 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisConfig.java
@@ -17,7 +17,6 @@
 package androidx.camera.core;
 
 import android.media.ImageReader;
-import android.os.Handler;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -442,31 +441,6 @@
     // Implementations of ThreadConfig default methods
 
     /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
-    @Override
-    @Nullable
-    public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
-        return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
-    }
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
-    @Override
-    @NonNull
-    public Handler getCallbackHandler() {
-        return retrieveOption(OPTION_CALLBACK_HANDLER);
-    }
-
-    /**
      * Returns the executor that will be used for background tasks.
      *
      * @param valueIfMissing The value to return if this configuration option has not been set.
@@ -912,19 +886,6 @@
         // Implementations of ThreadConfig.Builder default methods
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @Override
-        @NonNull
-        public Builder setCallbackHandler(@NonNull Handler handler) {
-            getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
-            return this;
-        }
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
index 0583b38..71a6232 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzer.java
@@ -16,7 +16,6 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
 import android.util.Log;
 
 import androidx.annotation.GuardedBy;
@@ -52,8 +51,9 @@
     private final AtomicLong mFinishedImageTimestamp;
 
     ImageAnalysisNonBlockingAnalyzer(AtomicReference<ImageAnalysis.Analyzer> subscribedAnalyzer,
-            AtomicInteger relativeRotation, Handler userHandler, Executor executor) {
-        super(subscribedAnalyzer, relativeRotation, userHandler);
+            AtomicInteger relativeRotation, AtomicReference<Executor> userExecutor,
+            Executor executor) {
+        super(subscribedAnalyzer, relativeRotation, userExecutor);
         mBackgroundExecutor = executor;
         mPostedImageTimestamp = new AtomicLong();
         mFinishedImageTimestamp = new AtomicLong();
@@ -128,26 +128,31 @@
         }
 
         mPostedImageTimestamp.set(imageProxy.getTimestamp());
-        try {
-            mUserHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    try {
-                        analyzeImage(imageProxy);
-                    } finally {
-                        finishImage(imageProxy);
-                        mBackgroundExecutor.execute(new Runnable() {
-                            @Override
-                            public void run() {
-                                analyzeCachedImage();
-                            }
-                        });
+        Executor executor = mUserExecutor.get();
+        if (executor != null) {
+            try {
+                executor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            analyzeImage(imageProxy);
+                        } finally {
+                            finishImage(imageProxy);
+                            mBackgroundExecutor.execute(new Runnable() {
+                                @Override
+                                public void run() {
+                                    analyzeCachedImage();
+                                }
+                            });
+                        }
                     }
-                }
-            });
-        } catch (RuntimeException e) {
-            // Unblock if fails to post to user thread.
-            Log.e(TAG, "Error calling user callback", e);
+                });
+            } catch (RuntimeException e) {
+                // Unblock if fails to post to user thread.
+                Log.e(TAG, "Error calling user callback", e);
+                finishImage(imageProxy);
+            }
+        } else {
             finishImage(imageProxy);
         }
     }
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
index 8486a42..dd76501 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCapture.java
@@ -22,6 +22,7 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.util.Log;
@@ -55,12 +56,13 @@
 import com.google.common.util.concurrent.ListenableFuture;
 
 import java.io.File;
-import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -101,10 +103,13 @@
     private static final Metadata EMPTY_METADATA = new Metadata();
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     final Handler mMainHandler = new Handler(Looper.getMainLooper());
+    @Nullable
+    private HandlerThread mProcessingImageResultThread;
+    @Nullable
+    private Handler mProcessingImageResultHandler;
+
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    final ArrayDeque<ImageCaptureRequest> mImageCaptureRequests = new ArrayDeque<>();
-    @SuppressWarnings("WeakerAccess") /* synthetic accessor */
-    final Handler mHandler;
+    final Deque<ImageCaptureRequest> mImageCaptureRequests = new ConcurrentLinkedDeque<>();
     @SuppressWarnings("WeakerAccess") /* synthetic accessor */
     SessionConfig.Builder mSessionConfigBuilder;
     private final CaptureConfig mCaptureConfig;
@@ -197,11 +202,6 @@
             mEnableCheck3AConverged = false; // skip 3A convergence in MIN_LATENCY mode
         }
 
-        mHandler = mConfig.getCallbackHandler(null);
-        if (mHandler == null) {
-            throw new IllegalStateException("No default handler specified.");
-        }
-
         CaptureConfig.Builder captureBuilder = CaptureConfig.Builder.createFrom(mConfig);
         mCaptureConfig = captureBuilder.build();
     }
@@ -212,20 +212,27 @@
         SessionConfig.Builder sessionConfigBuilder = SessionConfig.Builder.createFrom(config);
         sessionConfigBuilder.addRepeatingCameraCaptureCallback(mSessionCallbackChecker);
 
+        mProcessingImageResultThread = new HandlerThread("OnImageAvailableHandlerThread");
+        mProcessingImageResultThread.start();
+        mProcessingImageResultHandler = new Handler(mProcessingImageResultThread.getLooper());
+
         // Setup the ImageReader to do processing
         if (mCaptureProcessor != null) {
+            // TODO: To allow user to use an Executor for the image processing.
             ProcessingImageReader processingImageReader =
                     new ProcessingImageReader(
                             resolution.getWidth(),
                             resolution.getHeight(),
                             getImageFormat(), mMaxCaptureStages,
-                            mHandler, getCaptureBundle(CaptureBundles.singleDefaultCaptureBundle()),
+                            mProcessingImageResultHandler,
+                            getCaptureBundle(CaptureBundles.singleDefaultCaptureBundle()),
                             mCaptureProcessor);
             mMetadataMatchingCaptureCallback = processingImageReader.getCameraCaptureCallback();
             mImageReader = processingImageReader;
         } else {
             MetadataImageReader metadataImageReader = new MetadataImageReader(resolution.getWidth(),
-                    resolution.getHeight(), getImageFormat(), MAX_IMAGES, mHandler);
+                    resolution.getHeight(), getImageFormat(), MAX_IMAGES,
+                    mProcessingImageResultHandler);
             mMetadataMatchingCaptureCallback = metadataImageReader.getCameraCaptureCallback();
             mImageReader = metadataImageReader;
         }
@@ -256,7 +263,7 @@
                         }
                     }
                 },
-                mMainHandler);
+                mProcessingImageResultHandler);
 
         mDeferrableSurface = new ImmediateSurface(mImageReader.getSurface());
         sessionConfigBuilder.addNonRepeatingSurface(mDeferrableSurface);
@@ -285,6 +292,7 @@
         mDeferrableSurface = null;
         ImageReaderProxy imageReaderProxy = mImageReader;
         mImageReader = null;
+        HandlerThread handlerThread = mProcessingImageResultThread;
 
         if (deferrableSurface != null) {
             deferrableSurface.setOnSurfaceDetachedListener(
@@ -295,6 +303,9 @@
                             if (imageReaderProxy != null) {
                                 imageReaderProxy.close();
                             }
+
+                            // Close the handlerThread after the ImageReader was closed.
+                            handlerThread.quitSafely();
                         }
                     });
         }
@@ -1160,7 +1171,6 @@
             implements ConfigProvider<ImageCaptureConfig> {
         private static final CaptureMode DEFAULT_CAPTURE_MODE = CaptureMode.MIN_LATENCY;
         private static final FlashMode DEFAULT_FLASH_MODE = FlashMode.OFF;
-        private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 4;
 
         private static final ImageCaptureConfig DEFAULT_CONFIG;
@@ -1170,7 +1180,6 @@
                     new ImageCaptureConfig.Builder()
                             .setCaptureMode(DEFAULT_CAPTURE_MODE)
                             .setFlashMode(DEFAULT_FLASH_MODE)
-                            .setCallbackHandler(DEFAULT_HANDLER)
                             .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY);
 
             DEFAULT_CONFIG = builder.build();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
index 4d6e7e6..617da2a 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureConfig.java
@@ -17,7 +17,6 @@
 package androidx.camera.core;
 
 import android.graphics.ImageFormat;
-import android.os.Handler;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -547,31 +546,6 @@
     // Implementations of ThreadConfig default methods
 
     /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
-    @Override
-    @Nullable
-    public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
-        return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
-    }
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
-    @Override
-    @NonNull
-    public Handler getCallbackHandler() {
-        return retrieveOption(OPTION_CALLBACK_HANDLER);
-    }
-
-    /**
      * Returns the executor that will be used for background tasks.
      *
      * @param valueIfMissing The value to return if this configuration option has not been set.
@@ -1058,19 +1032,6 @@
         // Implementations of ThreadConfig.Builder default methods
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @Override
-        @NonNull
-        public Builder setCallbackHandler(@NonNull Handler handler) {
-            getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
-            return this;
-        }
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 6475597..1ad8711 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -20,7 +20,7 @@
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerThread;
 import android.util.Log;
 import android.util.Rational;
 import android.util.Size;
@@ -82,7 +82,10 @@
     private static final String TAG = "Preview";
     private static final String CONFLICTING_SURFACE_API_ERROR_MESSAGE =
             "PreviewSurfaceCallback cannot be used with OnPreviewOutputUpdateListener.";
-    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+    @Nullable
+    private HandlerThread mProcessingPreviewThread;
+    @Nullable
+    private Handler mProcessingPreviewHandler;
 
     private final PreviewConfig.Builder mUseCaseConfigBuilder;
     @Nullable
@@ -116,13 +119,20 @@
         final CaptureProcessor captureProcessor = config.getCaptureProcessor(null);
         if (captureProcessor != null) {
             CaptureStage captureStage = new CaptureStage.DefaultCaptureStage();
+            // TODO: To allow user to use an Executor for the processing.
+
+            if (mProcessingPreviewHandler == null) {
+                mProcessingPreviewThread = new HandlerThread("ProcessingSurfaceTexture");
+                mProcessingPreviewThread.start();
+                mProcessingPreviewHandler = new Handler(mProcessingPreviewThread.getLooper());
+            }
 
             ProcessingSurfaceTexture processingSurfaceTexture =
                     new ProcessingSurfaceTexture(
                             resolution.getWidth(),
                             resolution.getHeight(),
                             ImageFormat.YUV_420_888,
-                            config.getCallbackHandler(),
+                            mProcessingPreviewHandler,
                             captureStage,
                             captureProcessor);
 
@@ -182,6 +192,14 @@
         if (surfaceTextureHolder != null) {
             surfaceTextureHolder.release();
         }
+
+        // The handler no longer used by the ProcessingSurfaceTexture, we can close the
+        // handlerTread after the ProcessingSurfaceTexture was released.
+        if (mProcessingPreviewHandler != null) {
+            mProcessingPreviewThread.quitSafely();
+            mProcessingPreviewThread = null;
+            mProcessingPreviewHandler = null;
+        }
     }
 
     /**
@@ -584,7 +602,6 @@
      */
     @RestrictTo(Scope.LIBRARY_GROUP)
     public static final class Defaults implements ConfigProvider<PreviewConfig> {
-        private static final Handler DEFAULT_HANDLER = new Handler(Looper.getMainLooper());
         private static final Size DEFAULT_MAX_RESOLUTION =
                 CameraX.getSurfaceManager().getPreviewSize();
         private static final int DEFAULT_SURFACE_OCCUPANCY_PRIORITY = 2;
@@ -594,7 +611,6 @@
         static {
             PreviewConfig.Builder builder =
                     new PreviewConfig.Builder()
-                            .setCallbackHandler(DEFAULT_HANDLER)
                             .setMaxResolution(DEFAULT_MAX_RESOLUTION)
                             .setSurfaceOccupancyPriority(DEFAULT_SURFACE_OCCUPANCY_PRIORITY);
             DEFAULT_CONFIG = builder.build();
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
index 91854b5..e8b6f7e 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/PreviewConfig.java
@@ -16,7 +16,6 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -383,31 +382,6 @@
     // Implementations of ThreadConfig default methods
 
     /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
-    @Override
-    @Nullable
-    public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
-        return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
-    }
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
-    @Override
-    @NonNull
-    public Handler getCallbackHandler() {
-        return retrieveOption(OPTION_CALLBACK_HANDLER);
-    }
-
-    /**
      * Returns the executor that will be used for background tasks.
      *
      * @param valueIfMissing The value to return if this configuration option has not been set.
@@ -849,19 +823,6 @@
         // Implementations of ThreadConfig.Builder default methods
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @Override
-        @NonNull
-        public Builder setCallbackHandler(@NonNull Handler handler) {
-            getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
-            return this;
-        }
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
index 6744bd4..57b5262 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ProcessingSurfaceTexture.java
@@ -18,6 +18,7 @@
 
 import android.graphics.PixelFormat;
 import android.graphics.SurfaceTexture;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
@@ -219,7 +220,7 @@
                             }
                         }
                     },
-                    mImageReaderHandler
+                    AsyncTask.THREAD_POOL_EXECUTOR
             );
 
             // Need to wait for Surface has been detached before closing it
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
index c8814ec..679d446 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ThreadConfig.java
@@ -16,8 +16,6 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.RestrictTo;
@@ -38,16 +36,6 @@
     // *********************************************************************************************
 
     /**
-     * Option: camerax.core.thread.callbackHandler
-     *
-     * @hide
-     */
-    @RestrictTo(Scope.LIBRARY_GROUP)
-    Option<Handler> OPTION_CALLBACK_HANDLER =
-            Option.create("camerax.core.thread.callbackHandler", Handler.class);
-
-
-    /**
      * Option: camerax.core.thread.backgroundExecutor
      *
      * @hide
@@ -59,25 +47,6 @@
     // *********************************************************************************************
 
     /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
-    @Nullable
-    Handler getCallbackHandler(@Nullable Handler valueIfMissing);
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
-    @NonNull
-    Handler getCallbackHandler();
-
-    /**
      * Returns the executor that will be used for background tasks.
      *
      * @param valueIfMissing The value to return if this configuration option has not been set.
@@ -108,15 +77,6 @@
     interface Builder<B> {
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @NonNull
-        B setCallbackHandler(@NonNull Handler handler);
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java b/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
index 136e04f..e30326c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/VideoCapture.java
@@ -921,7 +921,6 @@
         static {
             VideoCaptureConfig.Builder builder =
                     new VideoCaptureConfig.Builder()
-                            .setCallbackHandler(DEFAULT_HANDLER)
                             .setVideoFrameRate(DEFAULT_VIDEO_FRAME_RATE)
                             .setBitRate(DEFAULT_BIT_RATE)
                             .setIFrameInterval(DEFAULT_INTRA_FRAME_INTERVAL)
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java b/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
index bc33309..422ecdd 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/VideoCaptureConfig.java
@@ -16,7 +16,6 @@
 
 package androidx.camera.core;
 
-import android.os.Handler;
 import android.util.Rational;
 import android.util.Size;
 import android.view.Surface;
@@ -577,31 +576,6 @@
     // Implementations of ThreadConfig default methods
 
     /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @param valueIfMissing The value to return if this configuration option has not been set.
-     * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
-     * configuration.
-     */
-    @Override
-    @Nullable
-    public Handler getCallbackHandler(@Nullable Handler valueIfMissing) {
-        return retrieveOption(OPTION_CALLBACK_HANDLER, valueIfMissing);
-    }
-
-    /**
-     * Returns the default handler that will be used for callbacks.
-     *
-     * @return The stored value, if it exists in this configuration.
-     * @throws IllegalArgumentException if the option does not exist in this configuration.
-     */
-    @Override
-    @NonNull
-    public Handler getCallbackHandler() {
-        return retrieveOption(OPTION_CALLBACK_HANDLER);
-    }
-
-    /**
      * Returns the executor that will be used for background tasks.
      *
      * @param valueIfMissing The value to return if this configuration option has not been set.
@@ -1105,19 +1079,6 @@
         // Implementations of ThreadConfig.Builder default methods
 
         /**
-         * Sets the default handler that will be used for callbacks.
-         *
-         * @param handler The handler which will be used to post callbacks.
-         * @return the current Builder.
-         */
-        @Override
-        @NonNull
-        public Builder setCallbackHandler(@NonNull Handler handler) {
-            getMutableConfig().insertOption(OPTION_CALLBACK_HANDLER, handler);
-            return this;
-        }
-
-        /**
          * Sets the default executor that will be used for background tasks.
          *
          * @param executor The executor which will be used for background tasks.
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
index b2d6555..1acb499 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisNonBlockingAnalyzerTest.java
@@ -23,8 +23,6 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
 
 import androidx.camera.core.impl.utils.executor.CameraXExecutors;
 import androidx.test.filters.SmallTest;
@@ -62,7 +60,7 @@
         mImageAnalysisNonBlockingAnalyzer = new ImageAnalysisNonBlockingAnalyzer(
                 new AtomicReference<ImageAnalysis.Analyzer>(mAnalyzer),
                 ROTATION,
-                new Handler(Looper.getMainLooper()),
+                new AtomicReference<>(CameraXExecutors.mainThreadExecutor()),
                 CameraXExecutors.directExecutor()
         );
     }
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
index cec54b0..0c29520 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
+++ b/camera/camera-core/src/test/java/androidx/camera/core/ImageAnalysisTest.java
@@ -197,18 +197,18 @@
 
     private void setUpImageAnalysisWithMode(ImageAnalysis.ImageReaderMode imageReaderMode) {
         mImageAnalysis = new ImageAnalysis(new ImageAnalysisConfig.Builder()
-                .setCallbackHandler(mCallbackHandler)
                 .setBackgroundExecutor(mBackgroundExecutor)
                 .setImageQueueDepth(QUEUE_DEPTH)
                 .setImageReaderMode(imageReaderMode)
                 .build());
 
-        mImageAnalysis.setAnalyzer(new ImageAnalysis.Analyzer() {
-            @Override
-            public void analyze(ImageProxy image, int rotationDegrees) {
-                mImagesReceived.add(image.getImage());
-            }
-        });
+        mImageAnalysis.setAnalyzer(CameraXExecutors.newHandlerExecutor(mCallbackHandler),
+                new ImageAnalysis.Analyzer() {
+                    @Override
+                    public void analyze(ImageProxy image, int rotationDegrees) {
+                        mImagesReceived.add(image.getImage());
+                    }
+                });
 
         Map<String, Size> suggestedResolutionMap = new HashMap<>();
         suggestedResolutionMap.put(ShadowCameraX.DEFAULT_CAMERA_ID, DEFAULT_RESOLUTION);
diff --git a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
index 00d3d30..e8f9ecc 100644
--- a/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
+++ b/camera/integration-tests/coretestapp/src/main/java/androidx/camera/integration/core/CameraXActivity.java
@@ -24,8 +24,6 @@
 import android.graphics.SurfaceTexture;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.util.Log;
@@ -396,7 +394,6 @@
                 new ImageAnalysisConfig.Builder()
                         .setLensFacing(mCurrentCameraLensFacing)
                         .setTargetName("ImageAnalysis")
-                        .setCallbackHandler(new Handler(Looper.getMainLooper()))
                         .build();
 
         mImageAnalysis = new ImageAnalysis(config);
@@ -409,6 +406,7 @@
         }
 
         mImageAnalysis.setAnalyzer(
+                CameraXExecutors.mainThreadExecutor(),
                 new ImageAnalysis.Analyzer() {
                     @Override
                     public void analyze(ImageProxy image, int rotationDegrees) {
diff --git a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
index aac9f9e..52d9f26 100644
--- a/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
+++ b/camera/integration-tests/extensionstestapp/src/main/java/androidx/camera/integration/extensions/CameraExtensionsActivity.java
@@ -23,8 +23,6 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.StrictMode;
 import android.util.Log;
 import android.view.TextureView;
@@ -89,8 +87,6 @@
     private ImageCapture mImageCapture;
     private ImageCaptureType mCurrentImageCaptureType = ImageCaptureType.IMAGE_CAPTURE_TYPE_HDR;
 
-    private HandlerThread mHandlerThread = new HandlerThread("CameraExtensionsActivityHandler");
-
     // Espresso testing variables
     @VisibleForTesting
     CountingIdlingResource mTakePictureIdlingResource = new CountingIdlingResource("TakePicture");
@@ -249,7 +245,6 @@
                         builder);
                 if (hdrImageCaptureExtender.isExtensionAvailable()) {
                     hdrImageCaptureExtender.enableExtension();
-                    builder.setCallbackHandler(new Handler(mHandlerThread.getLooper()));
                 }
                 break;
             case IMAGE_CAPTURE_TYPE_BOKEH:
@@ -394,8 +389,6 @@
                 new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build();
         StrictMode.setVmPolicy(policy);
 
-        mHandlerThread.start();
-
         // Get params from adb extra string
         Bundle bundle = getIntent().getExtras();
         if (bundle != null) {
@@ -416,12 +409,6 @@
         setupPermissions();
     }
 
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        mHandlerThread.quitSafely();
-    }
-
     private void setupCamera() {
         try {
             // Wait for permissions before proceeding.