实现过程:

  1. 创建一个窗口用来显示照相预览
  2. 判断预览中是否有人脸出现
  3. 探测到人脸时抓取当前图像
  4. 压缩当前图像提高上传速度
  5. 将压缩后图像调用链接post上传
  6. 解析返回结果

首先,自定义一个FaceDialogFragment,如下:

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.DialogFragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

public class FaceDialogFragment extends DialogFragment {

    private ComponentActivity mActivity;

    private SurfaceView surfaceView;
    private TextView errorMsg;
    private TextView again;
    private TextView prompt_msg;
    private Camera mCamera;
    private SurfaceHolder mHolder;
    private String TAG = "TAG";
    private Handler handler = new Handler(){

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(msg.what == 1){
                errorMsg.setText("是否重新验证");
                prompt_msg.setText("验证失败");
                prompt_msg.setTextColor(getResources().getColor(R.color.red));
                again.setVisibility(View.VISIBLE);
            }else if(msg.what == 2){
                Toast.makeText(mActivity,"识别成功",Toast.LENGTH_SHORT).show();
            }
        }
    };


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mActivity = (ComponentActivity) getActivity();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(DialogFragment.STYLE_NO_TITLE, R.style.FragmentDialog);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.facedetch, container, false);
        surfaceView = v.findViewById(R.id.face_view);
        errorMsg = v.findViewById(R.id.error_msg);
        again = v.findViewById(R.id.again);
        prompt_msg = v.findViewById(R.id.prompt_msg);
        TextView cancel = v.findViewById(R.id.cancel);
        openSurfaceView();
        again.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                errorMsg.setText("");
                prompt_msg.setText("正在验证..");
                prompt_msg.setTextColor(getResources().getColor(R.color.black));
                surfaceView.setVisibility(View.VISIBLE);
                mCamera.startPreview();
                mCamera.startFaceDetection();
                again.setVisibility(View.GONE);
            }
        });
        cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
        return v;
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    private void openSurfaceView() {
        mHolder = surfaceView.getHolder();
        mHolder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                //判断是否有相机
                if (mCamera == null) {
                    //前置相机
                    int CammeraIndexf = FindFrontCamera();
                    //后置相机
                    int CammeraIndexb = FindBackCamera();
                    if(CammeraIndexf!=-1){
                        mCamera = Camera.open(CammeraIndexf);
                    }else{
                        if(CammeraIndexb!=-1){
                            mCamera = Camera.open(CammeraIndexb);
                        }else{
                            Toast.makeText(mActivity,"不支持",Toast.LENGTH_SHORT).show();
                            return;
                        }
                    }

                    try {
                        // 设置脸部检测监听
                        mCamera.setFaceDetectionListener(new FaceDetectorListener());
                        //设置相机预览
                        mCamera.setPreviewDisplay(holder);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                if (mHolder.getSurface() == null) {
                    // preview surface does not exist
                    Log.e(TAG, "mHolder.getSurface() == null");
                    return;
                }

                try {
                    mCamera.stopPreview();

                } catch (Exception e) {
                    // ignore: tried to stop a non-existent preview
                    Log.e(TAG, "Error stopping camera preview: " + e.getMessage());
                }

                try {
                    mCamera.setPreviewDisplay(mHolder);
                    //获取当前预览框的宽高
                    int measuredWidth = surfaceView.getMeasuredWidth();
                    int measuredHeight = surfaceView.getMeasuredHeight();
                    //设置相机预览图宽高
                    setCameraParms(mCamera, measuredWidth, measuredHeight);
                    mCamera.startPreview();
                    //开启脸部探测
                    startFaceDetection(); // re-start face detection feature

                } catch (Exception e) {
                    // ignore: tried to stop a non-existent preview
                    Log.d(TAG, "Error starting camera preview: " + e.getMessage());
                }
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
                holder = null;
            }
        });
    }

    private void setCameraParms(Camera camera, int width, int height) {
        // 获取摄像头支持的pictureSize列表
        Camera.Parameters parameters = camera.getParameters();
        parameters.setJpegQuality(100);
        List<Camera.Size> pictureSizeList = parameters.getSupportedPreviewSizes();
        for (int i=0;i<pictureSizeList.size();i++){
            int pwidth = pictureSizeList.get(i).width;
            int pheight = pictureSizeList.get(i).height;
            //获取720宽度的预览图像
            if (pwidth==720){
                if (pheight == 720){
                    surfaceView.setLayoutParams(new LinearLayout.LayoutParams(width,width));
                }else{
                    surfaceView.setLayoutParams(new LinearLayout.LayoutParams(width,width*3/2));
                }
                parameters.setPreviewSize(pwidth,pheight);
                break;
            }
        }
        // 对焦
        if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
            // 连续对焦
            parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
        }

        //旋转90度,获取正向预览
        camera.setDisplayOrientation(90);
        camera.cancelAutoFocus();
        camera.setParameters(parameters);

    }

    /**
     * 启动脸部检测,如果getMaxNumDetectedFaces()!=0说明不支持脸部检测
     */
    public void startFaceDetection() {
        // Try starting Face Detection
        Camera.Parameters params = mCamera.getParameters();
        // start face detection only *after* preview has started
        if (params.getMaxNumDetectedFaces() > 0) {
            // mCamera supports face detection, so can start it:
            mCamera.startFaceDetection();
        } else {
            Log.e("tag", "【FaceDetectorActivity】类的方法:【startFaceDetection】: " + "不支持");
        }
    }

    private class FaceDetectorListener implements Camera.FaceDetectionListener {
        @Override
        public void onFaceDetection(Camera.Face[] faces, Camera camera) {
            if (faces.length > 0) {
                //探测到人脸后利用回调获取当前图像
                camera.takePicture(null,null,fjpgCallback);

                Camera.Face face = faces[0];
                Rect rect = face.rect;
                Log.d("FaceDetection", "可信度:" + face.score + "face detected: " + faces.length +
                        " Face 1 Location X: " + rect.centerX() +
                        "Y: " + rect.centerY() + "   " + rect.left + " " + rect.top + " " + rect.right + " " + rect.bottom);
                Log.e("tag", "【FaceDetectorListener】类的方法:【onFaceDetection】: "+face.score +"="+faces.length );

                mCamera.stopFaceDetection();

            } else {
                Log.e(TAG, "【FaceDetectorListener】类的方法:【onFaceDetection】: " + "没有脸部");
            }
        }
    }

    private Camera.PictureCallback fjpgCallback = new Camera.PictureCallback()
    {
        @SuppressLint("SimpleDateFormat")
        public void onPictureTaken( byte[] data , Camera camera )
        {
            Bitmap bitmap = rotateMyBitmap(BitmapFactory.decodeByteArray( data , 0 , data.length ));

            //获得压缩后的图片
            Bitmap bitmap2 = compressImage(transImage(bitmap,480,720,100));
            //将Bitamap转换为Base64格式
            final String base64data = ImageUtils.bitmapToBase64(bitmap2);

            new Thread(new Runnable() {
                @Override
                public void run() {
                    String backJson = HttpAssist.sendFace("调用地址URL","调用此 API 的 API Key",
                            "调用此 API 的 API Key","第一张图片的 URL",base64data);
                    //此处没有进行结果解析,只不过将返回的json字符串进行了切割,获取了两张照片的相似度数值
                    if (backJson.indexOf("confidence")!=-1){
                        String confidence = backJson.substring(
                                backJson.indexOf("confidence\":")+12,
                                backJson.indexOf("confidence\":")+19).trim();
                        if (confidence.indexOf(",")!=-1){
                            confidence = confidence.substring(0,confidence.indexOf(",")).trim();
                        }
                        double confidence1 = Double.parseDouble(confidence);
                        //当相似度大于80的时候 认定为同一人
                        if(confidence1>80){
                           
                            handler.sendEmptyMessage(2);
                            //调用activity中的自定义方法,在识别成功后进行页面跳转
                            mActivity.onAuthenticated();
                            dismiss();
                        }else{
                            handler.sendEmptyMessage(1);
                        }
                    }else{
                        handler.sendEmptyMessage(1);
                    }
                }
            }).start();

    };

    //缩放图片
    public Bitmap transImage(Bitmap bitmap, int width, int height, int quality) {

            int bitmapWidth = bitmap.getWidth();
            int bitmapHeight = bitmap.getHeight();
            // 缩放图片的尺寸
            float scaleWidth = (float) width / bitmapWidth;
            float scaleHeight = (float) height / bitmapHeight;
            Matrix matrix = new Matrix();
            matrix.postScale(scaleWidth, scaleHeight);
            // 产生缩放后的Bitmap对象
            Bitmap resizeBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmapWidth, bitmapHeight, matrix, false);

            if (!bitmap.isRecycled()) {
                bitmap.recycle();//记得释放资源,否则会内存溢出
            }
            return resizeBitmap;

    }

    //压缩图片
    private Bitmap compressImage(Bitmap image) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
        int options = 100;
        while ( baos.toByteArray().length / 1024>100) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
            baos.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
            options -= 10;//每次都减少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
        return bitmap;
    }

    //获取前置摄像头
    private int FindFrontCamera(){
        int cameraCount = 0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras(); // get cameras number

        for ( int camIdx = 0; camIdx < cameraCount;camIdx++ ) {
            Camera.getCameraInfo( camIdx, cameraInfo ); // get camerainfo
            if ( cameraInfo.facing ==Camera.CameraInfo.CAMERA_FACING_FRONT ) {
                // 代表摄像头的方位,目前有定义值两个分别为CAMERA_FACING_FRONT前置和CAMERA_FACING_BACK后置
                return camIdx;
            }
        }
        return -1;
    }

    //获取后置摄像头
    private int FindBackCamera(){
        int cameraCount = 0;
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        cameraCount = Camera.getNumberOfCameras(); // get cameras number

        for ( int camIdx = 0; camIdx < cameraCount;camIdx++ ) {
            Camera.getCameraInfo( camIdx, cameraInfo ); // get camerainfo
            if ( cameraInfo.facing ==Camera.CameraInfo.CAMERA_FACING_BACK ) {
                // 代表摄像头的方位,目前有定义值两个分别为CAMERA_FACING_FRONT前置和CAMERA_FACING_BACK后置
                return camIdx;
            }
        }
        return -1;
    }

    //选择获取的图片保持为正向
    public Bitmap rotateMyBitmap(Bitmap bmp){
        
        Matrix matrix = new Matrix();
        matrix.postRotate(-90);

        Bitmap bitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);

        Bitmap nbmp2 = Bitmap.createBitmap(bmp, 0,0, bmp.getWidth(),  bmp.getHeight(), matrix, true);

        return nbmp2;

    }
}

