Android设备端开发指南
LinkFace SDK封装了设备与云的通讯协议,实现了人员信息增删查,信息增删查和检测/识别事件上云等功能。SDK支持Android Studio开发环境。
SDK集成
依赖Linkkit SDK的通道能力,在集成本SDK之前,请务必完成Linkkit SDK的集成。
依赖引入
// 1. 在根目录下的build.gradle中添加Aliyun Maven仓库的引用
allprojects {
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/repositories/releases"
}
maven {
url "http://maven.aliyun.com/nexus/content/repositories/snapshot"
}
}
}
// 2. App build.gradle中添加依赖
implementation('com.aliyun.iotx:linkface:1.0.0')
// 3. App build.gradle中约束ABI,只使用armeabi-v7a
android {
defaultConfig {
externalNativeBuild {
ndk {
abiFilters "armeabi-v7a"
}
}
}
}
注意:如果使用SNAPSHOT包,因Android Studio默认不会主动对SNAPSHOT包拉取更新,因而会导致SNAPSHOT包更新时本地与远程不一致,请在App的build.gradle文件中添加以下配置,以便Android Studio禁用本地cache:configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds'}
混淆配置
# keep and don't warn LinkFace
-dontwarn com.aliyun.iotx.linkvisual.linkface.**
-keep class com.aliyun.iotx.linkvisual.linkface.** { *; }
权限说明
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET" />
最佳对接流程
1. 初始化
请在Linkkit SDK初始化完成后对LinkFace进行初始化,设备证书(ProductKey、DeviceName、DeviceSecret)必须传入。
@Override
public void onInitDone(Object data) {
Log.d(TAG, "onInitDone() called with: data = [" + JSON.toJSONString(data) + "]");
linkkitConnectState = ConnectState.CONNECTED;
getServiceList();
setServiceHandler();
//初始化LinkFace SDK
LinkFaceConfig linkFaceConfig = LinkFaceConfig.newBuilder()
.productKey(productKey)
.deviceName(deviceName)
.deviceSecret(deviceName)
.log_level(0)
.build();
int ret = LinkFace.getInstance().init(linkFaceConfig, MainActivity.this);
appendLog("LinkFace SDK 初始化完成: " + ret);
}
2. 监听Linkkit通道消息
LinkFace SDK由Linkkit通道的下行消息来驱动,需要在Linkkit的onNotify回调中将下行topic派遣到LinkFace SDK进行处理。
private IConnectNotifyListener connectNotifyListener = new IConnectNotifyListener() {
@Override
public void onNotify(String connectId, String topic, AMessage aMessage) {
Log.d(TAG, "onNotify() called with: connectId = [" + connectId + "], topic = [" + topic + "], aMessage = ["
+ new String((byte[]) aMessage.data) + "]");
/**
* 添加LinkFace SDK对LinkKit异步消息的监听。
*/
LinkFace.getInstance().notifyTopicReceived(connectId, topic, aMessage);
}
@Override
public boolean shouldHandle(String connectId, String topic) {
return true;
}
@Override
public void onConnectStateChange(String connectId, ConnectState connectState) {
appendLog("Linkkit连接状态变更为"+ connectState);
linkkitConnectState = connectState;
}
};
3. 创建数据库
建议分别建立人员信息表和底库表两个表,两表之间通过人员信息ID进行外键关联,表结构信息如下:
人员信息表 user_info
列名 | 类型 | 长度 | 是否为NULL | 含义 |
id | integer | 64 | 否 | 表的主键,递增 |
person_id | vchar | 64 | 否 | 人员信息的唯一ID,实际可能为身份证号,学号,工号之类的。 |
name | vchar | 128 | 可以 | 人员的姓名或别名,识别到后,可能需要在面板机上展示识别结果。 |
group_ids | vchar | 1024 | 否 | 人员所在的分组信息,建议实际存储为JSON Array格式以便于检索,比如 ["group_1", "group_2", "group_3"]。 |
group_id_count | integer | 64 | 否 | 用来标示@group_ids人员分组的个数。 |
timestamp_ms | integer | 64 | 否 | 此条人员信息更新的时间,不能由端侧自主生成,必须使用SDK传递过来的时间。 |
extra_info | vchar | 1024 | 可以 | 附加信息,格式为JSON String,建议长度为1K,可能不存在。 |
底库图片信息表 image_info
列名 | 类型 | 长度 | 是否为NULL | 含义 |
id | integer | 64 | 否 | 表的主键,递增 |
person_id | vchar | 64 | 否 | 人员信息的唯一ID,实际可能为身份证号,学号,工号之类的。 |
faceimg_md5 | vchar | 64 | 否 | 图片的md5值,必须使用SDK传递过来的值。 |
faceimg_size | integer | 32 | 否 | 图片的大小 |
timestamp_ms | integer | 64 | 否 | 此条底库图片更新的时间,不能由端侧自主生成,必须使用SDK传递过来的时间。 |
4. 处理回调接口
4.1 对接底库管理相关接口
4.1.1 批量添加底库
/**
* 云端下发底库同步请求时,sdk先缓存图片,再调用该接口将图片批量入库。
*
* @param imageInfos 注册图片信息列表
* @param num 图片信息列表的数量
* @return 每张图片入库结果. 参考:{@link com.aliyun.iotx.linkvisual.linkface.bean.LinkFaceErrorCode}
* <p>
* 注意:<br>
* 1. 该接口为同步调用,接口不宜耗时过久<br>
* 2. 该接口不会在多线程中并发调用<br>
* 3. 每张图片的添加结果,都要按序放入返回数组中<br>
* 4. 错误码请严格按照 {@link com.aliyun.iotx.linkvisual.linkface.bean.LinkFaceErrorCode} 中的定义<br>
*/
int[] onAddImageInfo(LinkFaceImageInfo[] imageInfos, int num);
把图片添加到算法特征库并执行SQL语句如下:
INSERT INTO image_info (person_id, faceimg_md5, faceimg_size, timestamp_ms)
VALUES ("aaa", "asdfasdfasf", 12, 12356337889);
4.1.2 删除底库
/**
* 云端需要删除某个底库时,sdk会调用该接口将信息逐个传递给厂商,厂商需要将该数据从数据库中删除
* @param imageInfo 带删除图片信息
* @ret 如果删除成功,则返回0,否则返回其他值. 参考:{@link com.aliyun.iotx.linkvisual.linkface.bean.LinkFaceErrorCode}
*
* 注意:<br>
* 1. 只有数据真实存在时(personId/faceImageMd5),才返回成功。<br>
* 2. personId 不存在返回-1。<br>
* 3. personId 存在而 faceImageMd5 不存在,返回-2。<br>
*/
int onDeleteImageInfo(LinkFaceImageInfo imageInfo);
把图片特征从算法库中删除并执行删除@image_info表的一行记录,SQL语句如下:
DELETE FROM image_info WHERE person_id = "aaa";
4.1.3 查询底库数据
/**
* 基于人员id,查询底库信息
*
* @param personId 人员ID
* @return 图片信息
*/
LinkFaceImageInfo onQueryImageInfo(String personId);
基于person_id查询image_info的一行记录,SQL语句如下:
SELECT * from image_info WHERE person_id = "aaa";
4.1.4 查询底库总数
/**
* 查询端侧底库总数
*
* @return 底库总数
*/
int onQueryImageCount();
SQL语句如下:
SELECT count(*) from image_info;
4.2 对接人员信息管理相关接口
4.2.1 添加人员信息
/**
* 添加人员信息
* @param userInfo 待添加的人员信息.
* @return 如果人员信息添加成功,则返回0,否则返回其他值. 参考:{@link com.aliyun.iotx.linkvisual.linkface.bean.LinkFaceErrorCode}
*/
int onAddUserInfo(LinkFaceUserInfo userInfo);
对应SQL:
INSERT INTO user_info (person_id, name, group_ids, group_id_count, timestamp_ms, extra_info)
VALUES ("aaa", "阿里巴巴", "[\"aaa\", \"bbb\"]", 2, 12356337889, "");
4.2.2 删除人员信息
/**
* 删除用户信息
* @param userInfo 待删除的人员信息
* @return 如果删除人员信息成功,则返回0,否则返回其他值. 参考:{@link com.aliyun.iotx.linkvisual.linkface.bean.LinkFaceErrorCode}
*/
int onDeleteUserInfo(LinkFaceUserInfo userInfo);
对应SQL:
DELETE FROM user_info WHERE person_id = "aaa";
4.2.3 查询人员信息
/**
* 基于人员id,查询人员信息
*
* @param personId 人员ID
* @return 人员信息
*/
LinkFaceUserInfo onQueryUserInfo(String personId);
对应SQL:
SELECT * from image_info WHERE person_id = "aaa";
4.3 对接其他接口
4.3.1 删除用户组下的所有数据
/**
* 清除用户分组下的所有用户信息和相关数据
* @param groupId 用户组ID
* @return 如果删除用户组成功,则返回0,否则返回具体错误码. 参考:{@link com.aliyun.iotx.linkvisual.linkface.bean.LinkFaceErrorCode}
*/
int onClearUserGroup(String groupId);
4.3.2 清除所有数据
/**
* 将算法特征库的内容完全清空并删除数据库记录
* @return 如果清空成功,则返回0,否则返回具体错误码. 参考:{@link com.aliyun.iotx.linkvisual.linkface.bean.LinkFaceErrorCode}
*/
int onClearAll();
对应SQL:
DELETE FROM user_info;
DELETE FROM image_info;
4.3.2 对接Linkkit通道状态感知接口
LinkFace内部的运行需要参考Linkkit通道的连接状态,需实现onLinkKitCheckStateNormal回调方法。
@Override
public int onLinkKitCheckStateNormal() {
appendLog("查询连接状态" + linkkitConnectState);
return linkkitConnectState == ConnectState.CONNECTED ? 1 : 0;
}
5 对接检测接口
无论比对是否成功,均调用SDK
getInstance
().uploadMatchResult(linkFaceMatchResult)接口,差异在于比对失败的case里,person id可以填空。
/**
* 当门禁机检测到后,无论是否能正确识别,均需要调用本接口将识别结果上报到云端。
*
* @param linkFaceMatchResult 识别结果,如果识别成功,需要包含已识别的人的基本信息。
* @return 成功返回0,失败返回-1
*
* 注意:
* 1. 该接口为同步接口,请勿并发调用。
* 2. 若接口返回失败,建议最多重试3次。
*/
public int uploadMatchResult(LinkFaceMatchResult linkFaceMatchResult);
6. 销毁
LinkFace应在长生命周期组件中运行,确认不使用时,请销毁.。
@Override
protected void onDestroy() {
super.onDestroy();
faceDBHelper.close();
LinkKit.getInstance().unRegisterOnPushListener(connectNotifyListener);
// 销毁LinkFace
LinkFace.getInstance().destroy();
}
包内容介绍
├── README.pdf // 本文档
├── api-doc // java-doc api文档
└── sample // 示例工程源码
FAQ
1. 人员信息表和底库表是否需要做关联删除?
不需要,删除人员信息和删除底图都是原子操作;假如需要同时删除人员信息及其底图,SDK会分别调用两次接口来完成,设备实现层无需做关联删除操作。
2. 运行时报liblinkface.so not found错误。
如果使用了SNAPSHOT包,遇到这种情况请Rebuild一下工程。
3.与LinkFace Linux 版本SDK有什么区别?
Android版本基于Linux C版本进行封装和移植,暂不支持设备数据校验码查询。