相机API在Jelly Bean上正常工作,但在Kitkat上不起作用。

6

我遇到了一个非常奇怪的问题。以下代码是用于在按钮点击时拍照的。它在Jelly Bean手机上正常工作,但在Kitkat上不起作用:

MainActivity.java:

package com.example.takepic;

import android.app.Activity;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;


public class MainActivity extends Activity {
  private final static String DEBUG_TAG = "MakePhotoActivity";
  private Camera camera;
  private Button capture = null;
  private int cameraId = 0;

  @Override
  public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  capture = (Button)findViewById(R.id.captureBack);
  capture.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
         camera.startPreview(); //After this, nothing gets printed, and picture does not get taken
        System.out.println("Camera preview has started.");
            camera.takePicture(null, null, new PhotoHandler(getApplicationContext()));
    }
});
// do we have a camera?
if (!getPackageManager()
    .hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
  Toast.makeText(this, "No camera on this device", Toast.LENGTH_LONG)
      .show();
} else {
  cameraId = findBackFacingCamera();
  if (cameraId < 0) {
    Toast.makeText(this, "No back facing camera found.",
        Toast.LENGTH_LONG).show();
  } else {
    camera = Camera.open(cameraId);
  }
}
  }



 private int findBackFacingCamera() {
int cameraId = -1;
// Search for the front facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
  CameraInfo info = new CameraInfo();
  Camera.getCameraInfo(i, info);
  if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
    Log.d(DEBUG_TAG, "Camera found");
    cameraId = i;
    break;
  }
}
return cameraId;
}

  @Override
  protected void onPause() {
if (camera != null) {
  camera.release();
  camera = null;
}
super.onPause();
 }

} 

PhotoHandler.java:

package com.example.takepic;



import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

public class PhotoHandler implements PictureCallback {

  private final Context context;

  public PhotoHandler(Context context) {
    this.context = context;
  }

  @Override
 public void onPictureTaken(byte[] data, Camera camera) {

   File pictureFileDir = getDir();
  Toast.makeText(context, "Entered onPictureTaken", Toast.LENGTH_LONG).show();
   if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {

  Log.d("Directory error", "Can't create directory to save image.");
  Toast.makeText(context, "Can't create directory to save image.",
      Toast.LENGTH_LONG).show();
  return;

 }

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
String date = dateFormat.format(new Date());
String photoFile = "Picture_" + date + ".jpg";

String filename = pictureFileDir.getPath() + File.separator + photoFile;

File pictureFile = new File(filename);

try {
  FileOutputStream fos = new FileOutputStream(pictureFile);
  fos.write(data);
  fos.close();
  Toast.makeText(context, "New Image saved:" + photoFile,
      Toast.LENGTH_LONG).show();
} catch (Exception error) {
  Log.d("File saving error", "File" + filename + "not saved: "
      + error.getMessage());
  Toast.makeText(context, "Image could not be saved.",
      Toast.LENGTH_LONG).show();
  }
}

  private File getDir() {
  //  File sdDir =    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
 File sdDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
 Toast.makeText(context, ("Path : "+sdDir.getAbsolutePath()), Toast.LENGTH_LONG).show();
  return sdDir;
 }
} 

清单文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.takepic"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="15" />
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
    <activity
        android:name="com.example.takepic.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

我为了调试目的添加了很多toast消息和打印语句。我没有在这里发布logcat,因为当我在KitKat手机上运行时,logcat中什么也没有显示出来,没有异常或警告。当我在jellybean手机上运行时,它可以正常运行,显示所有的toast和print,并拍摄照片。但是在Kitkat上运行时,

后面就没有任何调试信息了。
camera.startPreview();
System.out.println("Camera preview has started.");

我怀疑问题出在takePicture API上,但是我无法调试它。
编辑
经过进一步分析,我发现了问题的原因。PhotoHandler对象被成功调用,但是onPictureTaken方法没有被调用,可能是因为它没有得到相机拍摄照片的信息。我不知道为什么。

请查看以下链接以获取帮助:https://dev59.com/B33aa4cB1Zd3GeqPjeUq - KeTaN
我的问题不在于相册,而很可能是takePicture API。此外,我没有像那篇帖子中那样收到任何异常。 - SoulRayder
你尝试过连接调试器并逐步执行以查看它的流程吗? - fadden
不,我还没有...你能否给我一个链接,告诉我如何做到这一点? - SoulRayder
3个回答

5
我注意到您没有给相机分配任何表面持有者。给相机提供预览表面非常重要。
根据这里的文档:

http://developer.android.com/guide/topics/media/camera.html

跟随文档建议的代码。在没有预览的情况下拍照是一个很大的安全问题。Android开发者可能已经在Kitkat中修复了这个问题。你可能错过了这部分代码,因此还要检查一下,在SurfaceHolder的回调方法'onSurfaceCreated'中执行你的代码'camera.takePicture(null,null,callback)'。你可以在上述链接中获取所有相关的代码。

你的链接帮了很大的忙 :) 我犯的主要错误是没有分配一个表面持有者。显然,这在Kitkat手机上是必需的,而在JB手机上没有它也可以工作.. 感谢你的所有帮助。 :) - SoulRayder

2

KitKat的垃圾回收机制与之前的API不同。

我猜测你传递给takePicture()方法的PhotoHandler对象在onPictureTaken被调用之前被垃圾回收了。

尝试在MainActivity中创建一个PhotoHandler对象作为实例变量。

在类的顶部:

PhotoHandler photoHandler;

然后在onCreate()

photoHandler = new PhotoHandler(getApplicationContext());

那么当你调用takePicture()时:
camera.takePicture(null, null, photoHandler);

我尝试了你的方法,但它并不起作用。当你说“在PhotoHandler类的顶部”时,是指作为类成员还是在类外部 - SoulRayder
类成员 - 在Java中它们被称为实例变量。 - Matt Logan
这只在KitKat上发生的事实让我99%确定某些东西被垃圾回收得太早了。比如可能是OnClickListener。你需要用调试器和断点或一堆日志来找出是否有东西为空但不应该为空,这取决于你自己。 - Matt Logan
Matt Logan,你能否帮我解决关于http://stackoverflow.com/questions/26714771/android-camera-take-picture-failed-issue的问题? - koutuk

1
在您的清单文件中添加uses-feature
<uses-feature android:name="android.hardware.camera" />
 <uses-feature android:name="android.hardware.camera.autofocus" />

请检查这个链接。它可能会对您有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接