图片转换为Base64格式调用代码如下:

import android.graphics.Bitmap;

import android.util.Base64;

import java.io.ByteArrayOutputStream;
import java.io.IOException;


/**
 * 图片工具类
 * */
public class ImageUtils {


	public static String bitmapToBase64(Bitmap bitmap) {

		String result = null;
		ByteArrayOutputStream baos = null;
		try {
			if (bitmap != null) {
				baos = new ByteArrayOutputStream();
				bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

				baos.flush();
				baos.close();

				byte[] bitmapBytes = baos.toByteArray();
				result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (baos != null) {
					baos.flush();
					baos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result;
	}
	
}

图片上传代码如下:

import android.util.Log;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * Http post上传
 * */
public class HttpAssist {

	public static String sendFace(String url,String param1,String param2,String param3,String param4){
		String d1 = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
		OkHttpClient okHttpClient = new OkHttpClient();
		FormBody formBody = new FormBody
				.Builder()
				.add("api_key",param1)
				.add("api_secret",param2)
				.add("image_url1",param3)
				.add("image_base64_2",param4)
				.build();
		Request request = new Request.Builder()
				.post(formBody)
				.url(url)
				.build();
		try {
			Response response = okHttpClient.newCall(request).execute();
			String result = response.body().string();
			Log.e("tagRes",result);
			String d2 = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
			double d = Double.valueOf(d2) - Double.valueOf(d1);
			Log.e("tag2t",d + "");
			return result;
		} catch (IOException e) {
			Log.e("tage","出错了。。。"+e.getMessage().toString()+"==="+e.getCause());
			e.printStackTrace();
		}
		return "-1";
	}
}

FaceDialogFragment的布局代码facedetch.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/finger_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:visibility="visible">
        <SurfaceView
            android:id="@+id/face_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            />

        <TextView
            android:id="@+id/prompt_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:text="正在验证.."
            android:textColor="#000"
            android:textSize="16sp"
            />

        <TextView
            android:id="@+id/error_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="5dp"
            android:maxLines="1"
            android:textSize="12sp"
            android:textColor="#f45"
            />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/password_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:visibility="gone">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="20dp"
            android:text="请输入密码"
            android:textColor="#000"
            android:textSize="16sp"
            />

        <EditText
            android:id="@+id/pwd_edit"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="16sp"
            android:password="true"
            android:paddingTop="12dp"/>

        <TextView
            android:id="@+id/password_error_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="5dp"
            android:maxLines="1"
            android:textSize="12sp"
            android:textColor="#f45"
            />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="0.5dp"
        android:layout_marginTop="10dp"
        android:background="#ccc"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <TextView
            android:id="@+id/cancel"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:gravity="center"
            android:text="取消"
            android:textColor="#5d7883"
            android:textSize="16sp" />
        <TextView
            android:id="@+id/again"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:gravity="center"
            android:text="重新验证"
            android:textColor="#5d7883"
            android:textSize="16sp"
            android:visibility="gone"
            />

        <TextView
            android:id="@+id/password"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="1"
            android:gravity="center"
            android:text="密码验证"
            android:textColor="#5d7883"
            android:textSize="16sp"
            android:visibility="gone"
            />
    </LinearLayout>
</LinearLayout>

最后在Activity中调用如下方法即可使用该功能:

private void showFaceDialog(String meip) {
        FaceDialogFragment fragment = new FaceDialogFragment();
        fragment.show(getFragmentManager(), "face");
}