Android 5.1-手电筒应用小思路(三)

时间:2022-04-12 20:41:06
 前面记录了两篇关于Android 4.4开发 Android手电筒小应用的一些心得,当然只是从上层来做一些简单的处理。     然而随着Android的发展和更新,Android Camera类已被标识为过时的类,官方建议开发者使用Camera2。在Android官方文档中也提到过(Android官方开发指南-Camera(相机)。 在Android5.1之前版本的SDK中,看不到android.hardware.camera2,一般看到的都是android.hardware.camera类。android.hardware.camera在Android4.4的版本就已经过时 了,只不过在Android 4.4 SDK中并没有集成android.hardware.camera2,但是Android工程源码中是有的。     这两者有多大区别呢,下面有图有真相,乍一看,小伙伴们都惊呆了,这差别也太大了。不过,我个人感觉,底层可能差别不会太大,只不过上层的封装方式变了。如果你熟悉Android的代码结构,光看android.hardware.camera2中的各个类名,你都能猜出Camera2 需要通过CAMERA_SERVICE 来使用。     Android 5.1-手电筒应用小思路(三) Android 5.1-手电筒应用小思路(三)    Android 5.1-手电筒应用小思路(三)     而在CameraManager.java中前面的注释也告诉我们需要通过Context.getSystemService()来获得这个类的一个实例,eg:     CameraManager manager = (CameraManager)mContext.getSystemService(Context.CAMERA_SERVICE);     要通过FLASH来实现手电筒,就要open camera,要使用CameraManager中的openCamera方法:     public void openCamera(String cameraId, final CameraDevice.StateCallback callback, Handler handler)     很惊讶,怎么,openCamera()函数是 void类型。但他确实是这样的。cameraId 通过 getCameraIdList()来获取,此函数返回的是一个数组,并非是需要String 类型。也直接告诉我们,要获得CameraId 还需要做处理。按照正常的情况,一款手机通常有两个Camera,分前Camera和后Camera,这个在android.hardware.camera中就有代码表示(camerainfo):     
/**
* The facing of the camera is opposite to that of the screen.
*/
public static final int CAMERA_FACING_BACK = 0;
/**
* The facing of the camera is the same as that of the screen.
*/
public static final int CAMERA_FACING_FRONT = 1;

    而后camera通常才有闪光灯的功能。所以就有必要对CameraId数组进行循环查询各个Camera的功能特性。CameraManager类中的getCameraCharacteristics(String cameraId)函数正好满足需要,此函数的说明告诉我们,此函数查询对应ID camera的功能,返回CameraCharacteristics 类型的东东。于是大胆的在CameraCharacteristics.java中找FLASH相关的代码:FLASH_INFO_AVAILABLE,通过这个可判断有flash特性的camera ID。     为什么openCamera()函数返回的是void类型,且看此函数的第二个参数,是一个callback。CameraDevice.StateCallback 是一个抽象类。在代码中也必须new 此类,来实现此类中的抽象方法。此类中也说明,必须实现此抽象类的onOpened(CameraDevice camera)抽象方法。虽然没有函数体,但是通过它的说明可以了解道,当它被调用的时候,说明camera device 可以使用,所以openCamera并没有返回我们需要的camera,而在这个函数被调用以后,我们就可以获得camera device,也就打开了cameara。          成功获得camera devices以后,就要去设置FLAHS的MODE,而android.hardware.camera2设置Flash mode的方式,跟之前截然不同,之前是通过设置Parameters,android.hardware.camera2 则是通过设置一组键值对来实现。刚开始我也不知道怎么下手,在通过大致了解CameraMetadata 和 CaptureRequest 两个类的源码以后,才发现是以键值对的形式去设置,两个类中都有说明,而且遥相呼应,不难理解。设置Flash mode的大致代码如下:     
    CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW);
builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);

         当然,android.hardware.camera 在Android 5.1还是可以使用的。只不过我在实验的过程中碰到一个奇怪的事情,使用android.hardware.camera实现的手电筒分别在Android4.4 和Android 5.1手机上使用,如果手电筒的代码中没有setPreviewTexture(),那么这个手电筒在Android4.4上可以点亮闪光灯,在Android 5.1不能点亮闪光灯。          最后贴点代码,以作纪念!    
package com.example.richtorch;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.os.PowerManager;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.Toast;

public class TorchActivity extends Activity{
private ImageView mTorImageView;
private boolean torchState;
private static PowerManager.WakeLock wakeLock = null;
private FlashlightManager mFlashlightController;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.torch_activity);
mTorImageView = (ImageView) findViewById(R.id.imageview_torchlight);
//we must keep the phone's cpu working after openning flash light.
//if don't do this, the phone maybe crash and reboot.
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "RichTorch");
if(null != wakeLock){
wakeLock.acquire();
}

mFlashlightController = new FlashlightManager(this);
mTorImageView.setTag(false);
torchState = false;
}

public void onClick_Flashlight(View view){
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)){
Toast.makeText(this, "Sorry! your phone hasn't flash light!", Toast.LENGTH_LONG).show();
return;
}
mFlashlightController.setFlashlight(!getTorchStatus());
torchImageControl(!getTorchStatus());
setTorchStatus(!getTorchStatus());
}

private void torchImageControl(boolean viewTag){
if(viewTag){
TransitionDrawable openStateDrawable = (TransitionDrawable) mTorImageView.getDrawable();
openStateDrawable.reverseTransition(200);
mTorImageView.setTag(true);
}else{
TransitionDrawable closeStateDrawable = (TransitionDrawable)mTorImageView.getDrawable();
closeStateDrawable.reverseTransition(200);
mTorImageView.setTag(false);
}
}

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

