集成指南

1.SDK目录结构

目录1

2.准备工作

2.1 获取SDK Demo压缩包、License文件、License下载链接

请联系您的Linkface专属商务获取SDK Demo压缩包及License文件;

若需要使用SDK Demo的License动态更新机制,请同时向商务索要License的下载链接;

商务邮箱:market@linkface.cn

3.Demo示例

1.使用Android Studio打开SDK压缩包中的项目,活体检测示例Demo,如下图 打开Demo

2.找到项目中LFLivenessSample下Constants.java文件。Demo中如下 Constants.java文件 需要注释掉“注意”的两行文字,并填入2.1 获取SDK Demo压缩包、License文件、License下载链接获得的License下载链接和License更新提前的天数。 备注:若不使用License的动态更新机制,请参看下面的4部分。

3.然后在LFLivenessSample/build.gradle中applicationId的值修改成License绑定的包名。

4.不使用License动态更新机制的处理:

忽略Constants.java文件中LICENSE_INFO_URL、DAYS_BEFORE_LIC_EXPIRED值的填写,仅需要注释掉”注意“的文字说明。

注释掉MainTestActivity.java中对initSDK()方法中关于动态更新License的调用。代码注释后如下:

    private void initSDK() {
        //初始化Liveness SDK的License路径
        final String licPath = Constants.LICENSE_MIDDLE_PATH + File.separator + Constants.LICENSE_NAME;
        LFLivenessSDK.getInstance(this).initLicPath(licPath, Constants.LICENSE_NAME);
//        try {
//            //获取剩余天数
//            int remainingDays = LFLivenessSDK.getInstance(this).getRemainingDays();
//            //License不在有效期内或到了更新的时间,则启动下载
//            if (!LFLivenessSDK.getInstance(this).checkLicenseValid() || remainingDays < Constants.DAYS_BEFORE_LIC_EXPIRED) {
//                LFLiDownloadManager.getInstance(this).downLoadLic(Constants.LICENSE_INFO_URL, new LFLiDownloadManager.DownloadListener() {
//                    @Override
//                    public void onDownload(boolean isSuccess, String errorMsg) {
//                        //下载完成的回调
//                        LFLog.i(TAG, errorMsg);
//                    }
//                });
//            }
//        } catch (IllegalArgumentException e) {
//            LFLog.e(TAG, e.getMessage());
//            e.printStackTrace();
//        } catch (Exception e) {
//            LFLog.e(TAG, e.getMessage());
//            Toast.makeText(LFMainActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
//            e.printStackTrace();
//        }
    }

完成以上步骤,即可运行体验Demo功能了。

4.将SDK集成到开发环境

使用SDK前,首先需要将其集成到您的开发环境中。

4.1 将SDK集成到开发环境

4.1.1 module集成方式

1.将LFLiveness下的module模块LFLivenessLibrary以module的方式导入工程,通过File->New>importModule->选中LFLiveness文件夹下的LFLivenessLibrary->连续点击Next完成导入。 导入module

最后,点击finish,等待导入module完成。

2.在新建应用的builde.gradle下添加

repositories { flatDir { dirs '../LFLivenessLibrary/libs' } }

在build.gradle中的dependencies{}中添加

compile project(':LFLivenessLibrary')

如下

  repositories {
    flatDir {
        dirs '../LFLivenessLibrary/libs'
    }
  }

  dependencies {
        compile project(':LFLivenessLibrary')
  }

3.将LFLiveness工程中LFLivenessSample的assets目录下的所有文件拷贝到您的工程中的assets目录下。

4.将app/build.gradle中applicationId的值修改成License绑定的包名。

4.1.2 aar集成方式

更灵活的集成方式,获取aar文件内容进行集成

如果您的开发环境是Android Studio,只需要将aar包导入到app/libs中,在app/build.gradle中添加:

repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    compile(name: 'LFLivenessSDK', ext: 'aar')
}

另外,将assets文件夹下的模型文件拷贝到 app/src/main/assets 目录中。

如果您的开发环境是Eclipse,则按照如下步骤进行集成:

1.将LFLivenessLibrary/libs下LFLivenessSDK.aar文件后缀重命名为.zip文件,解压。目录结构如图 目录

2.将解压目录中assets文件夹下的模型文件拷贝到 app/src/main/assets 目录中

