《Android原生整合虹软SDK开发uniapp插件》


1、项目背景

应公司要求,需要开发一套类似人脸打卡功能的app,但是因为我们公司没有很强的原生android开发者,所以根据现状选择了第三方跨平台的uniapp,想必目前大多人都了解这个平台了,我也就不多赘述了,直接上uniapp官方网站,它有一个缺点就是很多复杂的功能实现不了,就比如今天我们所要说的基于虹软开放平台的人脸识别功能,那么怎么办呢?当然有办法,使用android原生整合虹软SDK,然后做成插件供uniapp使用,这就是咱们今天的主题。另外具体虹软开放平台是做什么的,大家可以去官方做更深一步的了解,上官方链接:虹软官方,为什么要用虹软,多了不说,我就说一点:免费、免费、免费,这个理由怎么样?!以下是虹软开放平台提供的解决方案:

微信图片_20210803110554.png

温馨提示\color{DarkTurquoise}{温馨提示}

本篇就是针对小白写的,小白不用怕另外也需要一定的android原生基础,入门能看懂代码即可,不需要精通\color{DarkTurquoise}{本篇就是针对小白写的,小白不用怕 另外也需要一定的android原生基础,入门能看懂代码即可,不需要精通}

2、本篇用到的技术栈以及SDK

– 虹软人脸识别SDK v3.0

– android

– vue

– uniapp

3、技术接入部分

1、去虹软控制台(要登录哦)下载人脸识别Demo,传送阵

注意需要新建一个应用,如下图,SDK中包含Demo

在这里插入图片描述

2、 将Demo导入AndroidStudio,下图就是Demo的样子:

注意:AndroidStudio导入的项目路径一定不要有中文\color{DarkTurquoise}{注意:AndroidStudio导入的项目路径一定不要有中文}

*

3、如果不出意外的话,运行项目就会出现如下界面了,至此虹软Demo也就跑起来了

如果出意外了,请查看该文章的可能遇到的错误\color{DarkTurquoise}{可能遇到的错误}章节

4、接下来去跑uniapp的Demo,首先去uniapp官方下载Android平台uni原生插件开发Demo

5、将Demo导入AndroidStudio,下图就是Demo的样子:

注意:AndroidStudio导入的项目路径一定不要有中文\color{DarkTurquoise}{注意:AndroidStudio导入的项目路径一定不要有中文}

在这里插入图片描述

6、跑项目,会出现未配置appkey或配置错误\color{DarkTurquoise}{未配置appkey或配置错误}字样,解决方法请参考:如何申请appkey传送阵\

注意解决这个问题还是稍微比较复杂点的,请认真阅读官方文档,不要怀疑官方文档的正确性\color{DarkTurquoise}{注意解决这个问题还是稍微比较复杂点的,请认真阅读官方文档,不要怀疑官方文档的正确性}


//过程中需要用到的一个生成 sha1 值得命令,在 C:\Program Files\Java\jre1.8.0_291\bin 路径下运行 cmd
keytool.exe -list -v -keystore 【keystore文件的绝对路径】

复制代码

7、拿到 appkey 之后,写入 AndroidManifest.xml 文件中的 meta-data 中,然后将申请 appkey 过程中申请的证书配置到项目中,再次跑项目,如果不出意外的话,运行项目就会出现如下界面了,至此uniapp的Demo也就跑起来了

在这里插入图片描述

8、两个 Demo 都跑起来了,接下来就是整合两个 Demo 了,首先在 uniapp 的 Demo 中右击创建一个Module

在这里插入图片描述

9、选择 Android Library ,在右侧填写如下图几个属性,注意 Package name 尽量与虹软Demo中的一致,因为之后会避免解决一些不必要的错误,下一步

在这里插入图片描述

10、将虹软 Demo 中的如下 文件夹中的所有内容(包括文件夹)复制到刚才创建的 Module 中的同样位置

libs 
java 
jniLibs
res
复制代码

11、将 Module 中的 build.gradle 中的 dependencies 全部删除,加入下面的

compileOnly fileTree(dir: '../app/libs', include: ['uniapp-v8-release.aar'])
implementation 'com.alibaba:fastjson:1.1.46.android'
implementation 'com.squareup.okhttp3:okhttp:4.9.1'
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.6'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
compileOnly "com.android.support:recyclerview-v7:28.0.0"
compileOnly "com.android.support:support-v4:28.0.0"
compileOnly "com.android.support:appcompat-v7:28.0.0"
implementation 'com.android.support.constraint:constraint-layout:2.0.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

复制代码

12、截至目前步骤,我们所有的准备基本已经就绪,接下来我们需要创建一下三个文件

FaceReco_AppProxy.java //用于初始化动态链接库
FaceReco.java //用于激活虹软SDK 
FaceRecoView.java //用于人脸检测视图
复制代码

在这里插入图片描述

13、我们找到 FaceAttrPreviewActivity 文件,将关于人脸识别的核心代码拷贝到 FaceRecoView 文件中,核心代码如下:

 * 初始化引擎
 */