@Override
protected void onResume() {
super.onResume();
if(null == mFlashlightController){
mFlashlightController = new FlashlightManager(this);
}
}

@Override
protected void onDestroy() {
super.onDestroy();
if(mFlashlightController != null){
mFlashlightController.killFlashlight();
mFlashlightController = null;
}
}

private boolean getTorchStatus(){
return torchState;
}

private void setTorchStatus(boolean state){
torchState = state;
}
}

package com.example.richtorch;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.util.Size;
import android.view.Surface;
import android.widget.Toast;

import java.util.ArrayList;

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class FlashlightManager {
private static final String TAG = "FlashlightManager";
private final CameraManager mCameraManager;
private Handler mHandler;
private boolean mFlashlightEnabled;
private String mCameraId;
private CameraDevice mCameraDevice;
private CaptureRequest mFlashlightRequest;
private CaptureRequest.Builder mBuilder;
private CameraCaptureSession mSession;
private SurfaceTexture mSurfaceTexture;
private Surface mSurface;
private Context mContext;

public FlashlightManager(Context context) {
mContext = context;
mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
init();
}

public void init() {
try {
mCameraId = getCameraId();
} catch (Throwable e) {
return;
}

if (mCameraId != null) {
startHandler();
}
}

private void showErrorMsg(){
Toast.makeText(mContext, "don't control camera devices", Toast.LENGTH_LONG).show();
}

public synchronized void setFlashlight(boolean enabled) {
if (mFlashlightEnabled != enabled) {
mFlashlightEnabled = enabled;
postUpdateFlashlight();
}
}

public void killFlashlight() {
boolean enabled;
synchronized (this) {
enabled = mFlashlightEnabled;
}
if (enabled) {
mHandler.post(mKillFlashlightRunnable);
}
}

private synchronized void startHandler() {
if (mHandler == null) {
HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mHandler = new Handler(thread.getLooper());
}
}

private void openCameraDevice() throws CameraAccessException {
mCameraManager.openCamera(getCameraId(), mStateCallback, mHandler);
}

private void openCameraSession() throws CameraAccessException {
mSurfaceTexture = new SurfaceTexture(0, false);
Size size = getSmallestSize(mCameraDevice.getId());
mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
mSurface = new Surface(mSurfaceTexture);
ArrayList<Surface> outputs = new ArrayList<>(1);
outputs.add(mSurface);
mCameraDevice.createCaptureSession(outputs, mSessionStateCallback, mHandler);
}

private Size getSmallestSize(String cameraId) throws CameraAccessException {
Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
.getOutputSizes(SurfaceTexture.class);
if (outputSizes == null || outputSizes.length == 0) {
throw new IllegalStateException(
"doesn't support any outputSize!");
}
Size chosen = outputSizes[0];
for (Size s : outputSizes) {
if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
chosen = s;
}
}
return chosen;
}

private void postUpdateFlashlight() {
startHandler();
mHandler.post(mUpdateFlashlightRunnable);
}

/**
* @author Richard
* @获得有FLAHS功能的Camera ID。
* */
private String getCameraId() throws CameraAccessException {
String[] ids = mCameraManager.getCameraIdList();
for (String id : ids) {
CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
if (flashAvailable != null && flashAvailable
&& lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
return id;
}
}
return null;
}

private void updateFlashlight(boolean status) {
try {
boolean enabled;
synchronized (this) {
enabled = mFlashlightEnabled && !status;
if (enabled) {
if (mCameraDevice == null) {
openCameraDevice();
return;
}
if (mSession == null) {
openCameraSession();
return;
}

if (mFlashlightRequest == null) {
CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW);
builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
builder.addTarget(mSurface);
mFlashlightRequest = builder.build();
mSession.capture(mFlashlightRequest, null, mHandler);
}

} else {
releaseResource();
}
}

} catch (CameraAccessException|IllegalStateException|UnsupportedOperationException e) {
showErrorMsg();
}
}


public void releaseResource() {
if(mBuilder != null){
mBuilder.removeTarget(mSurface);
mBuilder = null;
}

if(mCameraDevice != null){
mCameraDevice.close();
mCameraDevice = null;
}
mCameraDevice = null;
mSession = null;
mFlashlightRequest = null;
if (mSurface != null) {
mSurface.release();
mSurfaceTexture.release();
}
mSurface = null;
mSurfaceTexture = null;
}

private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
postUpdateFlashlight();
}

@Override
public void onDisconnected(CameraDevice camera) {
if (mCameraDevice == camera) {
releaseResource();
}
}

@Override
public void onError(CameraDevice camera, int error) {
if (camera == mCameraDevice || mCameraDevice == null) {
showErrorMsg();
}
}
};

private final CameraCaptureSession.StateCallback mSessionStateCallback =
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
if (session.getDevice() == mCameraDevice) {
mSession = session;
} else {
session.close();
}
postUpdateFlashlight();
}

@Override
public void onConfigureFailed(CameraCaptureSession session) {
if (mSession == null || mSession == session) {
showErrorMsg();
}
}
};

private final Runnable mUpdateFlashlightRunnable = new Runnable() {
@Override
public void run() {
updateFlashlight(false);
}
};

private final Runnable mKillFlashlightRunnable = new Runnable() {
@Override
public void run() {
synchronized (this) {
mFlashlightEnabled = false;
}
//releaseResource();
updateFlashlight(true);
}
};

}



链接: http://pan.baidu.com/s/1iEZuQ 密码: 1jdq (源码是链接下的RichTorch)