2015-01-09 113 views
4

我已經看過所有我能找到的SO文章,但沒有一個解決方案適用於我。Camera.open()阻止UI線程

當調用Camera.open()時,在UI線程被阻塞的地方存在3秒(給定或帶入)延遲。我試圖把它放在後臺線程中。我目前正在使用找到的解決方案here(粘貼在下面),但'wait'方法是同步的,所以它也阻止了UI線程。

我正在做的是加載這個片段,顯示一個進度微調,直到相機好走,然後在屏幕上顯示相機,但這種延遲正在殺死我,我似乎無法找到任何真的很好的解決方案。

我的片段:

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"); 
      } 
     } 
    } 
} 

UPDATE

感謝MeetTitan,在需要的時候,我能夠通過在後臺線程和張貼到UI藏在心裏得到這個工作很順利。這是任何人誰可能需要在未來:)

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 && 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); 
         } 
        }); 
       } 
      } 
     } 
    } 
} 
+1

嘿,還記得我?你不需要'notify()',因爲沒有任何'wait()'。你也可以簡化你的線程,對於我所謂的「靜態線程」來說,處理程序對我來說有點笨拙,這是一個每次都做同樣工作的線程。我將使用我在我的答案中顯示的方法,重寫線程的run()方法,然後調用'new CameraHandlerThread()。start()'將在新線程中運行run()'方法,避免發送任何東西,只是讓它做它的工作和死亡。 – MeetTitan 2015-01-10 00:35:24

+0

我已經有了這個代碼,並將其他所有東西裝入它。你是完全正確的。再次感謝 – Psest328 2015-01-10 00:38:18

回答

5

你能不能繼續你的線程和UI的呼叫組與yourContext.runOnUiThread()方法一起命令運行的代碼?然後你背景任何阻止代碼,等待相機準備就緒,並從後臺線程更新ui。

例如:

private class CameraHandlerThread extends ... { 
    public void run() { 
     getCamera(); 
     yourContext.runOnUiThread(new Runnable(){ 
      public void run() 
      { 
       ... 
      } 
     }); 
    } 
} 

然後,你可以簡單地new CameraHandlerThread().start();

+0

我仍然需要等待Camera.open,以便mPreview不會返回null,並等待()塊 – Psest328 2015-01-09 22:08:52

+0

@WizardKnight,將它放入您的線程中,以便順序運行。不要在後臺線程中wait()',let'camera.open()'阻塞,然後在後臺線程中更新'mPreview'(以及在打開相機後需要更新的任何其他對象)調用'camera.open()'。它應該按順序運行。 – MeetTitan 2015-01-09 22:15:28

+0

...這隻夠瘋了,工作,哈哈。現在要給它一個鏡頭 – Psest328 2015-01-09 22:18:19