private void initEngine() {
    faceEngine = new FaceEngine();
    afCode = faceEngine.init(getContext(), DetectMode.ASF_DETECT_MODE_VIDEO, ConfigUtil.getFtOrient(getContext()),
            16, 20, FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_AGE | FaceEngine.ASF_FACE3DANGLE | FaceEngine.ASF_GENDER | FaceEngine.ASF_LIVENESS);
    Log.i(TAG, "initEngine:  init: " + afCode);
    if (afCode != ErrorInfo.MOK) {
        System.out.println(R.string.init_failed+":"+afCode);
    }
}

/**
 * 卸载引擎
 */
private void unInitEngine() {
    if (afCode == 0) {
        afCode = faceEngine.unInit();
        Log.i(TAG, "unInitEngine: " + afCode);
    }
}

/**
 * 初始化摄像头
 */
private void initCamera() {
    DisplayMetrics metrics = new DisplayMetrics();
    Activity activity = (Activity)getContext();
    activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
    CameraListener cameraListener = new CameraListener() {

        @Override
        public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
            Log.i(TAG, "onCameraOpened: " + cameraId + "  " + displayOrientation + " " + isMirror);
            previewSize = camera.getParameters().getPreviewSize();
            drawHelper = new DrawHelper(previewSize.width, previewSize.height, previewView.getWidth(), previewView.getHeight(), displayOrientation
                    , cameraId, isMirror, false, false);
        }

        @Override
        public void onPreview(byte[] nv21, Camera camera) {
            if (faceRectView != null) {
                faceRectView.clearFaceInfo();
            }
            List<FaceInfo> faceInfoList = new ArrayList<>();
            long start = System.currentTimeMillis();
            int code = faceEngine.detectFaces(nv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList);
            if (code == ErrorInfo.MOK && faceInfoList.size() > 0) {
                code = faceEngine.process(nv21, previewSize.width, previewSize.height, FaceEngine.CP_PAF_NV21, faceInfoList, processMask);
                if (code != ErrorInfo.MOK) {
                    return;
                }
            } else {
                return;
            }
            List<AgeInfo> ageInfoList = new ArrayList<>();
            List<GenderInfo> genderInfoList = new ArrayList<>();
            List<Face3DAngle> face3DAngleList = new ArrayList<>();
            List<LivenessInfo> faceLivenessInfoList = new ArrayList<>();
            int ageCode = faceEngine.getAge(ageInfoList);
            int genderCode = faceEngine.getGender(genderInfoList);
            int face3DAngleCode = faceEngine.getFace3DAngle(face3DAngleList);
            int livenessCode = faceEngine.getLiveness(faceLivenessInfoList);
            // 有其中一个的错误码不为ErrorInfo.MOK,return
            if ((ageCode | genderCode | face3DAngleCode | livenessCode) != ErrorInfo.MOK) {
                return;
            }
            System.out.println("检测成功");
            if (faceRectView != null && drawHelper != null) {
                List<DrawInfo> drawInfoList = new ArrayList<>();
                for (int i = 0; i < faceInfoList.size(); i++) {
                    drawInfoList.add(new DrawInfo(drawHelper.adjustRect(faceInfoList.get(i).getRect()), genderInfoList.get(i).getGender(), ageInfoList.get(i).getAge(), faceLivenessInfoList.get(i).getLiveness(), RecognizeColor.COLOR_UNKNOWN, null));
                }
                drawHelper.draw(faceRectView, drawInfoList);
            }
        }

        @Override
        public void onCameraClosed() {
            Log.i(TAG, "onCameraClosed: ");
        }

        @Override
        public void onCameraError(Exception e) {
            Log.i(TAG, "onCameraError: " + e.getMessage());
        }

        @Override
        public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
            if (drawHelper != null) {
                drawHelper.setCameraDisplayOrientation(displayOrientation);
            }
            Log.i(TAG, "onCameraConfigurationChanged: " + cameraID + "  " + displayOrientation);
        }
    };
    cameraHelper = new CameraHelper.Builder()
            .previewViewSize(new Point(previewView.getMeasuredWidth(), previewView.getMeasuredHeight()))
            .rotation(activity.getWindowManager().getDefaultDisplay().getRotation())
            .specificCameraId(rgbCameraId != null ? rgbCameraId : Camera.CameraInfo.CAMERA_FACING_FRONT)
            .isMirror(false)
            .previewOn(previewView)
            .cameraListener(cameraListener)
            .build();
      cameraHelper.init();
      cameraHelper.start(); 
 }

复制代码

14、此时人脸检测页面就整合到 uniapp 中了,当然还不可以使用,为什么呢?当然是还有两个文件没做完呢,一个用于激活SDK的,一个用于初始化加载动态链接库文件的,最重要的两步,开搞~\

15、首先将初始化动态链接库文件代码写入 FaceReco_AppProxy 文件中

 * 检查能否找到动态链接库,如果找不到,请修改工程配置
 *
 * @param libraries 需要的动态链接库
 * @return 动态库是否存在
 */
