Appearance
Android SDK 接入指南
1. 导入前提
SDK 仓库:Android Release Note
系统要求
- Android 6.0及以上系统,API Level >= 23
- armeabi-v7a or arm64-v8a
- Android Studio 3.6.3及以上版本
- Android Build Tools Ver. > 23
2. 导入SDK
2.1. 申请你的 AppKey 及 AppSecret
先注册登录 ARWorld 创作者平台,在【我的应用】模块【创建应用】。
目前仅开放移动端 SDK,如有接入 SIM、Nreal、EZXR AR Glasses 的需求请联系易现工作人员或产品kelexin@ezxr.com
关键填写项目:
适配设备,指应用适配何种设备,我们会生成对应平台的授权信息;
存储方式,指应用的内容存储在何处,我们在平台提供存储服务,你也可使用阿里云的 OSS 服务,填写 阿里云 OSS 的相关配置即可。
完成应用创建后,再进入应用详情页,填写你要接入的 SDK 版本。
2.2. SDK 库导入方式
易现 AR-World SDK Android 版现在暂时只提供 离线aar 方式接入。请通过 Gitlab 获取离线的SDK aar 包。
将 insightar_world_unity.aar 文件复制到工程 app 模块的 libs 目录下,打开 app/build.gradle,在 dependencies 中添加以下引用:
java
repositories {
...
flatDir {
dirs 'libs'
}
}
dependencies {
//...
//use insight aar
implementation (name:'insightar_world_unity',ext:'aar')
//...
}
如果你使用的是 Android Studio 3.0 以下版本,请将 gradle中implementation 关键字替换为compile
3. 配置SDK
3.1. 权限配置
在 AndroidManifest.xml 添加以下 permission 及 feature
xml
<uses-feature android:name="android.hardware.camera" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--存储拍照或视频等媒体文件使用-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--录屏功能使用-->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--通过网络以及GPS获取设备当前位置信息,定位需求使用-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
当然在Android 6.0以上系统需要代码动态开启相应 Dangerous 权限
由于 SDK 内部的 min-sdk-version 是 23。如果外部的 min-sdk-version 小于 23,会带来构建问题,需要外部 APP 在 AndroidManifest.xml 中配置:
问题描述:
Manifest merger failed : uses-sdk:minSdkVersion 17 cannot be smaller than version 21 declared in library [:arinsight] /Users/zfxu/work/androidstudio_workspace/InsightSDK/arinsight/build/intermediates/manifests/full/debug/AndroidManifest.xml as the library might be using APIs not available in 17
xml
<uses-sdk tools:overrideLibrary="com.netease.insightar"></uses-sdk>
3.2. 混淆配置
在 app 目录 proguard-rules.pro 中添加以下防混淆代码:
java
-keep class com.netease.insightar.** { *; }
-dontwarn com.netease.insightar.**
3.3. AndroidManifest 配置
3.3.1. 配置 theme
由于洞见 ARWorld SDK-Android 现只支持 AR 视图全屏的样式展示,所以业务方需要在展示 AR 的 activity 中配置全屏的 theme。可由业务方自己定义,也可以使用 SDK 内部的 theme(推荐)。
xml
<style name="InsightActivityTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowFullscreen">true</item>
<item name="android:windowNoTitle">true</item>
</style>
在 app 清单文件 AndroidManifest.xml 中配置,例:
xml
<activity
android:name=".activity.DemoActivity"
android:theme="@style/InsightActivityTheme" />
3.3.2. 使用第三方 AR
本版本支持 ARCore 如需使用,在 build.gradle 中添加
dependencies { ...... //google arcore implementation 'com.google.ar:core:1.23.0' }
并在 AndroidManifest.xml 中添加
xml
<application>
<meta-data
android:name="com.google.ar.core"
android:value="optional" />
</application>
本版本支持 HUAWEI AR Engine ,如需使用请在工程中添加 HUAWEI AR Engine 的库
dependencies {
......
//华为ar
implementation 'com.huawei.hms:arenginesdk:2.15.0.4'
}
建议 SDK 接入方支持同时支持上述两种 AR,我们的 SDK 会根据机型采取适当的 AR 运行策略。
4. 接入SDK
4.1. 启用SDK
在 app 的 Application.java 类中初始化 SDK 并传入 AppKey 及 AppSecret。
开发者也可以在 Activity 中调用此方法,但是必须要保证在调用洞见 SDK 其他方法前调用此方法,否则会报错。
java
public class DemoApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
//...
EzArInsight.init(this, APP_KEY, APP_SECRET, false);
//...
}
}
参数:
- 应用上下文 context
- 开发者申请的 AppKey
- 开发者申请的 AppSecret
- 当前版本默认传入 false
4.2. 验证 SDK 可用性
SDK 内部对 AR 能力的可用性验证包含两部分:
- 验证当前手机是否支持 AR(含离线和在线两种方式)
- 判断 so 动态库是否加载成功
java
boolean isArSupport = EzArInsight.isArSupport(Context context);
isArSupport(Context context)会验证系统版本,传感器,CPU 架构是否支持 AR。
参数:
设备上下文 context
在线验证设备是否支持 AR(推荐使用该方法)
java
EzArInsight.isArSupportOnline(Context context, OnArInsightDeviceSupportCallback callback);
该方法除了验证系统版本,传感器,CPU 架构是否支持 AR 外,还会联网验证黑名单系统,将极个别不能友好支持 AR 体验的设备也排除掉。
- 检查 so 库是否成功
java
EzArInsight.checkArLibrary() throws ArInsightLibraryNotFoundException;
checkArLibrary 会检查当前 SDK 内部的 So 文件是否加载成功。如果失败会以异常的方式throw。
开发者可以根据异常判断出当前的错误日志。
4.3. SDK快速接入
目前洞见 ARWorld SDK 提供以 Landmark 为基础 AR 场景的 AR 体验:
主场景 AR 体验:通过传入主场景的 CID,调用 SDK 的 API 下载对应的主场景资源包,并通过 SDK 的自定义 AR View 展示 AR 效果。
在集成 SDK 后,开发者需要关注 SDK 中的两个主要功能类 EzArInsight.class 和EzArWorldView.class。两者主要的功能如下:
EzArInsight:主要负责业务数据(如资源包)的获取以及初始化、判断是否支持AR等功能接口的支持。
EzArWorldView:主要负责 AR 场景的加载与渲染。
注意:承载 EzArWorldView 所在 Activity 需要单独开辟一个进程进行加载,否则应用退出时,会将整个 App 杀掉。
4.3.1 创建 EzArWorldView
在Activity/Fragment的XML 布局文件中创建 EzArWorldView 控件:
xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ArWorldActivity">
<com.netease.insightar.EzArWorldView
android:id="@+id/ezArWorldView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
也可通过代码手动创建 EzArWorldView 实例。
在 Activity/Fragment 的生命周期方法中调用 EzArWorldView 对应的生命周期方法。下面以Activity 为例:
java
public class ArWorldActivity extends AppCompatActivity {
private EzArWorldView mArWorldView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ar_world);
mArWorldView = findViewById(R.id.ezArWorldView);
// 注意:这里需要向请求相机、存储、录音的Dangerous权限,否则EzArWorldView会显示异常或功能异常
// TODO:request dangerous permissions of CAMERA WRITE_EXTERNAL_STORAGE and RECORD_AUDIO
// create EzArWorldView
mArWorldView.create();
}
@Override
protected void onStart() {
super.onStart();
if (null != mArWorldView) {
// EzArWorldView start
mArWorldView.start();
}
}
@Override
protected void onResume() {
super.onResume();
if (null != mArWorldView) {
// EzArWorldView resume
mArWorldView.resume();
}
}
@Override
protected void onPause() {
super.onPause();
if (null != mArWorldView) {
// EzArWorldView pause
mArWorldView.pause();
}
}
@Override
protected void onStop() {
super.onStop();
if (null != mArWorldView) {
// EzArWorldView stop
mArWorldView.stop();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (null != mArWorldView) {
// EzArWorldView destroy
mArWorldView.destroy();
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// To support deep linking, we need to make sure that the client can get access to
// the last sent intent. The clients access this through a JNI api that allows them
// to get the intent set on launch. To update that after launch we have to manually
// replace the intent with the one caught here.
setIntent(intent);
mArWorldView.onNewIntent(intent);
}
// Low Memory Unity
@Override public void onLowMemory() {
super.onLowMemory();
mArWorldView.lowMemory();
}
// Trim Memory Unity
@Override public void onTrimMemory(int level) {
super.onTrimMemory(level);
mArWorldView.onTrimMemory(level);
}
// This ensures the layout will be correct.
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mArWorldView.onConfigurationChanged(newConfig);
}
// Notify Unity of the focus change.
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mArWorldView.onWindowFocusChanged(hasFocus);
}
// For some reason the multiple keyevent type is not supported by the ndk.
// Force event injection by overriding dispatchKeyEvent().
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_MULTIPLE) return mArWorldView.injectEvent(event);
return super.dispatchKeyEvent(event);
}
// Pass any events not handled by (unfocused) views straight to UnityPlayer
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return mArWorldView.injectEvent(event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
finish();
return false;
}
return mArWorldView.injectEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return mArWorldView.injectEvent(event);
}
/*API12*/
public boolean onGenericMotionEvent(MotionEvent event) {
return mArWorldView.injectEvent(event);
}
}
4.3.2 注册 AR 消息监听
通过注册 OnArInsightResultListener 来获取算法以及渲染引擎的状态和消息:
java
public class ArWorldActivity extends AppCompatActivity implements OnArInsightResultListener
,OnArInsightCaptureFileSavedCallback,
OnArInsightRecordCallback, OnArInsightUnityLoadingCallback
,RequestPermissionCallback{
private EzArWorldView mArWorldView;
...
@Override
protected void onStart() {
super.onStart();
if (null != mArWorldView) {
// EzArWorldView start
mArWorldView.start();
// register AR states and messages listener
mArWorldView.registerArInsightResultListener(this);
//注册拍照监听
mArWorldView.registerArInsightCaptureListener(this);
//注册录屏监听
mArWorldView.registerArRecordListener(this);
//注册ar场景加载进度回调监听
mArWorldView.registerArInsightUnityLoadingListener(this);
//注册权限申请回调
mArSceneView.setRequestPermissionCallback(this);
}
}
...
@Override
protected void onPause() {
super.onPause();
if (null != mArWorldView) {
// EzArWorldView pause
mArWorldView.pause();
}
}
@Override
protected void onStop() {
super.onStop();
if (null != mArWorldView) {
mArWorldView.stop();
// unreigster AR listener
mArWorldView.unregisterArInsightResultListener(this);
mArWorldView.unRegisterArInsightCaptureListener(this);
mArWorldView.unRegisterArRecordListener(this);
mArWorldView.unRegisterArInsightUnityLoadingListener(this);
}
}
...
@Override
public void on3dEventMessage(IAr3dEventMessage iAr3dEventMessage) {
// 具体实现见 4.1节「SDK与AR内容交互」
}
@Override
public void onArEngineResult(EzArAlgResult ezArAlgResult) {
// 除非需求方面需要特别关注算法状态,否则不需要实现
}
@Override
public void onArRenderResult(EzArRenderResult ezArRenderResult) {
// 除非需求方面需要特别引擎加载算法的状态,否则不需要实现
}
@Override public void onCaptureFinish(String s) {
//showErrorView("截屏成功:"+s);
}
@Override public void onCaptureError(String s) {
//showErrorView("截屏失败:"+s);
}
@Override public void onRecordStart() {
showErrorView("开始录屏");
}
@Override public void onRecordFinish(String s, @Nullable Bitmap bitmap) {
showErrorView("录屏成功:" + s);
//保存到相册.可以调用下面的代码,也可以改成自己的实现
MediaSaveUtil.saveMediaFile(this, s, MediaSaveUtil.MEDIA_TYPE_VIDEO);
}
@Override public void onRecordError(String s) {
showErrorView("录屏失败:" + s);
}
@Override
public void requestPermissionAccess(int type) {
//unity申请权限请求
}
@Override public void onUnityLoading(int loadType, int downloadProgress) {
if (loadType == 0) {//主场景资源加载,要覆盖全屏
showLoadingView("资源加载中" + downloadProgress + "%", loadType);
} else {//蒙层布局,一般用于子场景资源加载,如果没有该场景,可以忽略
showLoadingView("内容加载中" + downloadProgress + "%", loadType);
}
}
@Override public void onUnityLoadingComplete(int loadType) {
hideLoadingView(loadType);
}
}
4.3.3 获取AR场景数据
EzArWorldView 加载 AR 场景资源时需要本地有资源文件,而资源文件是通过 EzArInsight 类从洞见的平台上进行获取:
java
public class ArWorldActivity extends AppCompatActivity {
private EzArWorldView mArWorldView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ar_world);
mArWorldView = findViewById(R.id.ezArWorldView);
...
loadData();
}
...
private void loadData() {
// 下载AR资源,CID需要通过后台数据获取,来源有两个:
// 1.通过调用EzArInsight.getLandmarkListAll()方法获取当前APP_KEY能获取到的资源列表
// 2.写死固定的CID
EzArInsight.loadLandmarkDataByCid(CID, new SceneResourceFetchCallback(ArWorldActivity.this));
}
private static class SceneResourceFetchCallback implements OnArInsightDataCallback<EzArLandmark> {
// 使用弱引用包裹Callback,避免异步回调引起内存泄漏
private WeakReference<ArWorldActivity> mActivityRef;
SceneResourceFetchCallback(ArWorldActivity activity) {
mActivityRef = new WeakReference<>(activity);
}
@Override
public void onDownloadStart(String id) {
ArWorldActivity ins = mActivityRef.get();
if (null == ins) {
return;
}
// 显示开始下载交互UI,由外部自定义
ins.showLoadingView("资源加载中",0);
}
@Override
public void onDownloadProgress(String id, int progress) {
ArWorldActivity ins = mActivityRef.get();
if (null == ins) {
return;
}
// 显示下载进度交互UI,由外部自定义
ins.showLoadingProgress(progress);
}
@Override
public void onDownloadError(String id, int code, String msg) {
ArWorldActivity ins = mActivityRef.get();
if (null == ins) {
return;
}
// 显示下载失败交互UI,由外部自定义
ins.showErrorView();
}
@Override
public void onDownloadPause(String id) {
// 可以不用实现
}
@Override
public void onDownloadResume(String id) {
// 可以不用实现
}
@Override
public void onDownloadSucc(String id) {
// 可以不用实现
}
@Override
public void onNetworkDataSucc(@Nullable EzArLandmark ezArLandmark) {
// 获取后台取到的相关数据模型信息
}
@Override
public void onNetworkDataError(int code, String msg) {
ArWorldActivity ins = mActivityRef.get();
if (null == ins) {
return;
}
// 显示数据获取错误交互UI,由外部自定义
ins.showErrorView();
}
@Override
public void onUnzipStart(String id) {
ArWorldActivity ins = mActivityRef.get();
if (null == ins) {
return;
}
// 显示正在解压交互UI,由外部自定义
ins.showUnzipStartView();
}
@Override
public void onUnzipEnd(String id, String destFilePath) {
ArWorldActivity ins = mActivityRef.get();
if (null == ins) {
return;
}
// 解压完成加载AR场景资源
ins.showAr(id, destFilePath);
}
}
}
4.3.4 加载AR场景资源
在 unZipEnd(String id,String destFilePath) 回调方法的参数当中,destFilePath
表示的是本地资源下载后的路径,需要调用 showAr(id, destFilePath) 方法进行 AR 场景资源的加载。
java
public class ArWorldActivity extends AppCompatActivity implements OnArInsightResultListener{
private EzArWorldView mArWorldView;
...
private void showAr(String id, String destFilePath) {
if (TextUtils.isEmpty(destFilePath) || !new File(destFilePath).exists()) {
showErrorView("本地资源包异常...");
return;
}
try {
mArWorldView.doArShowView(id, destFilePath);
showLoadingView("场景资源加载中...", 0);
} catch (ArResourceNotFoundException e) {
showErrorView("资源包异常...");
e.printStackTrace();
}
}
...
}
到这一步,顺利的话,基本可以正常加载一个 AR 场景了。
4.3.5 加载AR场景资源进度回调
跟 Unity 游戏一样,加载 AR 场景需要有段时间,这个时候 SDK 提供了相关进度的回调接口供上层接入方使用。
EzArWorldView 类对外提供两个方法供外部注册和反注册监听
java
//注册加载进度监听器
public void registerArInsightUnityLoadingListener(OnArInsightUnityLoadingCallback listener);
//反注册加载进度监听器
public void unRegisterArInsightUnityLoadingListener(OnArInsightUnityLoadingCallback listener);
OnArInsightUnityLoadingCallback.java 是回调接口,当外部通过 EzArWorldView 的方式集成时,需要自己手动实现。而且为了防止内存泄露,请外部在 Activity 或者 Fragment 的对称生命周期内依次调用 register 和 unregister 方法(onStart/onStop 或者 onResume/onPause)。
java
OnArInsightUnityLoadingCallback.java
/**
* 对外提供的拍照结果回调接口,如果接入方需要关心,可以设置监听器
* 所有的回调都是在UI线程中
*/
public interface OnArInsightUnityLoadingCallback {
/**
* 加载进度回调
*
* @param loadType 加载类型 0:全屏,1:透明非全屏蒙层
* @param downloadProgress 下载进度
*
*/
void onUnityLoading(int loadType,int downloadProgress);
/**
* 加载完成
* @param loadType 加载类型 0:全屏,1:非全屏
*/
void onUnityLoadingComplete(int loadType);
}
下面介绍如何在SDK中使用拍照和录制视频的功能。
4.4. 使用拍照与录制视频
4.4.1关于 SDK 接入方调用拍照功能
EzArWorldView 类对外提供两个方法供外部注册和反注册监听
java
//注册拍照监听器
public void registerArInsightCaptureListener(OnArInsightCaptureFileSavedCallback listener);
//反注册拍照监听器
public void unRegisterArInsightCaptureListener(OnArInsightCaptureFileSavedCallback listener);
OnArInsightCaptureFileSavedCallback.java 是回调接口,当外部通过 EzArWorldView 的方式集成时,需要自己手动实现。而且为了防止内存泄露,请外部在 Activity 或者 Fragment 的对称生命周期内依次调用 register 和 unregister 方法(onStart/onStop 或者 onResume/onPause)。
java
OnArInsightCaptureFileSavedCallback.java
/**
* 对外提供的拍照结果回调接口,如果接入方需要关心,可以设置监听器
* 所有的回调都是在UI线程中
*/
public interface OnArInsightCaptureFileSavedCallback {
/**
* 拍照结束
* @param savePath 图片保存路径
*/
void onCaptureFinish(String savePath);
/**
* 拍照失败
* @param msg
*/
void onCaptureError(String msg);
}
4.4.2关于 SDK 接入方调用录屏功能
SDK 内部提供 AR 场景的录屏功能,具体接入步骤如下:
由于录屏的行为是异步的,外部开发者需要在界面初始化完成(onViewCreated)和界面退出(onDestroyView)中完成录屏监听器的注册和反注册。当然如果外部是以Activity的形式接入。
也可以在视图的对称生命周期内完成监听器的注册和反注册,例如 onResume - onPause 以及 onStart - onStop。
java
protected void registerArRecordListener(OnArInsightRecordCallback listener);
protected void unRegisterArRecordListener(OnArInsightRecordCallback listener);
注意:如果不注册监听器,前台不会收到录屏的回调。只会保存到文件夹中。由于 Unity 录屏的特殊性,在 App 生命周期里只要执行了 onPause 方法后,SDK 会自动停止录屏。
触发开始录屏
java
protected void doArStartRecord();
触发结束录屏
java
protected void doArStopRecord();
OnArInsightRecordCallback.java
java
/**
* ar 界面录屏的回调
* 由于录屏的行为是异步,所以相应的结果都是通过回调给出,
* 所有的回调都是在主线程中回调
*/
public interface OnArInsightRecordCallback {
/**
* 录屏开始
*/
void onRecordStart();
/**
* 录屏结束
* @param savePath 录屏文件存储的路径
* @param thumbnailBitmap 缩略图
*/
void onRecordFinish(String savePath, @Nullable Bitmap thumbnailBitmap);
/**
* 录屏产生错误
* @param msg
*/
void onRecordError(String msg);
}
5. 使用SDK
5.1 SDK 与 AR 内容交互
在制作完 AR 内容后,内容里会存在与上层业务的交互。SDK 提供相关的接口供外部实现或者重载。
强烈建议开发者实现和【AR内容交互】的所有消息协议,便于后续业务需求扩展需要
EzArWorldView 类对外提供两个方法供外部注册和反注册监听。
java
//注册内容及算法交互监听器
public void registerArInsightResultListener(OnArInsightResultListener listener);
//反注册内容及算法交互监听器
public void unregisterArInsightResultListener(OnArInsightResultListener listener);
OnArInsightResultListener.java 是回调接口,当外部通过 EzArWorldView 的方式集成时,需要自己手动实现。而且为了防止内存泄露,请外部在 Activity 或者 Fragment 的对称生命周期内依次调用 register 和 unregister 方法(onStart/onStop 或者 onResume/onPause)。
java
OnArInsightResultListener.java
/**
* 对外提供的ar结果回调接口,如果接入方需要关心,可以设置监听器
* 所有的回调都是在UI线程中
*/
public interface OnArInsightResultListener {
/**
* 回调绘制引擎运行的结果
* @param message 回调的数据结构
*/
void on3dEventMessage(IAr3dEventMessage message);
/**
* 回调当前ar算法运行状态
* @param result
*/
void onArEngineResult(EzArAlgResult result);}
/**
* 回调当前ar渲染引擎运行状态
*
* @param result 渲染引擎回调状态结果
*/
void onArRenderResult(EzArRenderResult result);
IAr3dEventMessage.java 是一个接口,不同的 type,SDK 内部会回调不同的数据 model。
java
IAr3dEventMessage.java
public interface IAr3dEventMessage {
int TYPE_EXECUTE_SCRIPTS = 100;
int TYPE_CLOSE_AR = 101;
int TYPE_RELOAD_AR = 102;
int TYPE_OPEN_URL = 108;
int TYPE_SCREENSHOT = 110;
int TYPE_SHARE = 111;
int TYPE_SCREEN_RECORD_START = 112;
int TYPE_SCREEN_RECORD_STOP = 113;
int TYPE_CALLBACK_SCRIPT = 500;
/**
* 事件类型
* @return
*/
int type();
/**
* 事件版本
*/
int version();
}
继承于 I3dEventMessage.java 的各个 model 定义:
类关系图:
从类关系图可以看出,SDK 对外提供的 3dEventMessagemodel 都是 I3dEventMessage.java 的实现。具体每个 model 属性定义如下:
类名 | 属性 | 备注 |
---|---|---|
Base3dEventMessage | mMessageType - 消息类型 mMessageVersion - 消息版本 | 抽象类,外部无法初始化 |
Common3dEventMessage | mCommonMessage - 通用字段 | 除了下面自定义的消息体,所有剩余的消息type都是已这个model回调给外层 |
Share3dEventMessage | mShareType - 分享类型(1 text; 2 image; 3 music; 4 video; 5 url) mTitle - 分享标题 mDesc - 分享描述 mUrl - 分享的url mLogoBitmapPath - ar内容内部的分享图路径,是一个绝对路径 mScreenshot - 合成后的分享成图,用于分享给三方 mVideoPath - 视频录制后存储在本地的绝对路径 | type =TYPE_SHARE 内容发送的分享的消息 |
ExecuteScript3dEventMessage | mScriptName - 脚本函数名称
mIdentity - 消息名称,即内容回调告诉给外部执行的是哪步操作,外部可能需要把这个字段重新通过i3dExecuteScript重新发给内容 (v2.0及之后版本提供) | type =TYPE_EXECUTE_SCRIPTS 内容发送的执行一段内容脚本的消息 |
OpenUrl3dEventMessage | mOpenUrl - 外部打开的链接 mOpenJson - 打开链接携带的参数,由内容开发者和外部具体协议 | type =TYPE_OPEN_URL 内容发送的打开一个外部链接的消息 |
Reload3dEventMessage | mPid - 携带的事件ID,对于云识别,该参数为null errorMessage - 内部reload发生错误时的错误信息 isLbsRequired - 该事件是否需要LBS地理位置信息支持的结果 | type =TYPE_RELOAD_AR 内容发送的重新加载当前界面内容的指令消息 |
Screenshot3dEventMessage | image - 截屏传出的Bitmap type - 截屏消息的业务用途type,可由内容和接入方约定 identity - 消息收到以后给内容的响应回调 | type = TYPE_SCREENSHOT |
type定义如下:
类型 | 描述 | 对应的数据model |
---|---|---|
TYPE_RELOAD_AR | 重新reload ar的消息,在云识别模式下,该指令表示云识别识别到的具体事件的pid,非云识别模式下,代表需要上层完成reload ar的指令 | Reload3dEventMessage |
TYPE_EXECUTE_SCRIPTS | 执行指定的内容脚本 | ExecuteScript3dEventMessage |
TYPE_SHARE | 分享的消息 | Share3dEventMessage |
TYPE_SCREENSHOT | 截屏的消息 | Common3dEventMessage |
TYPE_CLOSE_AR | 关闭ar界面 | Common3dEventMessage |
TYPE_OPEN_URL | 打开外部链接 | OpenUrl3dEventMessage |
TYPE_CALLBACK_SCRIPT | 脚本执行回调消息 | Common3dEventMessage |
5.2 SDK 与AR 算法交互
在 【5.1 SDK和AR内容交互】 章节中介绍了内容与上层的交互,如果外部需要关心 AR 算法的回调结果,只需要注册监听器即可。
监听器和内容一样:
java
//注册内容及算法交互监听器
public void registerArInsightResultListener(OnArInsightResultListener listener);
//反注册内容及算法交互监听器
public void unregisterArInsightResultListener(OnArInsightResultListener listener);
在 OnArInsightResultListener.java 中 OnArEngineResult 里 SDK 会回调算法执行结果。
开发者只需要关心改回调的参数 EzArAlgResult。
该 model 定义如下:
java
/**
* @desc 洞见算法引擎对外回调结果
*/
public class EzArAlgResult {
/**
* 错误码
* @see com.netease.insightar.ar.InsightARError
*/
private int mErrorCode;
/**
* 当前引擎的状态
* @see com.netease.insightar.ar.InsightARState
*/
private int mArState;
public int getErrorCode() {
return mErrorCode;
}
public void setErrorCode(int errorCode) {
mErrorCode = errorCode;
}
public String getErrorMsg() {
return InsightARError.getErrorMessage(mErrorCode);
}
public int getArState() {
return mArState;
}
public void setArState(int arState) {
mArState = arState;
}
}
具体的 errorCode 及 arState:
InsightARError.java
java
public class InsightARError {
public final static int SUCCESS = 0;
public final static int ERROR_CAMERA_DEVICE = 1;
public final static int ERROR_CAMERA_PERMISSION = 2;
public final static int ERROR_CAMERA_TIMEOUT = 3;
public final static int ERROR_CAMERA_DISABLE = 4;
public final static int ERROR_CAMERA_UNKNOWN = 5;
public final static int WARNING_IMU_ACCESS = 6;
public final static int WARNING_ACCE_ACCESS = 7;
public final static int WARNING_GYRO_ACCESS = 8;
public final static int WARNING_GRAV_ACCESS = 9;
public final static int ERROR_NO_IMU_DATA = 10;
public final static int ERROR_CONFIGFILE_NOT_FOUND = 11;
public final static int ERROR_CONFIGFILE_ERROR = 12;
public final static int ERROR_AppKey_SECRET_ERROR = 13;
public final static int WARNING_AR_RUNGING = 14;
public final static int ERROR_DEVICE_UNSUPPORTED = 15;
public final static int WARNING_INSUFFICIENT_FEATURES = 16;
public final static int WARNING_TRACK_BAD = 17;
public final static int WARNING_LOW_LIGHT = 18;
public final static int WARNING_EXCESSIVE_MOTION = 19;
public final static int ERROR_IMU_ACCESS = 20;
public final static int ERROR_API_LEVEL = 21;
public final static int ERROR_ARCORE_INIT_FAIL = 22;
public final static int ERROR_ARCORE_RESUME_FAIL = 23;
public static String getErrorMessage(int error){
String msg = "SUCCESS";
switch (error){
case ERROR_CAMERA_DEVICE:
msg = "ERROR_CAMERA_DEVICE.Camera may be in use,or maybe permission is not granted";
break;
case ERROR_CAMERA_PERMISSION:
msg = "ERROR_CAMERA_PERMISSION.Camera permission is not granted";
break;
case ERROR_CAMERA_TIMEOUT:
msg = "ERROR_CAMERA_TIMEOUT.Opening camera is out of wait Time";
break;
case ERROR_CAMERA_DISABLE:
msg = "ERROR_CAMERA_DISABLE.Camera is not available,or permission may be not granted";
break;
case ERROR_CAMERA_UNKNOWN:
msg = "ERROR_CAMERA_UNKNOWN.Camera is not available with unknown reason";
break;
case WARNING_IMU_ACCESS:
msg = "WARNING_IMU_ACCESS.This device's IMU sensor service is not available";
break;
case WARNING_ACCE_ACCESS:
msg = "WARNING_ACCE_ACCESS.This device's accelerator sensor is not available";
break;
case WARNING_GYRO_ACCESS:
msg = "WARNING_GYRO_ACCESS.This device's gyroscrope sensor is not available";
break;
case WARNING_GRAV_ACCESS:
msg = "WARNING_GRAV_ACCESS.This device's gravity sensor is not available";
break;
case ERROR_NO_IMU_DATA:
msg = "ERROR_NO_IMU_DATA.This type ARScene is running with IMU sensors which is not available";
break;
case ERROR_CONFIGFILE_NOT_FOUND:
msg = "ERROR_CONFIGFILE_NOT_FOUND.InsightAR SDK config files is missing,please check the files configured in MR";
break;
case ERROR_CONFIGFILE_ERROR:
msg = "ERROR_CONFIGFILE_ERROR.InsightAR SDK config files is Wrong,please check the files configured in MR";
break;
case ERROR_AppKey_SECRET_ERROR:
msg = "ERROR_AppKey_SECRET_ERROR.Checking authentication work went wrong,plaese check the network, AppKey or AppSecret ";
break;
case WARNING_AR_RUNGING:
msg = "WARNING_AR_RUNGING.Current ARScene is running";
break;
case ERROR_DEVICE_UNSUPPORTED:
msg = "ERROR_DEVICE_UNSUPPORTED.Sorry this device is not support by InsightAR,maybe because OS version is too low,or performace is not satisfied";
break;
case WARNING_INSUFFICIENT_FEATURES:
msg = "WARNING_INSUFFICIENT_FEATURES.This environment may be too simple and lack of features for AR-Tracking";
break;
case WARNING_TRACK_BAD:
msg = "WARNING_TRACK_BAD.Sorry ,AR is working badly right now";
break;
case WARNING_LOW_LIGHT:
msg = "WARNING_LOW_LIGHT.This environment may be too dark.Please move to somewhere brighter";
break;
case WARNING_EXCESSIVE_MOTION:
msg = "WARNING_EXCESSIVEMOTION.Your movement was too fast";
break;
case ERROR_IMU_ACCESS:
msg = "ERROR_IMU_ACCESS.Some problem occur when accessing sensors";
break;
case ERROR_API_LEVEL:
msg = "ERROR_API_LEVEL.This device's API level may be not in supported list";
break;
case ERROR_ARCORE_INIT_FAIL:
msg = "ERROR_ARCORE_INIT_FAIL.Failed to init ARCore";
break;
case ERROR_ARCORE_RESUME_FAIL:
msg = "ERROR_ARCORE_RESUME_FAIL.Failed to resume ARCore";
break;
default:
break;
}
return msg;
}
}
同样的,如果关心 Unity 引擎加载结果,请参看如下状态值:
InsightARState.java
java
public class InsightARState {
public final static int UNINITIALIZED = 0;
public final static int INITING = 1;
public final static int INIT_OK = 2;//unity加载资源成功
public final static int INIT_FAIL = 3;//unity加载资源失败
public final static int DETECTING = 4;
public final static int DETECT_OK = 5;
public final static int DETECT_FAIL = 6;
public final static int TRACKING = 7;
public final static int TRACK_LIMITED = 8;
public final static int TRACK_LOST = 9;
public final static int TRACK_FAIL = 10;
public final static int TRACK_STOP = 11;
public final static int DETECT_TIMEOUT = 12;
}
5.3 SDK 申请应用权限
SDK 需要使用应用权限如麦克风,camera 等时需要注册权限申请回调
java
//设置权限申请回调
public void setRequestPermissionCallback(RequestPermissionCallback permissionCallback);
RequestPermissionCallback 实现见下面示例代码
java
@Override
public void requestPermissionAccess(int type) {
switch (type) {
case PermissionsUtil.IPermissionTypeCamera:
PermissionsUtil.with(this)
.addPermission(Manifest.permission.CAMERA)
.initPermission();
break;
case PermissionsUtil.IPermissionTypeAudio:
PermissionsUtil.with(this)
.addPermission(Manifest.permission.RECORD_AUDIO)
.initPermission();
break;
case PermissionsUtil.IPermissionTypeAlbum:
PermissionsUtil.with(this)
.addPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.initPermission();
break;
case PermissionsUtil.IPermissionTypeGPS:
PermissionsUtil.with(this)
.addPermission(Manifest.permission.ACCESS_FINE_LOCATION)
.initPermission();
break;
case PermissionsUtil.IPermissionTypeRead:
PermissionsUtil.with(this)
.addPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
.initPermission();
break;
}
权限申请完毕后,需要将权限申请结果告知 UnitySDK
java
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1 && grantResults.length > 0) {
for (int i = 0; i < permissions.length; i++) {
if (permissions[i].equals(Manifest.permission.CAMERA)) {
mArSceneView.setPermissionResult(PermissionsUtil.IPermissionTypeCamera,
grantResults[i] != PackageManager.PERMISSION_GRANTED ? 1 : 2);
}
if (permissions[i].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
mArSceneView.setPermissionResult(PermissionsUtil.IPermissionTypeAlbum,
grantResults[i] != PackageManager.PERMISSION_GRANTED ? 1 : 2);
}
if (permissions[i].equals(Manifest.permission.RECORD_AUDIO)) {
mArSceneView.setPermissionResult(PermissionsUtil.IPermissionTypeAudio,
grantResults[i] != PackageManager.PERMISSION_GRANTED ? 1 : 2);
}
if (permissions[i].equals(Manifest.permission.ACCESS_FINE_LOCATION)
|| permissions[i].equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
mArSceneView.setPermissionResult(PermissionsUtil.IPermissionTypeGPS,
grantResults[i] != PackageManager.PERMISSION_GRANTED ? 1 : 2);
}
}
}
}