不管是游戏还是普通APP,经常会有用定位功能,这里我以Cocos2d-X为例基于百度地图SDK实现定位功能,其他APP其实也是类似方式。
申请百度地图AK
https://lbsyun.baidu.com/ 进入百度地图开放平台,注册并且创建应用,创建成功后可以获取到AK,不同的平台需要多次申请
IOS平台
导入百度SDK相关框架,并且在项目的 Info.plist 添加定位权限申请,具体可以参考百度SDK文档https://lbsyun.baidu.com/index.php?title=ios-locsdk/guide/create-project/manual-create
在AppController.h 文件中,添加百度定位相关的 Delegate,并且加上一些相关的成员变量,请忽略代码中的微信API:
#import <UIKit/UIKit.h> #import "WXApi.h" #import <BaiduMapAPI_Base/BMKBaseComponent.h> #import <BaiduMapAPI_Base/BMKMapManager.h> #import <BaiduMapAPI_Location/BMKLocationService.h> #import <BaiduMapAPI_Search/BMKGeocodeSearch.h> @class RootViewController; @interface AppController : NSObject <UIApplicationDelegate,WXApiDelegate, BMKLocationServiceDelegate ,BMKGeoCodeSearchDelegate> { UIWindow *window; BMKMapManager* _mapManager; BMKLocationService* _locService; BMKGeoCodeSearch *_geocodesearch; } @property(nonatomic, readonly) RootViewController* viewController; @end
在 AppController.mm中的didFinishLaunchingWithOptions中初始化百度地图服务
//百度地图 _mapManager = [[BMKMapManager alloc] init]; BOOL ret = [_mapManager start:@BD_APPKEY generalDelegate:nil]; _geocodesearch = [[BMKGeoCodeSearch alloc] init]; _geocodesearch.delegate = self; if (!ret) { NSLog(@"manager start failed!"); } _locService = [[BMKLocationService alloc] init];//初始化BMKLocationService _locService.delegate = self; [_locService startUserLocationService];//启动LocationService
//实现相关delegate 处理位置信息更新 //处理方向变更信息 - (void)didUpdateUserHeading:(BMKUserLocation *)userLocation { NSLog(@"heading is %@", userLocation.heading); } //处理位置坐标更新 - (void)didUpdateBMKUserLocation:(BMKUserLocation *)userLocation { NSLog(@"didUpdateUserLocation lat %f,long %f", userLocation.location.coordinate.latitude, userLocation.location.coordinate.longitude); BMKReverseGeoCodeOption *reverseGeocodeSearchOption = [[BMKReverseGeoCodeOption alloc] init]; reverseGeocodeSearchOption.reverseGeoPoint = userLocation.location.coordinate; BOOL flag = [_geocodesearch reverseGeoCode:reverseGeocodeSearchOption]; if (flag) { NSLog(@"反geo检索发送成功"); [_locService stopUserLocationService]; onLocationInfo(userLocation.location.coordinate.longitude, userLocation.location.coordinate.latitude); } else { NSLog(@"反geo检索发送失败"); } [reverseGeocodeSearchOption release]; } //定位结果 - (void)onGetReverseGeoCodeResult:(BMKGeoCodeSearch *)searcher result:(BMKReverseGeoCodeResult *)result errorCode:(BMKSearchErrorCode)error { NSLog(@"address:%@----%@", result.addressDetail, result.address); if (result != nil) { onAddressInfo([result.address cStringUsingEncoding:NSUTF8StringEncoding]); } else { //NSString *msg = @"定位失败!"; } } //定位失败 - (void)didFailToLocateUserWithError:(NSError *)error { NSLog(@"error:%@", error); //NSString *msg = @"定位失败!"; }
通过自定义的onLocationInfo函数,和onAddressInfo 两个静态函数将具体的经纬度与物理地址,赋值给Cocos2d-X逻辑里的定位相关对象,方便后续IOS与ANDROID统一处理。
ANDROID平台
第一步同样是导入百度SDK相关的各种包和申请权限,具体如何导出与申请那些权限查看百度SDK文档https://lbsyun.baidu.com/index.php?title=android-locsdk/guide/create-project/eclipse
准备工作搞定后,AppActivity中添加定位代码,在onCreate中初始化百度地图服务
mLocationClient = new LocationClient(getApplicationContext());// 声明LocationClient类 initLocation(); mLocationClient.registerLocationListener(myListener); mLocationClient.start();
/** * 初始化定位配置 */ private void initLocation() { LocationClientOption option = new LocationClientOption(); option.setLocationMode(LocationMode.Hight_Accuracy); option.setCoorType("bd09ll"); option.setScanSpan(0); // 可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的 option.setIsNeedAddress(true); // 可选,设置是否需要地址信息,默认不需要 option.setOpenGps(true); // 可选,默认false,设置是否使用gps option.setLocationNotify(true); // 可选,默认false,设置是否当GPS有效时按照1S/1次频率输出GPS结果 option.setIsNeedLocationDescribe(true); // 可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近” option.setIsNeedLocationPoiList(true); // 可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到 option.setIgnoreKillProcess(false); // 可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死 option.SetIgnoreCacheException(false); // 可选,默认false,设置是否收集CRASH信息,默认收集 option.setEnableSimulateGps(false); // 可选,默认false,设置是否需要过滤GPS仿真结果,默认需要 mLocationClient.setLocOption(option); }
/** * 定位 */ public BDLocationListener myListener = new BDLocationListener() { @Override public void onReceiveLocation(final BDLocation location) { boolean isOk = false; // 获取定位结果 StringBuffer sb = new StringBuffer(256); sb.append("time : "); sb.append(location.getTime()); // 获取定位时间 sb.append("\nerror code : "); sb.append(location.getLocType()); // 获取类型类型 sb.append("\nlatitude : "); sb.append(location.getLatitude()); // 获取纬度信息 sb.append("\nlontitude : "); sb.append(location.getLongitude()); // 获取经度信息 sb.append("\nradius : "); sb.append(location.getRadius()); // 获取定位精准度 if (location.getLocType() == BDLocation.TypeGpsLocation) {// GPS定位结果 isOk = true; sb.append("gps定位成功"); address = location.getAddrStr(); // 地址 } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) {// 网络定位结果 isOk = true; sb.append("网络定位成功"); address = location.getAddrStr(); // 地址 } else if (location.getLocType() == BDLocation.TypeOffLineLocation) { address = "离线定位失败"; // 地址 } else if (location.getLocType() == BDLocation.TypeServerError) { address = "服务端网络定位失败"; // 地址 } else if (location.getLocType() == BDLocation.TypeNetWorkException) { address = "网络不同导致定位失败,请检查网络是否通畅"; } else if (location.getLocType() == BDLocation.TypeCriteriaException) { address = "无法获取有效定位依据导致定位失败"; } sb.append(address); Log.i("BaiduLocationApiDem", sb.toString()); mLocationClient.stop(); if(isOk){ JniHelper.onAddressInfo(location.getLatitude(), location.getLongitude(), address); } } @Override public void onConnectHotSpotMessage(String arg0, int arg1) { } };
到这里已经拿到了定位信息了,通过JniHelper.onAddressInfo方法将定位结果通过JNI方式传递到Cocos2d-X的C++代码层,方式也很简单JAVA层定义个Native方法
public static native void onAddressInfo(double latitude, double longitude, String address);
C++层定一个对应的JNI函数
#if ((CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)) #include <jni.h> #include "platform/android/jni/JniHelper.h" #include <android/log.h> extern "C"{ void Java_net_yw365_jni_JniHelper_onAddressInfo(JNIEnv *env,jdouble latitude, jdouble longitude, jstring address) { GetJni()->m_Latitude = latitude; GetJni()->m_Longitude =longitude; GetJni()->m_Address = cocos2d::StringUtils::getStringUTFCharsJNI(env,address); } }; #endif
这样就将IOS与ANDROID平台的定位结果全部放到 m_Latitude,m_Longitude,m_Address三个变量中,后续业务逻辑与平台无关了。