3.将解压目录中jni文件夹下的so文件夹拷贝到 app/src/main/jniLibs 目录中

4.将 classes.jar 文件拷贝 app/libs 目录下。在builde.gradle添加依赖。注:可重命名该文件。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

4.2 配置 AndroidManifest.xml 文件

在 AndroidManifest.xml 中增加权限,代码如下:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

权限说明:

名称 用途
CAMERA 调用相机进行扫描或拍照
VIBRATE 调用手机震动器再识别成功时发出震动
WRITE_EXTERNAL_STORAGE 拍摄照片时的临时存储
INTERNET 访问网络权限,用于online版本解密识别结果或License下载时
ACCESS_NETWORK_STATE 获取网络状态

动态申请系统权限。Android6.0之后需要动态申请权限。在相应权限使用之前,请添加相应权限的申请。此处给出开始活体检测时,CAMERA及写权限的判断和动态申请示例。位于MainTestActivity.java中,代码如下:

//常量定义
    private static final int PERMISSION_REQUEST_CAMERA = 0;
    private static final int PERMISSION_REQUEST_SD_WRITE = 1;
    private static final int PERMISSION_REQUEST_WRITE = 3;
        /**
     * 开始检测
     */
    private void onClickStartDetectLiveness() {
        if (isMarshmallow()) {
            if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                // Permission has not been granted and must be
                // requested.
                if (shouldShowRequestPermissionRationale(Manifest.permission.CAMERA)) {
                    // Provide an additional rationale to the user if
                    // the permission was not granted
                    // and the user would benefit from additional
                    // context for the use of the permission.
                }
                // Request the permission. The result will be received
                // in onRequestPermissionResult()
                requestPermissions(
                        new String[]{Manifest.permission.CAMERA},
                        PERMISSION_REQUEST_CAMERA);
            } else {
                // Permission is already available, start camera preview
                requestWriteSdPermission();
            }
        } else {
            requestWriteSdPermission();
        }
    }

        private void requestWriteSdPermission() {
        if (isMarshmallow()) {

            if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                // Permission has not been granted and must be
                // requested.

                if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    // Provide an additional rationale to the user if
                    // the permission was not granted
                    // and the user would benefit from additional
                    // context for the use of the permission.
                }
                // Request the permission. The result will be received
                // in onRequestPermissionResult()
                requestPermissions(
                        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        PERMISSION_REQUEST_SD_WRITE);
            } else {
                // Permission is already available, start camera preview
                startLiveness();
            }
        } else {
            startLiveness();
        }
    }

    public void onRequestPermissionsResult(int requestCode,
                                           String[] permissions, int[] grantResults) {
        if (requestCode == PERMISSION_REQUEST_CAMERA) {
            // Request for camera permission.
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission has been granted. Start camera preview Activity.
                requestWriteSdPermission();
            } else {
                // Permission request was denied.
                Toast.makeText(this, Constants.ERROR_CAMERA_REFUSE,
                        Toast.LENGTH_SHORT).show();
            }
        }
        if (requestCode == PERMISSION_REQUEST_SD_WRITE) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission has been granted. Start camera preview Activity.
                startLiveness();
            } else {
                // Permission request was denied.
                Toast.makeText(this, Constants.ERROR_SD_REFUSE,
                        Toast.LENGTH_SHORT).show();
            }
        }

        if (requestCode == PERMISSION_REQUEST_WRITE) {
            // Request for camera permission.
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission has been granted. Start camera preview Activity.
                initSDK();
            } else {
                // Permission request was denied.
                Toast.makeText(this, Constants.ERROR_STORAGE_REFUSE,
                        Toast.LENGTH_SHORT).show();
            }
        }
    }

详细可参考Demo中MainTestActivity。

如需申请其他权限,原理相同,可自行修改添加。

4.3 添加License

将SDK包中license目录下面的LinkfaceID.lic拷贝到工程的app/src/main/assets目录下面

4.4 解决代码混淆

如果不开启代码混淆则跳过这一步; 由于代码混淆后可能会发生crash,所以建议先在混淆配置中解决代码混淆问题。

具体操作为:在proguard文件中加入下面的代码解决:

-keep class com.linkface.** { *; }

4.5 SDK初始化及使用说明

