Android 相机 android.hardware.Camera 已弃用

Android camera android.hardware.Camera deprecated

如果 android.hardware.Camera 已被弃用并且您不能使用变量 Camera,那么替代方案是什么?

API 文档

根据 android.hardware.CameraAndroid developers guide,他们说:

We recommend using the new android.hardware.camera2 API for new applications.

在有关 android.hardware.camera2 的信息页面上(上面已链接),指出:

The android.hardware.camera2 package provides an interface to individual camera devices connected to an Android device. It replaces the deprecated Camera class.

问题

当您查看该文档时,您会发现这两个相机 API 的实现非常不同。

例如在 android.hardware.camera

上获取相机方向
@Override
public int getOrientation(final int cameraId) {
    Camera.CameraInfo info = new Camera.CameraInfo();
    Camera.getCameraInfo(cameraId, info);
    return info.orientation;
}

对比android.hardware.camera2

@Override
public int getOrientation(final int cameraId) {
    try {
        CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
        String[] cameraIds = manager.getCameraIdList();
        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraIds[cameraId]);
        return characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
    } catch (CameraAccessException e) {
        // TODO handle error properly or pass it on
        return 0;
    }
}

这使得很难从一种实现切换到另一种实现并编写可以处理两种实现的代码。

请注意,在这个单一的代码示例中,我已经不得不解决以下事实:旧相机 API 使用相机 ID 的 int 原语,而新相机可以使用有 String 个对象。对于此示例,我通过使用 int 作为新 API 中的索引快速修复了该问题。如果相机的退回顺序不总是相同,这已经会导致问题。替代方法是使用 String 对象和旧 int cameraID 的 String 表示形式,这可能更安全。

一程左右

现在要解决这个巨大的差异,您可以先实现一个接口并在您的代码中引用该接口。

在这里我将列出该接口的一些代码和 2 个实现。您可以将实现限制为您实际使用的相机 API 以限制工作量。

在下一节中,我将快速解释如何加载一个或另一个。

接口包装了所有你需要的,为了限制这个例子,我这​​里只有 2 个方法。

public interface CameraSupport {
    CameraSupport open(int cameraId);
    int getOrientation(int cameraId);
}

现在有一个 class 旧相机硬件 api:

@SuppressWarnings("deprecation")
public class CameraOld implements CameraSupport {

    private Camera camera;

    @Override
    public CameraSupport open(final int cameraId) {
        this.camera = Camera.open(cameraId);
        return this;
    }

    @Override
    public int getOrientation(final int cameraId) {
       Camera.CameraInfo info = new Camera.CameraInfo();
       Camera.getCameraInfo(cameraId, info);
       return info.orientation;
    }
}

还有一个用于新硬件 api:

public class CameraNew implements CameraSupport {

    private CameraDevice camera;
    private CameraManager manager;

    public CameraNew(final Context context) {
        this.manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
    }

    @Override
    public CameraSupport open(final int cameraId) {
        try {
            String[] cameraIds = manager.getCameraIdList();
            manager.openCamera(cameraIds[cameraId], new CameraDevice.StateCallback() {
                @Override
                public void onOpened(CameraDevice camera) {
                    CameraNew.this.camera = camera;
                }

                @Override
                public void onDisconnected(CameraDevice camera) {
                    CameraNew.this.camera = camera;
                    // TODO handle
                }

                @Override
                public void onError(CameraDevice camera, int error) {
                    CameraNew.this.camera = camera;
                    // TODO handle
                }
            }, null);
        } catch (Exception e) {
            // TODO handle
        }
        return this;
    }

    @Override
    public int getOrientation(final int cameraId) {
        try {
            String[] cameraIds = manager.getCameraIdList();
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraIds[cameraId]);
            return characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
        } catch (CameraAccessException e) {
            // TODO handle
            return 0;
        }
    }
}

正在加载正确的 API

现在要加载您的 CameraOldCameraNew class,您必须检查 API 级别,因为 CameraNew 只能从 api 21级.

