大概操作步骤按笔记006执行。
OS:Windows10
Android Studio3.2
Unity 2018.1.2f1
Unity端:主调方,UI
Android端:底层接口,打包aar方式
对比百度demo,导入百度lib,如图:
模仿demo,新建Activity,如下:
//apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
// Add
sourceSets {
main {
//Path to your source code
main {
jniLibs.srcDir('libs')
jni.srcDirs = []
}
java {
srcDir 'src/main/java'
}
}
}
defaultConfig {
// applicationId "com.polin.baiduarmapsdk"
minSdkVersion 15
targetSdkVersion 28
// versionCode 1
// versionName "1.0"
// testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// Add
lintOptions {
abortOnError false
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation files('libs/android-support-v4.jar')
implementation files('libs/BaiduLBS_Android.jar')
implementation files('libs/classes.jar')
implementation files('libs/gson-2.2.4.jar')
implementation files('libs/org.apache.http.legacy.jar')
}
// Add Backup
//task to delete the old jar
task deleteOldJar(type: Delete) {
delete 'release/BaiduMapKit.jar'
}
//task to export contents as jar
task exportJar(type: Copy) {
from('build/intermediates/packaged-classes/release/')
into('release/')
include('classes.jar')
///Rename the jar
rename('classes.jar', 'BaiduMapKit.jar')
}
exportJar.dependsOn(deleteOldJar, build)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.polin.baiduarmapsdk">
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION">uses-permission>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION">uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE">uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE">uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE">uses-permission>
<uses-permission android:name="android.permission.READ_PHONE_STATE">uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">uses-permission>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:label="@string/app_name">
<meta-data
android:name="com.baidu.lbsapi.API_KEY"
android:value="****">
meta-data>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
<meta-data
android:name="unityplayer.UnityActivity"
android:value="true" />
activity>
<activity android:name=".ArActivity" >
<meta-data
android:name="unityplayer.UnityActivity"
android:value="true" />
activity>
<activity android:name=".BuildingArActivity" >
<meta-data
android:name="unityplayer.UnityActivity"
android:value="true" />
activity>
<activity android:name=".SceneryArActivity">
<meta-data
android:name="unityplayer.UnityActivity"
android:value="true" />
activity>
<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote">service>
application>
manifest>
在该目录下找到aar包,连同AndroidManifest.xml一起,即是我们将放入Unity中的Android资源。
至此,Android Studio端的配置完成。
将MainActivity作为入口,编写工具类LocSdkClient、PermissionsChecker、UntiyClient。
LocSdkClient:
public class LocSdkClient {
/**
*
* 百度定位sdk 坐标获取工具类
*/
private static LocSdkClient mInstance = null;
public LocationClient mLocationClient = null;
private LocSdkClient(Context context) {
mLocationClient = new LocationClient(context.getApplicationContext());
// 声明LocationClient类
// mLocationClient.registerLocationListener(myListener);
// 注册监听函数
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
// 可选,设置定位模式,默认高精度
// LocationMode.Hight_Accuracy:高精度;
// LocationMode. Battery_Saving:低功耗;
// LocationMode. Device_Sensors:仅使用设备;
option.setCoorType("bd09");
// 可选,设置返回经纬度坐标类型,默认gcj02
// gcj02:国测局坐标;
// bd09ll:百度经纬度坐标;
// bd09:百度墨卡托坐标;
// 海外地区定位,无需设置坐标类型,统一返回wgs84类型坐标
option.setScanSpan(2000);
// 可选,设置发起定位请求的间隔,int类型,单位ms
// 如果设置为0,则代表单次定位,即仅定位一次,默认为0
// 如果设置非0,需设置1000ms以上才有效
option.setOpenGps(true);
// 可选,设置是否使用gps,默认false
// 使用高精度和仅用设备两种定位模式的,参数必须设置为true
option.setLocationNotify(true);
// 可选,设置是否当GPS有效时按照1S/1次频率输出GPS结果,默认false
option.setIgnoreKillProcess(false);
// 可选,定位SDK内部是一个service,并放到了独立进程。
// 设置是否在stop的时候杀死这个进程,默认(建议)不杀死,即setIgnoreKillProcess(true)
option.SetIgnoreCacheException(false);
// 可选,设置是否收集Crash信息,默认收集,即参数为false
option.setWifiCacheTimeOut(5 * 60 * 1000);
// 可选,7.2版本新增能力
// 如果设置了该接口,首次启动定位时,会先判断当前WiFi是否超出有效期,若超出有效期,会先重新扫描WiFi,然后定位
option.setEnableSimulateGps(false);
// 可选,设置是否需要过滤GPS仿真结果,默认需要,即参数为false
mLocationClient.setLocOption(option);
// mLocationClient为第二步初始化过的LocationClient对象
// 需将配置好的LocationClientOption对象,通过setLocOption方法传递给LocationClient对象使用
// 更多LocationClientOption的配置,请参照类参考中LocationClientOption类的详细说明
mLocationClient.start();
// mLocationClient为第二步初始化过的LocationClient对象
// 调用LocationClient的start()方法,便可发起定位请求e
}
public LocationClient getLocationStart() {
return mLocationClient;
}
public static LocSdkClient getInstance(Context context) {
if (mInstance == null) {
Class var0 = LocSdkClient.class;
synchronized (LocSdkClient.class) {
if (mInstance == null) {
mInstance = new LocSdkClient(context);
}
}
}
return mInstance;
}
}
PermissionsChecker:
/**
* 权限申请
*/
public class PermissionsChecker {
private final Context mContext;
public PermissionsChecker(Context context) {
mContext = context.getApplicationContext();
}
// 判断权限集合
public boolean lacksPermissions() {
String[] permissions = new String[] {
// Manifest.permission.FLASHLIGHT,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.INTERNET,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.ACCESS_WIFI_STATE
};
for (String permission : permissions) {
if (lacksPermission(permission)) {
return true;
}
}
return false;
}
// 判断是否缺少权限
private boolean lacksPermission(String permission) {
return ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_DENIED;
}
}
UntiyClient:
public class UntiyClient extends UnityPlayerActivity {
/**
* unity项目启动时的的上下文
*/
private static Activity _unityActivity;
/**
* 获取unity项目的上下文
* @return
*/
public static Activity GetActivity(){
if(null == _unityActivity) {
try {
Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
Activity activity = (Activity) classtype.getDeclaredField("currentActivity").get(classtype);
_unityActivity = activity;
} catch (ClassNotFoundException e) {
} catch (IllegalAccessException e) {
} catch (NoSuchFieldException e) {
}
}
return _unityActivity;
}
/**
* 调用Unity的方法
* @param gameObjectName 调用的GameObject的名称
* @param functionName 方法名
* @param args 参数
* @return 调用是否成功
*/
public static boolean CallUnity(String gameObjectName, String functionName, String args){
try {
Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
Method method =classtype.getMethod("UnitySendMessage", String.class,String.class,String.class);
method.invoke(classtype,gameObjectName,functionName,args);
return true;
} catch (ClassNotFoundException e) {
} catch (NoSuchMethodException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
}
return false;
}
}
MainActivity:
public class MainActivity extends UnityPlayerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
}
// MainActivity初始化函数
public void init(){
// 判断权限
PermissionsChecker permissionsChecker = new PermissionsChecker(GetActivity());
if (permissionsChecker.lacksPermissions()) {
Toast.makeText(GetActivity(), "缺少权限,请开启权限!", Toast.LENGTH_LONG).show();
openSetting();
}
else{
Toast.makeText(GetActivity(), "权限已开启!", Toast.LENGTH_LONG).show();
}
}
/**
* 打开设置权限界面
*
* @param
*/
public void openSetting() {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
}
导出aar包与AndroidManifest.xml到Unity的Plugins/Android中,编写Unity主调脚本:
public class UIControl : MonoBehaviour {
// Use this for initialization
void Start () {
transform.Find("MainActivityInit").GetComponent<Button>().onClick.AddListener(InitMainActivity);
}
public void InitMainActivity()
{
AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
currentActivity.Call("init");
}
}
打包apk
报错1.
解决方案1.
把Unity打包方式从Gradle改为Internal。
报错2.
解决方案2.
报错信息显示:aar包中的Unity接口包classes.jar重复了,那就删掉。
用压缩软件打开aar包,找到libs目录下的classes.jar,删除此包。
报错3.
解决方案3.
这些资源找不到,再次打开aar包,找到./res/values/values.xml并进行修改:
<resources>
<color name="colorAccent">#D81B60color>
<color name="colorPrimary">#008577color>
<color name="colorPrimaryDark">#00574Bcolor>
<string name="app_name">BaiduArMapSDKstring>
<style name="AppTheme" parent="android:Theme.AppCompat.Light.DarkActionBar">
- "android:colorPrimary"
>@color/colorPrimary
- "android:colorPrimaryDark"
>@color/colorPrimaryDark
- "android:colorAccent"
>@color/colorAccent
style>
resources>
<resources>
<color name="colorAccent">#D81B60color>
<color name="colorPrimary">#008577color>
<color name="colorPrimaryDark">#00574Bcolor>
<string name="app_name">BaiduArMapSDKstring>
<style name="AppTheme" parent="android:Theme.Light">
- "android:colorPrimary"
>@color/colorPrimary
- "android:colorPrimaryDark"
>@color/colorPrimaryDark
- "android:colorAccent"
>@color/colorAccent
style>
resources>
报错5.
解决方案5.
layout.xml报错,我们去aar包中找到它:./res/layout/activity_main.xml,删掉这些:
保存,在Unity中reimport,继续打包。
报错6.
解决方案6.
LocSdkClient类名不一致,删掉此类,重新建类。
依然报错,clean project,rebuild project,重新导包。
此步骤成功。_
经测试,aar包与AndroidManifest.xml缺一不可!
包括手机权限申请和百度Key认证
修改MainActivity.xml:
public class MainActivity extends UnityPlayerActivity implements OnGetDataResultListener, OnGetPoiSearchResultListener{
public static ArInfoScenery arInfoScenery; // 景区
public static ArBuildingResponse arBuildingResponse; // 识楼
public static List<PoiInfoImpl> poiInfos; // 探索
private PoiSearch mPoiSearch = null;
private ArSdkManager mArSdkManager = null;
private LatLng center = new LatLng(22.866084, 108.285501);
int radius = 500; // 500米半径
private int loadIndex = 0;
// 自定义多点数据
private ArLatLng[] latLngs = {
new ArLatLng(22.865926, 108.284935),
new ArLatLng(22.866817, 108.286763),
new ArLatLng(22.866484, 108.287801),
new ArLatLng(22.866531, 108.285363),
new ArLatLng(22.863861, 108.285308)
};
// MainActivity初始化函数
public void init(){
// ArSDK模块初始化
ArSdkManager.getInstance().initApplication(GetActivity().getApplication(), new MyGeneralListener());
// 若用百度定位sdk,需要在此初始化定位SDK
LocSdkClient.getInstance(GetActivity()).getLocationStart();
// 若用探索功能需要再这集成检索模块 在使用 SDK 各组间之前初始化 context 信息,传入 ApplicationContext
SDKInitializer.initialize(GetActivity().getApplication());
// 检索模块 自4.3.0起,百度地图SDK所有接口均支持百度坐标和国测局坐标,用此方法设置您使用的坐标类型.
// 包括BD09LL和GCJ02两种坐标,默认是BD09LL坐标。
SDKInitializer.setCoordType(CoordType.BD09LL);
// 如果需要检索,初始化搜索模块,注册搜索事件监听
mPoiSearch = PoiSearch.newInstance();
mPoiSearch.setOnGetPoiSearchResultListener(this);
// 如果需要Ar景区功能、Ar识楼功能要注册监听
mArSdkManager = ArSdkManager.getInstance();
mArSdkManager.setOnGetDataResultListener(this);
// 判断权限
PermissionsChecker permissionsChecker = new PermissionsChecker(GetActivity());
if (permissionsChecker.lacksPermissions()) {
Toast.makeText(GetActivity(), "缺少权限,请开启权限!", Toast.LENGTH_LONG).show();
openSetting();
}
else{
Toast.makeText(GetActivity(), "权限已开启!", Toast.LENGTH_LONG).show();
}
}
/**
* 打开设置权限界面
*
* @param
*/
public void openSetting() {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
static class MyGeneralListener implements MKGeneralListener {
// 1、事件监听,用来处理通常的网络错误,授权验证错误等
@Override
public void onGetPermissionState(int iError) {
// 2、非零值表示key验证未通过
if (iError != 0) {
// 授权Key错误:
Toast.makeText(GetActivity(),"arsdk 验证异常,请在AndoridManifest.xml中输入正确的授权Key,并检查您的网络连接是否正常!error: " + iError, Toast
.LENGTH_LONG).show();
} else {
Toast.makeText(GetActivity(), "key认证成功", Toast.LENGTH_LONG).show();
}
}
// 回调给ArSDK获取坐标(demo调用百度定位sdk)
@Override
public ArBDLocation onGetBDLocation() {
// 3、用于传递给ArSdk经纬度信息
// a、首先通过百度地图定位SDK获取经纬度信息
// b、包装经纬度信息到ArSdk的ArBDLocation类中 return即可
BDLocation location =
LocSdkClient.getInstance(ArSdkManager.getInstance().getAppContext()).getLocationStart()
.getLastKnownLocation();
if (location == null) {
return null;
}
ArBDLocation arBDLocation = new ArBDLocation();
// 设置经纬度信息
arBDLocation.setLongitude(location.getLongitude());
arBDLocation.setLatitude(location.getLatitude());
return arBDLocation;
}
}
@Override
public void onGetPoiResult(PoiResult result) {
if (result == null || result.error == SearchResult.ERRORNO.RESULT_NOT_FOUND) {
Toast.makeText(this, "未找到结果", Toast.LENGTH_LONG)
.show();
return;
}
if (result.error == SearchResult.ERRORNO.NO_ERROR) {
poiInfos = new ArrayList<PoiInfoImpl>();
for (PoiInfo poi : result.getAllPoi()) {
ArPoiInfo poiInfo = new ArPoiInfo();
ArLatLng arLatLng = new ArLatLng(poi.location.latitude, poi.location.longitude);
poiInfo.name = poi.name;
poiInfo.location = arLatLng;
PoiInfoImpl poiImpl = new PoiInfoImpl();
poiImpl.setPoiInfo(poiInfo);
poiInfos.add(poiImpl);
}
Toast.makeText(this, "查询到: " + poiInfos.size() + " ,个POI点", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, ArActivity.class);
MainActivity.this.startActivity(intent);
return;
}
if (result.error == SearchResult.ERRORNO.AMBIGUOUS_KEYWORD) {
// 当输入关键字在本市没有找到,但在其他城市找到时,返回包含该关键字信息的城市列表
String strInfo = "在";
for (CityInfo cityInfo : result.getSuggestCityList()) {
strInfo += cityInfo.city;
strInfo += ",";
}
strInfo += "找到结果";
Toast.makeText(this, strInfo, Toast.LENGTH_LONG)
.show();
}
}
@Override
public void onGetPoiDetailResult(PoiDetailResult result) {
if (result.error != SearchResult.ERRORNO.NO_ERROR) {
Toast.makeText(this, "抱歉,未找到结果", Toast.LENGTH_SHORT)
.show();
} else {
Toast.makeText(this, result.getName() + ": " + result.getAddress(), Toast.LENGTH_SHORT)
.show();
}
}
@Override
public void onGetPoiDetailResult(PoiDetailSearchResult poiDetailSearchResult) {}
@Override
public void onGetPoiIndoorResult(PoiIndoorResult poiIndoorResult) {}
@Override
public void onGetSceneryResult(ArSceneryResponse var1){}
@Override
public void onGetBuildingResult(ArBuildingResponse var1){}
}
在MainActivity.xml中添加onClick()方法监听Unity中场景转换按键事件:
public void onClick(String index){
switch (index){
// 单点数据展示
case ConstantValue.singlePointShow:
Intent intent2SinglePoint = new Intent();
intent2SinglePoint.setClass(GetActivity(),ArActivity.class);
GetActivity().startActivity(intent2SinglePoint);
break;
// 多点数据展示
case ConstantValue.multiPointsShow:
Intent intent2MultiPoint = new Intent();
intent2MultiPoint.setClass(GetActivity(),ArActivity.class);
GetActivity().startActivity(intent2MultiPoint);
break;
// 景区功能 传入uid信息
case ConstantValue.sceneryArShow:
break;
// 识楼功能
case ConstantValue.buildingArShow:
break;
// 探索功能
case ConstantValue.exploreArShow:
break;
default:
break;
}
}
修改activity_ar.xml,默认布局会报错。证明Unity中调用的Android原生布局不能为空。以下xml为测试用。
<LinearLayout 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"
android:orientation="vertical">
<EditText
android:id="@+id/editText2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="This is ArActivity" />
LinearLayout>
修改Unity控制脚本UIControl:
public class UIControl : MonoBehaviour {
AndroidJavaClass unity;
AndroidJavaObject currentActivity;
private Button singlePointShowBtn;
private Button multiPointsShowBtn;
private Button sceneryArShowBtn;
private Button buildingArShowBtn;
private Button exploreArShowBtn;
private void Awake()
{
transform.Find("MainActivityInit").GetComponent<Button>().onClick.AddListener(InitMainActivity);
singlePointShowBtn = transform.Find("SinglePointShow").GetComponent<Button>();
multiPointsShowBtn = transform.Find("MultiPointsShow").GetComponent<Button>();
sceneryArShowBtn = transform.Find("SceneryArShow").GetComponent<Button>();
buildingArShowBtn = transform.Find("BuildingArShow").GetComponent<Button>();
exploreArShowBtn = transform.Find("ExploreArShow").GetComponent<Button>();
}
// Use this for initialization
void Start () {
unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
currentActivity = unity.GetStatic<AndroidJavaObject>("currentActivity");
singlePointShowBtn.onClick.AddListener(delegate () { OnClick(singlePointShowBtn.name); });
multiPointsShowBtn.onClick.AddListener(delegate () { OnClick(multiPointsShowBtn.name); });
sceneryArShowBtn.onClick.AddListener(delegate () { OnClick(sceneryArShowBtn.name); });
buildingArShowBtn.onClick.AddListener(delegate () { OnClick(buildingArShowBtn.name); });
exploreArShowBtn.onClick.AddListener(delegate () { OnClick(exploreArShowBtn.name); });
}
public void InitMainActivity()
{
currentActivity.Call("init");
}
public void OnClick(string id)
{
switch (id)
{
case ConstantValue.singlePointShow:
currentActivity.Call("onClick", ConstantValue.singlePointShow);
break;
case ConstantValue.multiPointsShow:
currentActivity.Call("onClick", ConstantValue.multiPointsShow);
break;
case ConstantValue.sceneryArShow:
currentActivity.Call("onClick", ConstantValue.sceneryArShow);
break;
case ConstantValue.buildingArShow:
currentActivity.Call("onClick", ConstantValue.buildingArShow);
break;
case ConstantValue.exploreArShow:
currentActivity.Call("onClick", ConstantValue.exploreArShow);
break;
default:
break;
}
}
}
重新编译,打包。
成功跳转。此外,到目前为止已成功实现Unity中调用Android原生Activity、layout。
报错1. unity调用Android,在真机上闪退
原因:aar包中的so库与unity库冲突。
解决方法:将aar包中的so库移出aar包,并删除多余空目录
显示成功
报错2.AR食堂图标依旧没有显示
原因:
待续…
[注] 若在Unity中出现不能打包的情况,可试着配置gradle.properties
注释掉该句:
org.gradle.jvmargs=-Xmx1536m
参考:
link百度API
link最简单四步查看Android studio SHA1.不用输入命令。