title: Camera.open() blocking UI thread
body: <p>I've looked at all the SO articles I could find on this but none of the solutions work for me.</p>
  
  <p>When called Camera.open(), there is a 3 second (give or take) delay where the UI thread is blocked.  I have attempted to put that in a background thread.  I'm currently using the solution found <a href="https://stackoverflow.com/a/19154438/1409132">here</a> (pasted below) but the 'wait' method is synchronous so it blocks the UI thread too.</p>
  
  <p>What I'm looking to do is load this fragment, show a progress spinner until the camera is good to go, then show the camera on screen, but this delay is killing me and I can't seem to find any really good solution on it.  </p>
  
  <p>My fragment:</p>
  
  <pre><code>public class BarcodeFinderFragment extends Fragment implements View.OnClickListener, Camera.AutoFocusCallback, Camera.PreviewCallback {
  
      private static final String CAMERA_THREAD_NAME = "CAMERA_THREAD_NAME";
      private Camera mCamera;
  
      private CamViewFinder mPreview;
      private Handler autoFocusHandler;
      private boolean previewing = true;
      private Button noScan;
      private Button noBarcode;
      private FrameLayout preview;
      private BarcodeFinderCallback callBack;
      private ImageScanner scanner;
  
      private CameraHandlerThread mThread = null;
  
  
      private BarcodeFinderCallback dummyCallback = new BarcodeFinderCallback() {
          @Override
          public void onNoScanClicked() {
  
          }
  
          @Override
          public void onNoBarcodeClicked() {
  
          }
  
          @Override
          public void finishActivity() {
  
          }
  
          @Override
          public void setActivityResult(Bundle bundle) {
  
          }
  
          @Override
          public void showProgressDialog(boolean showProgress) {
  
          }
      };
  
      public static BarcodeFinderFragment newInstance() {
          return new BarcodeFinderFragment();
      }
  
      @Override
      public void onAttach(Activity activity) {
          super.onAttach(activity);
  
          try {
              callBack = (BarcodeFinderActivity) activity;
          } catch (ClassCastException e) {
              throw new ClassCastException(activity.toString()
                      + " must implement OnHeadlineSelectedListener");
          }
      }
  
      @Override
      public void onCreate(final Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
  
  
      }
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          View view = inflater.inflate(R.layout.fragment_barcode_finder, container, false);
  
          noScan = (Button) view.findViewById(R.id.btnNoScan);
          noBarcode = (Button) view.findViewById(R.id.btnNobarcode);
          preview = (FrameLayout) view.findViewById(R.id.cameraPreview);
          noScan.setOnClickListener(this);
          noBarcode.setOnClickListener(this);
  
          return view;
      }
  
      @Override
      public void onResume() {
          super.onResume();
  
          autoFocusHandler = new Handler();
  
          //Instance barcode scanner
          scanner = new ImageScanner();
          scanner.setConfig(0, Config.X_DENSITY, 3);
          scanner.setConfig(0, Config.Y_DENSITY, 3);
  
          openCamera();
  
          mPreview = new CamViewFinder(getActivity(), mCamera, BarcodeFinderFragment.this, BarcodeFinderFragment.this);
          preview.addView(mPreview);
          callBack.showProgressDialog(false);
      }
  
      private void getCamera() {
          mCamera = null;
          try {
              mCamera = Camera.open();
          } catch (final Exception e) {
              Log.d("BarcodeFinderFragment", e.toString());
          }
      }
  
      private void openCamera() {
          if (mThread == null)
              mThread = new CameraHandlerThread(CAMERA_THREAD_NAME);
  
          synchronized (mThread) {
              mThread.openCamera();
          }
      }
  
      @Override
      public void onPause() {
          super.onPause();
          releaseCamera();
      }
  
      @Override
      public void onDetach() {
          super.onDetach();
  
          callBack = dummyCallback;
      }
  
      private Runnable doAutoFocus() {
          return new Runnable() {
              @Override
              public void run() {
                  if (previewing) {
                      mCamera.autoFocus(BarcodeFinderFragment.this);
                  }
              }
          };
      }
  
      private void releaseCamera() {
          if (mCamera != null) {
              previewing = false;
              mCamera.stopPreview();
              mCamera.setPreviewCallback(null);
              mCamera.release();
              mCamera = null;
          }
  
          callBack.finishActivity();
      }
  
      @Override
      public void onClick(View v) {
          switch (v.getId()) {
              case R.id.btnNoScan:
                  callBack.onNoScanClicked();
                  break;
  
              case R.id.btnNobarcode:
                  callBack.onNoBarcodeClicked();
                  break;
          }
      }
  
      @Override
      public void onAutoFocus(boolean success, Camera camera) {
          autoFocusHandler.postDelayed(doAutoFocus(), 1000);
      }
  
      @Override
      public void onPreviewFrame(byte[] data, Camera camera) {
  
          final Camera.Parameters parameters = camera.getParameters();
          final Camera.Size size = parameters.getPreviewSize();
  
          final Image barcode = new Image(size.width, size.height, "Y800");
          barcode.setData(data);
  
          final int result = scanner.scanImage(barcode);
  
          if (result != 0) {
              previewing = false;
              mCamera.setPreviewCallback(null);
              mCamera.stopPreview();
  
              final SymbolSet syms = scanner.getResults();
              for (final Symbol sym : syms) {
                  final Bundle bundle = new Bundle();
                  bundle.putString("result", sym.getData());
                  bundle.putString("codeType", "" + sym.getType());
  
                  callBack.setActivityResult(bundle);
              }
          }
      }
  
      public interface BarcodeFinderCallback {
          void onNoScanClicked();
  
          void onNoBarcodeClicked();
  
          void finishActivity();
  
          void setActivityResult(Bundle bundle);
  
          void showProgressDialog(boolean showProgress);
      }
  
      private class CameraHandlerThread extends HandlerThread {
  
          Handler mHandler = null;
  
          public CameraHandlerThread(String name) {
              super(name);
              callBack.showProgressDialog(true);
              start();
  
              mHandler = new Handler(getLooper());
          }
  
          synchronized void notifyCameraOpened() {
              notify();
          }
  
          void openCamera() {
              mHandler.post(new Runnable() {
                  @Override
                  public void run() {
                      getCamera();
                      notifyCameraOpened();
                  }
              });
  
              try {
                  wait();
              } catch (InterruptedException e) {
                  Log.d("BarcodeFinderFragment", "wait was interrupted");
              }
          }
      }
  }
  </code></pre>
  
  <p><strong><em>UPDATE</em></strong></p>
  
  <p>Thanks to MeetTitan, I was able to get this working very smoothly by keeping everything in the background thread and posting to the UI when needed.  Here is the working code for anybody who may need it in the future :)</p>
  
  <pre><code>public class BarcodeFinderFragment extends Fragment implements View.OnClickListener {
  
      private static final String CAMERA_THREAD_NAME = "CAMERA_THREAD_NAME";
      private Camera mCamera;
      private CamViewFinder mPreview;
      private Handler autoFocusHandler;
      private FrameLayout preview;
      private ImageScanner scanner;
      private boolean previewing = true;
      private CameraHandlerThread mThread = null;
  
      private BarcodeFinderCallback callBack;
      private BarcodeFinderCallback dummyCallback = new BarcodeFinderCallback() {
          @Override
          public void onNoScanClicked() {
          }
  
          @Override
          public void onNoBarcodeClicked() {
          }
  
          @Override
          public void finishActivity() {
          }
  
          @Override
          public void setActivityResult(int resultCode, Bundle bundle) {
          }
  
          @Override
          public void showProgressDialog(boolean showProgress) {
          }
      };
  
      public static BarcodeFinderFragment newInstance() {
          return new BarcodeFinderFragment();
      }
  
      @Override
      public void onAttach(Activity activity) {
          super.onAttach(activity);
  
          try {
              callBack = (BarcodeFinderActivity) activity;
          } catch (ClassCastException e) {
              throw new ClassCastException(activity.toString()
                      + " must implement BarcodeFinderCallback");
          }
      }
  
      @Override
      public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
          View view = inflater.inflate(R.layout.fragment_barcode_finder, container, false);
  
          Button noScan = (Button) view.findViewById(R.id.btnNoScan);
          Button noBarcode = (Button) view.findViewById(R.id.btnNobarcode);
          preview = (FrameLayout) view.findViewById(R.id.cameraPreview);
          noScan.setOnClickListener(this);
          noBarcode.setOnClickListener(this);
  
          return view;
      }
  
      @Override
      public void onResume() {
          super.onResume();
  
          autoFocusHandler = new Handler();
  
          //Instance barcode scanner
          scanner = new ImageScanner();
          scanner.setConfig(0, Config.X_DENSITY, 3);
          scanner.setConfig(0, Config.Y_DENSITY, 3);
  
          callBack.showProgressDialog(true);
          openCamera();
  
      }
  
      private void openCamera() {
          if (mThread == null) {
              try {
                  mThread = new CameraHandlerThread(CAMERA_THREAD_NAME);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
          synchronized (mThread) {
              mThread.openCamera();
          }
      }
  
      @Override
      public void onPause() {
          super.onPause();
          releaseCamera();
  
          if (mThread != null &amp;&amp; mThread.isAlive())
              mThread.interrupt();
      }
  
      @Override
      public void onDetach() {
          super.onDetach();
  
          callBack = dummyCallback;
      }
  
      private void releaseCamera() {
          if (mCamera != null) {
              previewing = false;
              mCamera.stopPreview();
              mCamera.setPreviewCallback(null);
              mCamera.release();
              mCamera = null;
          }
  
          callBack.finishActivity();
      }
  
      @Override
      public void onClick(View v) {
          switch (v.getId()) {
              case R.id.btnNoScan:
                  callBack.onNoScanClicked();
                  break;
  
              case R.id.btnNobarcode:
                  callBack.onNoBarcodeClicked();
                  break;
          }
      }
  
      public interface BarcodeFinderCallback {
          void onNoScanClicked();
  
          void onNoBarcodeClicked();
  
          void finishActivity();
  
          void setActivityResult(int resultCode, Bundle bundle);
  
          void showProgressDialog(boolean showProgress);
      }
  
      private class CameraHandlerThread extends HandlerThread implements Camera.AutoFocusCallback, Camera.PreviewCallback {
  
          Handler mHandler = null;
  
          public CameraHandlerThread(String name) throws InterruptedException {
              super(name);
              callBack.showProgressDialog(true);
              start();
  
              mHandler = new Handler(getLooper());
          }
  
          void openCamera() {
              mHandler.post(new Runnable() {
                  @Override
                  public void run() {
                      mCamera = null;
                      try {
                          mCamera = Camera.open();
                      } catch (final Exception e) {
                          Log.d("BarcodeFinderFragment", e.toString());
                          callBack.setActivityResult(Activity.RESULT_CANCELED, null);
                          interrupt();
                      }
                      notifyCameraOpened();
  
                      getActivity().runOnUiThread(new Runnable() {
                          @Override
                          public void run() {
                              mPreview = new CamViewFinder(getActivity(), mCamera, CameraHandlerThread.this, CameraHandlerThread.this);
                              preview.addView(mPreview);
  
                              new Handler().postDelayed(new Runnable() {
                                  @Override
                                  public void run() {
                                      callBack.showProgressDialog(false);
                                  }
                              }, 500);
                          }
                      });
                  }
              });
          }
  
          synchronized void notifyCameraOpened() {
              notify();
  
          }
  
          @Override
          public void onAutoFocus(boolean success, Camera camera) {
              autoFocusHandler.postDelayed(new Runnable() {
                  @Override
                  public void run() {
                      if (previewing) {
                          getActivity().runOnUiThread(new Runnable() {
                              @Override
                              public void run() {
                                  mCamera.autoFocus(CameraHandlerThread.this);
                              }
                          });
                      }
                  }
              }, 1000);
          }
  
          @Override
          public void onPreviewFrame(byte[] data, Camera camera) {
  
              final Camera.Parameters parameters = camera.getParameters();
              final Camera.Size size = parameters.getPreviewSize();
  
              final Image barcode = new Image(size.width, size.height, "Y800");
              barcode.setData(data);
  
              final int result = scanner.scanImage(barcode);
  
              if (result != 0) {
                  previewing = false;
                  mCamera.setPreviewCallback(null);
                  mCamera.stopPreview();
  
                  final SymbolSet syms = scanner.getResults();
                  for (final Symbol sym : syms) {
                      final Bundle bundle = new Bundle();
                      bundle.putString("result", sym.getData());
                      bundle.putString("codeType", "" + sym.getType());
  
                      getActivity().runOnUiThread(new Runnable() {
                          @Override
                          public void run() {
                              callBack.setActivityResult(Activity.RESULT_OK, bundle);
                          }
                      });
                  }
              }
          }
      }
  }
  </code></pre>