Camera.open()阻止UI线程 [英] Camera.open() blocking UI thread

查看:140
本文介绍了Camera.open()阻止UI线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看了所有关于SO的文章,但是没有一种解决方案对我有用.

I've looked at all the SO articles I could find on this but none of the solutions work for me.

当调用Camera.open()时,UI线程被阻塞有3秒(延迟或延迟)的延迟.我试图将其放在后台线程中.我目前正在使用此处(下面已粘贴)找到的解决方案,但是"wait"方法是同步的,因此它阻止了UI线程.

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 here (pasted below) but the 'wait' method is synchronous so it blocks the UI thread too.

我想要做的是加载此片段,显示一个进度微调框,直到相机可以运行为止,然后在屏幕上显示该相机,但是这种延迟使我丧命,我似乎找不到任何真正的很好的解决方案.

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.

我的片段:

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

UPDATE

感谢MeetTitan,通过将所有内容保留在后台线程中并在需要时发布到UI,我能够使此工作非常顺利.这是将来可能需要的任何人的工作代码:)

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 :)

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

推荐答案

是否可以继续线程并与yourContext.runOnUiThread()方法一起调用ui命令组?然后,您将任何阻塞代码作为背景,等待摄像机准备就绪,然后从后台线程更新ui.

Could you not continue your thread and call groups of ui commands together with the yourContext.runOnUiThread() method? Then you background any blocking code, wait for the camera to be ready, and update the ui from the background thread.

例如:

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

然后您可以简单地new CameraHandlerThread().start();

这篇关于Camera.open()阻止UI线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