1.初始化SDK识别模型。在开始活体检测前执行以下代码,完成LFLivenessSDK单例模式的初始化:

旧版本(<版本4.7)

初始化SDK识别模型的代码:

LFLivenessSDK mDetector = new LFLivenessSDK(getActivity());

并处理可能抛出的异常。可用try...catch...语句包裹。

新版本(>=版本4.7)

1.初始化License路径,如在启动的Activity的onCreate方法中调用。Demo中是在MainTestActivity.java,代码如下:

private void initSDK() {
        //初始化Liveness SDK的License路径
        final String licPath = Constants.LICENSE_MIDDLE_PATH + File.separator + Constants.LICENSE_NAME;
        LFLivenessSDK.getInstance(this).initLicPath(licPath, Constants.LICENSE_NAME);
        try {
            //获取剩余天数
            int remainingDays = LFLivenessSDK.getInstance(this).getRemainingDays();
            //License不在有效期内或到了更新的时间,则启动下载
            if (!LFLivenessSDK.getInstance(this).checkLicenseValid() || remainingDays < Constants.DAYS_BEFORE_LIC_EXPIRED) {
                LFLiDownloadManager.getInstance(this).downLoadLic(Constants.LICENSE_INFO_URL, new LFLiDownloadManager.DownloadListener() {
                    @Override
                    public void onDownload(boolean isSuccess, String errorMsg) {
                        //下载完成的回调
                        LFLog.i(TAG, errorMsg);
                    }
                });
            }
        } catch (IllegalArgumentException e) {
            LFLog.e(TAG, e.getMessage());
        } catch (Exception e) {
            LFLog.e(TAG, e.getMessage());
            Toast.makeText(MainTestActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
            e.printStackTrace();
        }
    }

2.在正式使用活体检测前初始化活体检测的SDK,即创建活体检测的Handle。Demo中是在FaceOverlapFragment中的startLivenessIfNeed中实现的。

private void startLivenessIfNeed() {
        if (mDetector == null) {
            try {
                mDetector = LFLivenessSDK.getInstance(getActivity());
                int createResultCode = mDetector.createHandle();
                int errorCode = LivenessActivity.RESULT_INTERNAL_ERROR;
                switch (createResultCode) {
                    case LFLivenessSDK.LF_LIVENESS_INIT_SUCCESS:
                        mIsCreateHandleSuccess = true;
                        break;
                    case LFLivenessSDK.LF_LIVENESS_INIT_FAIL_LICENSE_OUT_OF_DATE://-15
                        mIsCreateHandleSuccess = false;
                        errorCode = LivenessActivity.RESULT_SDK_INIT_FAIL_LICENSE_OUT_OF_DATE;//6
                        break;
                    case LFLivenessSDK.LF_LIVENESS_INIT_FAIL_BIND_APPLICATION_ID: //-14
                        mIsCreateHandleSuccess = false;
                        errorCode = LivenessActivity.RESULT_SDK_INIT_FAIL_APPLICATION_ID_ERROR;
                        break;
                    case LFLivenessSDK.LF_LIVENESS_INIT_FAIL_MODEL_OUT_OF_DATA://-9
                        mIsCreateHandleSuccess = false;
                        errorCode = LivenessActivity.RESULT_SDK_INIT_FAIL_OUT_OF_DATE;
                        break;
                    case LFLivenessSDK.LF_LIVENESS_INIT_FAIL_MODEL_NO_LIC_PATH://-100
                        mIsCreateHandleSuccess = false;
                        errorCode = LivenessActivity.RESULT_FAILED_NO_LIC_PATH;
                        break;
                    default:
                        mIsCreateHandleSuccess = false;
                        errorCode = LivenessActivity.RESULT_INTERNAL_ERROR;
                        break;
                }
                if (mIsCreateHandleSuccess) {
                    mIsDetectorStartSuccess = mDetector.start(getLivenessConfig());
                } else {
                    onErrorHappen(errorCode);
                }
            } catch (Throwable e) {
                onErrorHappen(LivenessActivity.RESULT_INTERNAL_ERROR);
            }
        }
    }

并处理异常。详情可见Demo。

2.您可以设置动作序列,返回单图或多图,设置难易程度,以及是否打开语音提示。SDK会根据您的设置,来进行活体检测。

LFLivenessSample中启动活体检测配置如下:

private void startLiveness() {
        try {
            Bundle bundle = new Bundle();
            /**
             * OUTPUT_TYPE 配置, 传入的outputType类型为singleImg (单图),multiImg (多图),video(低质量视频),fullvideo(高质量视频)
             */
            bundle.putString(LivenessActivity.OUTTYPE, getOutputType());
            /**
             * EXTRA_MOTION_SEQUENCE 动作检测序列配置,支持四种检测动作, BLINK(眨眼), MOUTH(张嘴), NOD(点头), YAW(摇头), 各个动作以空格隔开。 推荐第一个动作为BLINK。
             * 默认配置为"BLINK MOUTH NOD YAW"
             */
            bundle.putString(LivenessActivity.EXTRA_MOTION_SEQUENCE, getActionSequence());
            /**
             * SOUND_NOTICE 配置, 传入的soundNotice为boolean值,true为打开, false为关闭。
             */
            bundle.putBoolean(LivenessActivity.SOUND_NOTICE, true);
            /**
             * COMPLEXITY 配置, 传入的complexity类型为normal,支持四种难度,easy, normal, hard, hell.
             */
            bundle.putString(LivenessActivity.COMPLEXITY, Constants.NORMAL);

            Intent intent = new Intent();
            intent.setClass(this, LivenessActivity.class);
            intent.putExtras(bundle);
            //设置返回图片结果
            intent.putExtra(LivenessActivity.KEY_DETECT_IMAGE_RESULT, true);
            //设置返回video结果,video模式才会返回
            intent.putExtra(LivenessActivity.KEY_DETECT_VIDEO_RESULT, true);
            // 设置protobuf结果
            intent.putExtra(LivenessActivity.KEY_DETECT_PROTO_BUF_RESULT, true);
            startActivityForResult(intent, KEY_TO_DETECT_REQUEST_CODE);
        } catch (Exception e) {
            e.printStackTrace();
}

我们建议将 BLINK 放第一个检测,后面可以随意组合。

推荐的配置为:

动作序列为:"BLINK MOUTH NOD YAW",多图multiImg,难易程度为normal, 语音提示true。

代码详情参见Demo。

5.License集成说明

5.1 License说明

我们的SDK是通过License进行包名绑定授权的。

注意:自版本4.7.0起,License文件名统一更改成LinkfaceID.lic。

5.2 License使用指南

  1. 将Linkface返回的License(.lic结尾)重命名为 LinkfaceID.lic。
  2. 需要将LinkfaceID.lic 文件放在 LFLiveness/LFLivenessSample/src/main/assets目录下。
  3. 我们的License中绑定了App包名有效期等信息。当授权出错时,SDK会返回错误码及Sample显示相应的提示语:
错误码 Sample提示语 描述
ERROR_LICENSE_PACKAGE_NAME_MISMATCH 绑定包名错误 集成SDK的工程包名与license文件绑定的包名不符
ERROR_LICENSE_EXPIRE License过期 license过期
ERROR_LICENSE_FILE_NOT_FOUND License不存在 LinkfaceID.lic的文件名或者路径被修改,导致无法找到License
ERROR_LICENSE_INVALID License不合法 SDK无法解析LinkfaceID.lic的内容

6.License动态更新使用指南

6.1 说明

  • Liveness4.7版本开始支持动态传入License路径、检测license有效期、支持动态更新License;
  • License更新下载地址请联系market@linkface.cn;
  • 新版的License需向Linkface申请。
  • 6.2 使用指南

  • 备注:
    1. License下载后会保存到APP的私有目录下,此处的License路径是License保存的子目录。
    2. 若不使用SDK提供的证书动态更新机制,以下内容可忽略。
    3. 如果需要使用SDK提供的动态更新License机制,则需要打开LFLivenessSample module下面的Constants.java文件修改其中的
      public static final String LICENSE_INFO_URL = "https://cloud-license.linkface.cn/json/xxx.json";
      public static final int DAYS_BEFORE_LIC_EXPIRED = 5;
    

    将LICENSE_INFO_URL的值修改为在我公司申请的License动态更新的链接;

    将DAYS_BEFORE_LIC_EXPIRED的值修改为License需要动态更新下载提前的天数;

    6.2.1 传入License路径

    在APP的默认启动Activity中,调用SDK方法传入License路径。如在APP的MainTestActivity的onCreate中调用。

    String licPath = "/liveness_license/LinkfaceID.lic";
    String licName = "LinkfaceID.lic";
    //集成license动态更新机制,需传递licPath
    LFLivenessSDK.getInstance(MainTestActivity.this).initLicPath(licPath,licName);
    

    备注:

    licPath的值需要与动态更新下载License的存储路径保持一致,此路径是APP私有目录下的相对路径。 即,若APP私有目录为/data/user/0/<包名>/files,则License实际的存储路径为/data/user/0/<包名>/files/liveness_license/LinkfaceID.lic。

    6.2.2 获取License剩余有效天数

    返回license剩余有效天数

    int remainingDays = LFLivenessSDK.getInstance(MainTestActivity.this).getRemainingDays();
    

    6.2.3 根据定制的更新License链接下载新License

    下载新License到指定路径

    if (!LFLivenessSDK.getInstance().checkLicenseValid(this) || remainingDays < LFConstants.DAYS_BEFORE_LIC_EXPIRED) {
    LFLicDownloadManager.getInstance().downLoadLic(LFConstants.LICENSE_INFO_URL, downloadListener);
    }
    

    6.3 License下载说明

    6.3.1 获取下载License信息的链接

    该链接为客户定制链接,请联系market@linkface.cn。

    6.3.2 License下载时序图

    时序图

    6.3.3 License信息格式

    License信息格式为Json。

    各字段及其说明

    字段名 说明
    md5 由lic文件得到的MD5,32位小写
    start_time License有效期起始时间,Unix时间戳格式
    expired_time License有效期结束时间,Unix时间戳格式
    lic_url License下载地址

    json示例:

    { "md5": "qwertyhgfr567u8i987yt65rfghjkiuy", "start_time": 1517500800, "expired_time": 1546185600, "lic_url": "http://www.linkface.com/license_url" }

    6.3.4 License下载原理

    1. 根据定制链接请求License信息的Json数据;
    2. 根据Json数据中的lic_url下载License;
    3. 对该License做MD5得到32位小写字符串与License信息的json中md5字段的值进行比对,验证License;
    4. 将License放入初始化设置的路径中,替换原有License;
    5. 使用SDK提供的功能。

    6.4 同一应用同时集成Linkface的多个SDK的说明

    6.4.1 多个SDK使用同一License

    一般,同一应用的License是唯一的,所以集成不同SDK时只需要使用和管理一份License即可。 例如,先后集成了活体SDK和OCR SDK,集成活体SDK时,License路径为"/linkface/LinkfaceI.lic",该路径可以自定义。

    6.4.1.1 初始化时,使用同一License。

    活体SDK代码:

    String licPath = "/linkface/LinkfaceID.lic";
    String licName = "LinkfaceID.lic";
    //集成license动态更新机制,需传递licPath
    LFLivenessSDK.getInstance(MainTestActivity.this).initLicPath(licPath,licName);
    

    OCR SDK代码:

    String licPath = "/linkface/LinkfaceID.lic";
    LFBankCardScan.getInstance().initLicensePath(LFMainActivity.this, licPath, CardRecognizer.LICENSE_NAME);
    
    6.4.1.2 动态更新下载时,仅需要使用活体SDK或OCR SDK的一个SDK中判断License有效期并使用LFLicDownloadManager下载。

    详见6.2.3 根据定制的更新License链接下载新License

    6.4.2 多个SDK使用不同License

    6.4.2.1 初始化SDK

    需要将不同SDK初始化时的License路径设置为不同即可。如 活体SDK代码:

    String licPath = "/liveness/LinkfaceID.lic";
    String licName = "LinkfaceID.lic";
    //集成license动态更新机制,需传递licPath
    LFLivenessSDK.getInstance(MainTestActivity.this).initLicPath(licPath,licName);
    

    OCR SDK代码:

    String licPath = "/ocr/LinkfaceID.lic";
    LFBankCardScan.getInstance().initLicensePath(LFMainActivity.this, licPath, CardRecognizer.LICENSE_NAME);
    
    6.4.2.2 License动态更新

    因为License不同,所以需要分别动态更新License。详见5.2.3 根据定制的更新License链接下载新License

    results matching ""

      No results matching ""