Camera API not working on KITKAT

2019-02-05 06:39发布

问题:

I have a really strange problem. The following code I have is used to take a picture on button click. It works properly on Jelly Bean phones, but not on 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;
 }
} 

Manifest file:

<?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>

I have put quite a few toast messages and print statements for debugging purposes. I have not posted logcat here, because, when I run this on a KitKat phone, I get nothing on logcat. No exception or warning.

When I run this on a jellybean phone, it runs properly, displaying all the toasts and prints and takes the picture.

When I run this on Kitkat, I do not get any debugging messages after the

camera.startPreview();
System.out.println("Camera preview has started.");

I suspect problem is with the takePicture API, but I am unable to debug it.

Please help me resolve this issue.

EDIT: After further analysis, I found the reason for issue. The PhotoHandler object gets invoked successfully, but the onPictureTaken method is not called, probably because it did not get information that picture was clicked by the camera. I don't know why. Please help me fix this issue.

回答1:

I have observed that you are not assigning any surface holder to the camera. Giving a preview surface to the camera is important.

According to the docs here:

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

follow the code suggested by the doc. Taking a picture without a preview is a big security concern. Android guys might have fixed this in kitkat.

You might have missed that part of the code while pasting here so as an added concern also check that you are executing your code 'camera.takePicture(null,null,callback)' inside the callback method 'onSurfaceCreated' of SurfaceHolder.

You can get all the relevant code at aforementioned link.



回答2:

KitKat's garbage collection works differently than previous APIs.

I'm guessing that the PhotoHandler object that you pass to the takePicture() method is getting garbage collected before onPictureTaken can be called.

Try making a PhotoHandler object as in instance variable in your MainActivity.

At the top of the class:

PhotoHandler photoHandler;

Then in onCreate()

photoHandler = new PhotoHandler(getApplicationContext());

Then when you call takePicture():

camera.takePicture(null, null, photoHandler);


回答3:

Add uses-feature in your manifest file :

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

and check this link. It might be help you.