android - 频闪相机参数?为什么我的应用程序崩溃了?

标签 android multithreading android-camera

我在 Google 上有几个应用程序。

在这些应用程序中,我有一个频闪灯,它会在用户按下按钮时运行。 问题是,当我切换 Activity 时,应用程序崩溃了。由于在用户按回键时相机的参数被设置,我得到了一些 anr。这意味着当他们进入下一个要求使用相机的 Activity 时,应用程序崩溃了。我什至在手机上下载了该应用程序,但有时我的手机会停止响应。我试图弄清楚为什么我自己的手机开始出现问题,但我发现是我的应用程序导致了这些问题。

这是我的宝贝!我打扰了你们大约 2 周,试图弄清楚如何进行这个特定的实现。

    public void strobeTimer182() {
    superStrobe = new CountDownTimer(857, 1) {

        public void onTick(long millisUntilFinished) {
            if (millisUntilFinished % 2 == 0) {

                p.setFlashMode(Parameters.FLASH_MODE_TORCH);
                camera.setParameters(p);
                camera.startPreview();
                p.setFlashMode(Parameters.FLASH_MODE_OFF);
                camera.setParameters(p);
                camera.stopPreview();

            } else {

                p.setFlashMode(Parameters.FLASH_MODE_TORCH);
                camera.setParameters(p);
                camera.startPreview();
                p.setFlashMode(Parameters.FLASH_MODE_OFF);
                camera.setParameters(p);
                camera.stopPreview();
                 crazy.nextInt(265)));
            }
            if (millisUntilFinished == 0) {

                p.setFlashMode(Parameters.FLASH_MODE_TORCH);
                camera.setParameters(p);
                camera.startPreview();
                p.setFlashMode(Parameters.FLASH_MODE_OFF);
                camera.setParameters(p);
                camera.stopPreview();

            }

        }

我读到这是因为相机的参数在手机期望它们关闭时打开。

我的问题是。为什么会这样。您可以在代码中看到 if 语句应该完全关闭它。

这里是一些关于错误的代码。

这是我的onStart()

@Override
protected void onStart() {
    super.onStart();
     // on starting the app get the camera params
    getCamera();
    // turnOffFlash();
}

getCamera() “按钮”是按钮上的切换相机。所以当应用程序启动时.. 你必须打开它。有些手机不能很好地安装相机,所以我想我必须先检查一下。当你按下其他按钮时。音乐会播放,闪光灯会播放 857 毫秒。如你所见。

公共(public)无效 getCamera() {

Context context = this;
// Retrieve application packages that are currently installed
// on the device which includes camera, GPS etc.
PackageManager pm = context.getPackageManager();

if (!pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
    Log.e("err", "Device has no camera!"); // Toast a message to let the
    // user know that camera is not // installed in the device
    Toast.makeText(getApplicationContext(),
            "Your device doesn't have camera!",         Toast.LENGTH_SHORT)
            .show();
    button.setEnabled(false);

    // Return from the method, do nothing after this code block
    return;
} else {

    camera = Camera.open();
    p = camera.getParameters();
}

}

最佳答案

您需要确保在 Activity 挂起或暂停时松开相机。这很重要,因为一次只允许一个 Activity 拥有对相机的访问权限,如果你的 Activity 在它结束之前从未释放它,那么你的相机在你重启手机之前实际上是无用的。

此外,请确保在您的 CountDownTimer 上调用 .cancel()

// YourActivity.java
@Override
public void onPause(){
    super.onPause();

    if(superStrobe != null){
        superStrobe.cancel();
    }

    if(camera != null){
        camera.release();
        camera = null;
    }
}

编辑 2

我花了一些时间来构建您的应用的效果,并确保它能够干净利落地自行处理。漂亮的频闪!因此,您确实需要牢记以下几点:

  1. 相机在处理时需要特别小心,因此您需要谨慎设置和拆除 Activity。

  2. 使用旨在安全处理您的资源的特定方法将对您有很大帮助。除非您通过能够在他们尝试操作之前安全地检查他们正在做什么的方法进行操作,否则永远不要操作您的相机或计时器。

  3. 通常,最好在 .onResume() 中设置您的 Activity,在 .onPause()

  4. 中拆卸它

我将跳过您似乎已经理解的部分,但为您和发现此问题的任何其他人提出一些非常重要的注意事项。

因为处理 Android 摄像头有点危险,所以您会需要这些方法,而且它们应该成对出现。一个用于创建您的 Assets ,另一个用于清理它们:

// Ways to safely access the camera...
safelyAcquireCamera()
safelyReleaseCamera()

// Ways to safely access the timer...
startTimer()
stopTimer()

// Ways to setup your button...
setupButton()

// Special error handling code that makes sure to clean up the Activity if it crashes.
setupUncaughtExceptionHandler()
restoreOriginalUncaughtExceptionHandler()

你会想要这样的变量

Camera camera;
Camera.Parameters cameraParameters;
CountDownTimer countDownTimer;
UncaughtExceptionHandler originalExceptionHandler;
boolean timerIsStarted = false;

我会为你分解这个。这可能看起来很很长,但这里有很多概念:

