相机 API 适用于 Jelly Bean 但不适用于 Kitkat

Posted

技术标签:

【中文标题】相机 API 适用于 Jelly Bean 但不适用于 Kitkat【英文标题】:Camera API working on Jelly Bean but not Kitkat 【发布时间】:2014-05-02 18:48:38 【问题描述】:

我有一个非常奇怪的问题。我拥有的以下代码用于在按钮单击时拍照。它可以在 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 手机上运行此程序时,它运行正常,显示所有祝酒词和打印并拍照。

当我在 Kitkat 上运行它时,在

之后我没有收到任何调试消息
camera.startPreview();
System.out.println("Camera preview has started.");

我怀疑 takePicture API 有问题,但我无法调试它。

编辑

经过进一步分析,我找到了问题的原因。 PhotoHandler对象调用成功,但onPictureTaken方法没有被调用,可能是因为它没有得到图片被相机点击的信息。我不知道为什么。

【问题讨论】:

查看下面的链接帮助你***.com/questions/22576049/… 我的问题不在于画廊。它很可能与 takePicture API 相关。此外,我没有像该帖子中那样得到任何例外。 您是否尝试过附加调试器并单步执行它以查看它的去向? 不,我没有……你能给我一个链接告诉我怎么做吗? 【参考方案1】:

我观察到您没有为相机分配任何表面支架。为相机提供预览表面很重要。

根据此处的文档:

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

遵循文档建议的代码。在没有预览的情况下拍照是一个很大的安全问题。 Android 人可能已经在 kitkat 中解决了这个问题。

您可能在粘贴此处时错过了那部分代码,因此另外需要注意的是,还要检查您是否在 SurfaceHolder 的回调方法“onSurfaceCreated”内执行代码“camera.takePicture(null,null,callback)”。

您可以在上述链接中获取所有相关代码。

【讨论】:

您的链接帮了很多忙 :) 我犯的主要错误是没有分配表面支架。显然这是 Kitkat 手机的必需品,而在 JB 手机中没有它也可以工作。感谢您的所有帮助。 :)【参考方案2】:

KitKat 的垃圾收集工作方式与以前的 API 不同。

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

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

全班第一:

PhotoHandler photoHandler;

然后在onCreate()

photoHandler = new PhotoHandler(getApplicationContext());

那么当你打电话给takePicture():

camera.takePicture(null, null, photoHandler);

【讨论】:

我试过你的方法,它不起作用。当您对 PhotoHandler 说“在班级的顶端”时,您是指作为班级成员还是在班级之外 类成员——它们在java中被称为实例变量 这种情况只发生在 KitKat 上,这让我 99% 确信某些东西过早地收集垃圾。例如,可能是 OnClickListener。您可以使用调试器和断点或一堆日志来解决这个问题,告诉您某些内容是否为空,而它不应该为空。 Matt Logan 你能帮我处理一下***.com/questions/26714771/…【参考方案3】:

在清单文件中添加uses-feature

<uses-feature android:name="android.hardware.camera" />
 <uses-feature android:name="android.hardware.camera.autofocus" />

并检查此link。可能对你有帮助。

【讨论】:

以上是关于相机 API 适用于 Jelly Bean 但不适用于 Kitkat的主要内容,如果未能解决你的问题,请参考以下文章

我的 webview 视频适用于模拟器,但不适用于设备

相机在 android jelly bean 中崩溃

Jelly Bean WebView 不适用于文本框的 HTML maxlength 属性

iOS 上的 Plupload 仅适用于新照片,不适用于相机胶卷

如何创建一个不包含 Android Kitkat 但适用于 Jelly Beans 的 Android 项目

使用 CORS 与 LocomotiveJs 一起休息 API。适用于本地机器,但不适用于 Amazon 或 Heroku