private boolean checkSoFile(String[] libraries,Application application) {
    ApplicationInfo applicationInfo = application.getApplicationInfo();
    File dir = new File(applicationInfo.nativeLibraryDir);
    System.out.println("文件路径:"+dir.getAbsolutePath());
    File[] files = dir.listFiles();
    if (files == null || files.length == 0) {
        return false;
    }
    List<String> libraryNameList = new ArrayList<>();
    for (File file : files) {
        System.out.println("文件名字:"+file.getName());
        libraryNameList.add(file.getName());
    }
    boolean exists = true;
    for (String library : libraries) {
        exists &= libraryNameList.contains(library);
    }
    return exists;
}


复制代码

16、然后激活SDK文件代码写入 FaceReco 文件中

/**
 * 激活设备
 */
private void active(){
    Observable.create(new ObservableOnSubscribe<Integer>() {
        @Override
        public void subscribe(ObservableEmitter<Integer> emitter) {
            RuntimeABI runtimeABI = FaceEngine.getRuntimeABI();
            Log.i(TAG, "subscribe: getRuntimeABI() " + runtimeABI);
            int activeCode = FaceEngine.activeOnline(mUniSDKInstance.getContext(), CommonUtil.getAppId(), CommonUtil.getSdkKey());
            emitter.onNext(activeCode);
        }
    })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Observer<Integer>() {
                @Override
                public void onSubscribe(Disposable d) {

                }
                @Override
                public void onNext(Integer activeCode) {
                    if (activeCode == ErrorInfo.MOK) {
                        showToast(getString(R.string.active_success));
                        mJsCallback.invokeAndKeepAlive("激活成功");
                    } else if (activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED) {
                        showToast(getString(R.string.already_activated));
                        mJsCallback.invokeAndKeepAlive("该设备已激活");
                    } else {
                        showToast(getString(R.string.active_failed)+":"+activeCode);
                        mJsCallback.invokeAndKeepAlive("激活失败,错误码:"+activeCode);
                    }
                    ActiveFileInfo activeFileInfo = new ActiveFileInfo();
                    int res = FaceEngine.getActiveFileInfo(mUniSDKInstance.getContext(), activeFileInfo);
                    if (res == ErrorInfo.MOK) {
                        Log.i(TAG, activeFileInfo.toString());
                    }
                }
                @Override
                public void onError(Throwable e) {
                    showToast(e.getMessage());
                }
                @Override
                public void onComplete() {

                }
            });
}

复制代码

17、将 AndroidManifest.xml 文件替换如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.arcsoft.arcfacedemo">

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

</manifest>

复制代码

18、至此,咱们插件的所有的配置基本完成,接下来删除两个文件夹,为什么要删除这两个文件夹呢,因为这两个文件夹都是安卓原生的 activity 视图,因为目前咱们的视图是 uniapp 来驱动的,所以用不到这些东西了

activity 
fragment
复制代码

19、将 app 项目中引入咱们的插件,在 app 项目中的 build.gradle 中配置

implementation project(':arcfacedemo')
复制代码

20、将项目跑起来,没有任何错误,漂亮,一切皆是那么的完美,如下图,呵呵,没有任何变化,为什么没有变化呢?咱们继续!

在这里插入图片描述

21、刚刚看到的是咱们的 uniapp 主界面,咱们目前只是把插件部分做完了,接下来就是让 uniapp 去调咱们的插件,首先去写一个界面,在这里我就不写界面了,我就直接说怎么调插件了,咦,对了,咱们的插件还没有打包,接下来打包插件

22、在 Android Studio 中选择 Build->Rebuild Project ,就将插件打包好了,如图:

在这里插入图片描述

23、怎么用呢?在这里我提供一下 package.json ,有了这个就不用我多说了吧!

{
    "name": "虹软SDK人脸检测",
    "id": "arc-face",
#### "version": "1.0.0",
    "description": "基于虹软SDK开发的人脸检测插件,插件永久维护,欢迎提需求(qq群:785919513)",
    "_dp_type":"nativeplugin",
    "_dp_nativeplugin":{
        "android": {
            "plugins": [
            	{
					"type": "module",
					"name": "arc-faceReco",
					"class": "com.arcsoft.arcfacedemo.FaceReco"
				},
				{
					"type": "component",
					"name": "arc-faceRecoView",
					"class": "com.arcsoft.arcfacedemo.FaceRecoView"
				}
            ],
            "hooksClass": "com.arcsoft.arcfacedemo.FaceReco_AppProxy",
            "integrateType": "aar",
            "abis": [
                "armeabi-v7a",
                "arm64-v8a"
            ],
            "minSdkVersion":23
        }
    }
}

复制代码

24、至此插件制作的全过程讲解完毕\

25、最后附上源码:源码传送阵

4、可能遇到的错误

这个怎么说呢!一般遇到编译不通过的错误大部分都是环境问题,或者业务问题,这个需要对症下药,博主说一下自己在整合的时候遇到的一些问题吧

1.找不到动态链接库(.so文件)

解决方法:忘记把 .so 文件拷贝过来

2.忘记这个错误了,稍后补上

解决方法:创建 Module 时选择 Android Library ,而不是选择 Phone & Tablet

3.忘记这个错误了,稍后补上

解决方法:项目路径中不要有中文

5、完结

了解更多人脸识别产品相关内容请到虹软视觉开放平台

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享