@Override
public void onResume() {
    super.onResume();

    this.setupUncaughtExceptionHandler(); 
        // ^^ Super important!  ^^
        // This saves you if you crash!


    boolean didAcquireCamera = safelyAcquireCamera();

    setupButton(didAcquireCamera);
        // ^^ Set up your button, letting the 
        // method know if you succeeded in acquiring the camera.
        // you probably know how to implement this already. 

}

@Override
public void onPause() {
    super.onPause();

    stopTimer();
        // FIRST stop your timer.  Even though the timer has logic that
        // accounts for you doing this out of order, it's still correct 
        // to stop your running action first.

    safelyReleaseCamera();
        // Now you should release your camera using your safe method.

    releaseButtons();
        // Release your buttons...

    restoreOriginalUncaughtExceptionHandler(); 
        // ^^ Since you safely cleaned up your Activity
        // it is s time to restore the Exception Handler.
}

protected void stopTimer() {
    // This method gives you a safe way to stop the timer.
    if (countDownTimer != null) {
        countDownTimer.cancel();
        countDownTimer = null;
        timerIsStarted = false;
    }
}

protected void safelyReleaseCamera() {

    // This method gives you a safe way to release the camera.
    if (camera != null) {

        // You probably want to make sure to turn the flash off
        // if you had it on already!
        if (cameraParameters != null) {
            cameraParameters.setFlashMode(Parameters.FLASH_MODE_OFF);
            camera.setParameters(cameraParameters);
            camera.stopPreview();
        }

        camera.release();
        camera = null;
        cameraParameters = null;

    }

}

protected boolean safelyAcquireCamera() {
    safelyReleaseCamera();
      // ^^ It's very important to make sure your app DOES NOT
      // have a stray camera before you try to acquire a new one!
      // Be absolutely sure to call safe release before you try to 
      // call Camera.open(); here.


    /**
     * You seem to know how to acquire the camera already.  Just
     * return true if you succeeded and false if you didn't.
     **/


    return camera != null;
}


protected void startTimer(long millisInTheFuture, long countDownInterval) {
    stopTimer();
    timerIsStarted = true;
    countDownTimer = new CountDownTimer(millisInTheFuture, countDownInterval) {

        @Override
        public void onTick(long millisUntilFinished) {
            if (camera == null || cameraParameters == null) {
                stopTimer();
                return;
                  // Clearly things have gone awry if you lost your camera!  
                  // Bail Out.
            }

            /**
             * Do your strobing like normal in here...
             **/

        }

        @Override
        public void onFinish() {
            timerIsStarted = false;
        }
    };
    countDownTimer.start();
}

protected void setupButtons(boolean didAcquireCamera){

    /**
     *  You seem to have a good handle on
     *  how to set up a button.  Make it so!
     **/

}

protected void releaseButtons(){
    // And here you should safely set all your button handlers to null!
}

/*****************
 * Safety Methods
 *   This might be advanced, but I'll try to make it simple.
 *****************/

private void setupUncaughtExceptionHandler() {

    restoreOriginalUncaughtExceptionHandler();
        // ^^ Ensure that you're in as close to a default state as you can.

    Thread currentlyRunningThread = Thread.currentThread();
    originalExceptionHandler = currentlyRunningThread.getUncaughtExceptionHandler();
        // ^^ This is the thing that happens when your app normally crashes.
        // You'll be giving it a new, special set of instructions in this case,
        // but you'll still want to hold onto the default implmenetation.

    currentlyRunningThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

        @Override
        public void uncaughtException(Thread thread, Throwable ex) {

            stopTimer();
            safelyReleaseCamera();
                // ^^ You don't know how or why you crashed.
                // So call your safe disposal methods here!

            releaseButtons();

            if(originalExceptionHandler != null){
                originalExceptionHandler.uncaughtException(thread, ex);
                    // Now make sure you call the original handler so that 
                    // Android does its normal crash thing.
            }

            thread.setUncaughtExceptionHandler(originalExceptionHandler);
                // And restore the original crash behavior to be the default.

        }

    });
}

private void restoreOriginalUncaughtExceptionHandler() {
    if (originalExceptionHandler != null) {
        Thread.currentThread().setUncaughtExceptionHandler(originalExceptionHandler);
        originalExceptionHandler = null;
    }
}

您可能需要滚动上面的代码区域。

希望这对不同经验水平的人有所帮助。其中一些看起来像是黑魔法,但我已尽力解释其背后的原因

关于android - 频闪相机参数?为什么我的应用程序崩溃了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22692621/

相关文章:

java - 线程同步

android - 如何使用 robotium 测试 android 小部件

c# - log4net:运行时不同文件附加程序上的不同日志

android - 在 android 中的相机中捕获图像后的全屏预览

具有多个客户端的 C# 套接字(多线程方法)

Android 发送捕获的图像在某些设备中旋转 90 度

Android 在三星手机上制作照片然后发送附件

Android:拍摄的照片比预览大

java - 添加到 Fragment 内的 listView 中的 TextView 显示但为空(无文本)

android - 旋转动画后如何在ImageView上设置OnClickListener