如果您已经设置了依赖注入,您可以在提供 CameraSupport 实现时在您的模块中这样做。示例:

@Module public class CameraModule {

    @Provides
    CameraSupport provideCameraSupport(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            return new CameraNew(context);
        } else {
            return new CameraOld();
        }
    } 
}

如果您不使用 DI,您可以制作一个实用程序或使用工厂模式来创建合适的实用程序。重要的是检查了 API 级别。

面临同样的问题,通过已弃用的摄像头支持旧设备 API 并且当前设备需要新的 Camera2 API 并进入未来;我 运行 遇到了同样的问题——并且 没有 找到了一个连接两个 API 的第 3 方库,可能是因为它们非常不同,我求助于基本的 OOP 原则

这 2 个 API 明显不同,因此对于期望旧 API 中呈现的接口的客户端对象来说,互换它们会产生问题。新的 API 具有不同的对象和不同的方法,使用不同的体系结构构建。爱 Google,但是 ragnabbit!真令人沮丧。

所以我创建了一个界面,只关注我的应用程序需要的相机功能,并为 both APIs 创建了一个简单的包装器来实现那个界面。 这样我的相机 activity 就不必关心它 运行 在哪个平台上...

我还设置了一个Singleton来管理API(s);使用旧 Android OS 设备的接口实例化旧的 API 的包装器,并使用新的 API 的新设备的包装器 class API。单例具有获取 API 级别然后实例化正确对象的典型代码。

两个包装器 classes 使用相同的接口,所以应用程序运行在 Jellybean 还是 Marshmallow 上并不重要——只要界面使用相同的方法签名为我的应用程序提供它从相机 API 需要的东西;对于 Android.

的新旧版本,相机在应用程序中的运行方式相同

Singleton 还可以做一些与 API 无关的相关事情——比如检测设备上确实有摄像头,并保存到媒体库。

希望我的想法能帮到你。

现在我们必须使用 android.hardware.camera2 作为 android.hardware。相机已弃用,它只能在 API >23 FlashLight 上使用

   public class MainActivity extends AppCompatActivity {

     Button button;

     Boolean light=true;

     CameraDevice cameraDevice;

     private CameraManager cameraManager;

     private CameraCharacteristics cameraCharacteristics;

     String cameraId;

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button=(Button)findViewById(R.id.button);
        cameraManager = (CameraManager) 
        getSystemService(Context.CAMERA_SERVICE);
        try {
          cameraId = cameraManager.getCameraIdList()[0];
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(light){
                    try {

                        cameraManager.setTorchMode(cameraId,true);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }

                    light=false;}
                    else {

                    try {

                      cameraManager.setTorchMode(cameraId,false);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }


                    light=true;
                    }


            }
        });
    }
}

此处提供的关于使用哪个相机 api 的答案是错误的。或者更好地说它们是不够的。

一些 phones(例如三星 Galaxy S6)可能高于 api 级别 21,但仍可能不支持 Camera2 api。

CameraCharacteristics mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
Integer level = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null || level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
    return false;
}

Camera2Api 中的 CameraManager class 有一个读取相机特性的方法。您应该检查硬件智能设备是否支持 Camera2 Api。

但是,如果您真的想让它适用于严肃的应用程序,还有更多问题需要处理:例如,自动闪光选项可能不适用于某些设备,或者 phone 的电池电量可能会导致相机上的 RuntimeException 或 phone 可能 return 无效的相机 ID 等

所以最好的方法是有一个回退机制,因为某些原因 Camera2 无法启动,您可以尝试 Camera1,如果也失败,您可以调用 Android 为您打开默认相机。

 if ( getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {

          CameraManager cameraManager=(CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);


           try {
               String cameraId = cameraManager.getCameraIdList()[0];
               cameraManager.setTorchMode(cameraId,true);
           } catch (CameraAccessException e) {
               e.printStackTrace();
           }


 }