zoukankan      html  css  js  c++  java
  • 《Android原生整合虹软SDK开发uniapp插件》


    1、项目背景

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

    微信图片_20210803110554.png

    温馨提示

    本篇就是针对小白写的,小白不用怕

    另外也需要一定的android原生基础,入门能看懂代码即可,不需要精通

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

    - 虹软人脸识别SDK v3.0

    - android

    - vue

    - uniapp

    3、技术接入部分

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

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

    在这里插入图片描述

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

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

    *

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

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

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

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

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

    在这里插入图片描述

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

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

    
    //过程中需要用到的一个生成 sha1 值得命令,在 C:Program FilesJavajre1.8.0_291in 路径下运行 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、完结

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

  • 相关阅读:
    http的8种请求方式
    死锁
    进程与线程
    vuex
    路由懒加载
    SPA单页面富应用
    组件中的data为什么必须是函数
    v-for中key的作用
    关于排序的常识总结
    关于树的常见操作-C++面试
  • 原文地址:https://www.cnblogs.com/ccLqqy/p/15102597.html
Copyright © 2011-2022 走看看