1. 介绍
简介
定位服务(Location
Kit)采用GNSS、Wi-Fi、基站等多途径的混合定位模式进行定位,赋予您的应用快速、精准地获取用户位置信息的能力,构建全球定位服务能力,助力您发展全球业务。您将通过本Codelab学习如何调用Location
Kit定位服务能力创建一款Android 应用程序(Codelab App)。
此应用程序的基本架构如图所示:Codelab
App将集成Location SDK,然后通过Location SDK去调用HMS Core
(APK)的定位服务能力。
您将建立什么
在这个Codelab中,您将创建一个获取设备位置信息的App。
您将会学到什么
- 如何配置开发环境。
- 如何调用Location Kit的定位服务能力。
2. 您需要什么
硬件要求
- 计算机(台式机或者笔记本电脑),用于安装Android Studio开发环境。
- EMUI 5.0及以上的华为手机、华为平板或Android 5.1及以上的非华为手机一部,用于运行开发调试版本的定位服务App。
软件要求
- Android Studio(推荐使用3.6.1及以上版本)
- JDK(推荐使用1.7+版本及以上)
- Android API 22及以上
- Gradle 5.4.1及以上
3. 能力接入准备
集成HUAWEI HMS Core能力,需要完成以下准备工作:
- 创建AppGallery Connect项目及应用
- 创建Android Studio工程
- 生成签名证书
- 生成签名证书指纹
- AppGallery Connect上添加指纹证书
- AppGallery Connect上开通API服务
- 添加配置
- 配置项目签名
- 同步工程
4. 配置开发环境
配置SDK的Maven仓地址
Android Studio的代码库配置在Gradle插件7.0以下版本、7.0版本和7.1及以上版本有所不同。请根据您当前的Gradle插件版本,选择对应的配置过程。
| 7.0以下版本 | 7.0版本 | 7.1及以上版本 |
7.0以下版本
- 打开Android Studio项目级“build.gradle”文件。
- 添加HUAWEI agcp插件以及Maven代码库。
- 在“buildscript > repositories”中配置HMS Core SDK的Maven仓地址。
- 在“allprojects > repositories”中配置HMS Core SDK的Maven仓地址。
- 如果App中添加了“agconnect-services.json”文件则需要在“buildscript > dependencies”中增加agcp插件配置。
buildscript {
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven仓地址。
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
...
// 增加AGC插件配置,请您参见AGC插件依赖关系选择合适的AGC插件版本。
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
}
}
allprojects {
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven仓地址。
maven {url 'https://developer.huawei.com/repo/'}
}
}
7.0版本
- 打开Android Studio项目级“build.gradle”文件。
- 添加HUAWEI agcp插件以及Maven代码库。
- 在“buildscript > repositories”中配置HMS Core SDK的Maven仓地址。
- 如果App中添加了“agconnect-services.json”文件则需要在“buildscript > dependencies”中增加AGC插件和Android Gradle插件配置。
buildscript {
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven仓地址。
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
...
// 增加Android Gradle插件版本号配置,{version}为实际的Gradle插件版本号,例如7.0.1。
classpath 'com.android.tools.build:gradle:{version}'
// 增加AGC插件配置,请您参见AGC插件依赖关系选择合适的AGC插件版本。
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
}
}
dependencyResolutionManagement {
...
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven仓地址。
maven {url 'https://developer.huawei.com/repo/'}
}
}
7.1及以上版本
- 打开Android Studio项目级“build.gradle”文件。
- 如果App中添加了“agconnect-services.json”文件则需要在“buildscript > dependencies”中增加AGC插件和Android Gradle插件配置。
buildscript {
dependencies {
...
// 增加Android Gradle插件版本号配置,{version}为实际的Gradle插件版本号,例如7.1.1。
classpath 'com.android.tools.build:gradle:{version}'
// 增加AGC插件配置,请您参见AGC插件依赖关系选择合适的AGC插件版本。
classpath 'com.huawei.agconnect:agcp:1.6.0.300'
}
}
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
// 配置HMS Core SDK的Maven仓地址。
maven { url 'https://developer.huawei.com/repo/' }
}
}
dependencyResolutionManagement {
...
repositories {
google()
mavenCentral()
// 配置HMS Core SDK的Maven仓地址。
maven { url 'https://developer.huawei.com/repo/' }
}
}
添加编译依赖
- 打开应用级“build.gradle”文件。
- 在相应的app模块中添加SDK依赖。
dependencies {
implementation 'com.huawei.hms:location:6.4.0.300'
}
5. 配置混淆脚本
您编译APK前需要配置混淆配置文件,避免混淆HMS Core SDK导致功能异常。
- 打开Android工程的混淆配置文件"proguard-rules.pro"。
-
添加用于排除HMS Core SDK的混淆配置。
-ignorewarnings -keepattributes *Annotation* -keepattributes Exceptions -keepattributes InnerClasses -keepattributes Signature -keepattributes SourceFile,LineNumberTable -keep class com.huawei.hianalytics.**{*;} -keep class com.huawei.updatesdk.**{*;} -keep class com.huawei.hms.**{*;} -keep class * extends com.huawei.hms.core.aidl.IMessageEntity{ *; } -keep public class com.huawei.location.nlp.network.** {*; } -keep class com.huawei.wisesecurity.ucs.**{*;} -
如果您使用了AndResGuard,需要在混淆配置文件中加入AndResGuard允许清单。
"R.string.hms*", "R.string.connect_server_fail_prompt_toast", "R.string.getting_message_fail_prompt_toast", "R.string.no_available_network_prompt_toast", "R.string.third_app_*", "R.string.upsdk_*", "R.layout.hms*", "R.layout.upsdk_*", "R.drawable.upsdk*", "R.color.upsdk*", "R.dimen.upsdk*", "R.style.upsdk*" -
(可选)当您集成Location
6.0.0.xxx版本时,此系列版本使用了so加固,加固后的so不能压缩优化,为了保证您的App可以正常运行,需要您在应用级的“build.gradle”文件中添加如下声明:
android { buildTypes { release { packagingOptions { // 如果CPU架构为ARMv7、ARMv8时,需增加如下配置。 doNotStrip "*/arm64-v8a/libucs-credential.so" doNotStrip "*/armeabi-v7a/libucs-credential.so" // 如果CPU架构为x86时,需增加如下配置。 doNotStrip "*/x86/libucs-credential.so" doNotStrip "*/x86_64/libucs-credential.so" } } debug { packagingOptions { // 如果CPU架构为ARMv7、ARMv8时,需增加如下配置。 doNotStrip "*/arm64-v8a/libucs-credential.so" doNotStrip "*/armeabi-v7a/libucs-credential.so" // 如果CPU架构为x86时,需增加如下配置。 doNotStrip "*/x86/libucs-credential.so" doNotStrip "*/x86_64/libucs-credential.so" } } } }
6. 定位服务开发
Demo开发过程中涉及到的文件均取自示例中源码,建议您先下载示例代码。
-
申请定位权限。
-
在工程的“AndroidManifest.xml”文件中添加精准位置权限和粗略位置权限。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> -
在Android
10版本中,如果您需要应用程序在后台执行时也具备持续定位能力,需要在“AndroidManifest.xml”文件中添加ACCESS_BACKGROUND_LOCATION权限。
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> -
动态申请相关权限
Java示例代码如下:
Kotlin示例代码如下:// check location permission if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { Log.i(TAG, "sdk < 28 Q"); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { String[] strings = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}; ActivityCompat.requestPermissions(this, strings, 1); } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { String[] strings = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}; ActivityCompat.requestPermissions(this, strings, 3); }else{ if (ActivityCompat.checkSelfPermission(this, "android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) { String[] permission = {"android.permission.ACCESS_BACKGROUND_LOCATION"}; ActivityCompat.requestPermissions(this, permission, 0); } } } else { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, "android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED){ String[] strings = {android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION, "android.permission.ACCESS_BACKGROUND_LOCATION"}; ActivityCompat.requestPermissions(this, strings, 2); } }// check location permission if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { Log.i(TAG, "sdk < 28 Q") if (checkSelfPermission( this, ACCESS_FINE_LOCATION ) != PERMISSION_GRANTED && checkSelfPermission( this, ACCESS_COARSE_LOCATION ) != PERMISSION_GRANTED ) { val strings = arrayOf( ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION ) requestPermissions(this, strings, 1) } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (checkSelfPermission( this, ACCESS_FINE_LOCATION ) != PERMISSION_GRANTED && checkSelfPermission( this, ACCESS_COARSE_LOCATION ) != PERMISSION_GRANTED ) { val strings = arrayOf(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION) ActivityCompat.requestPermissions(this, strings, 3) } else { if (checkSelfPermission( this, "android.permission.ACCESS_BACKGROUND_LOCATION" ) != PERMISSION_GRANTED ) { val permission = arrayOf("android.permission.ACCESS_BACKGROUND_LOCATION") ActivityCompat.requestPermissions(this, permission, 0) } } } else { if (checkSelfPermission(this@RequestLocationUpdatesWithCallbackActivity, ACCESS_FINE_LOCATION) != PERMISSION_GRANTED && checkSelfPermission( this@RequestLocationUpdatesWithCallbackActivity, ACCESS_COARSE_LOCATION ) != PERMISSION_GRANTED && checkSelfPermission( this@RequestLocationUpdatesWithCallbackActivity, "android.permission.ACCESS_BACKGROUND_LOCATION" ) != PERMISSION_GRANTED ) { val strings = arrayOf( ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, "android.permission.ACCESS_BACKGROUND_LOCATION" ) requestPermissions(this, strings, 2) } } -
权限申请结果回调。
Java示例代码如下:
Kotlin示例代码如下:@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1) { if (grantResults.length > 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSION successful"); } else { Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed"); } } if (requestCode == 2) { if (grantResults.length > 2 && grantResults[2] == PackageManager.PERMISSION_GRANTED && grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION successful"); } else { Log.i(TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION failed"); } } if (requestCode == 3) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED || grantResults[1] == PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.checkSelfPermission(this, "android.permission.ACCESS_BACKGROUND_LOCATION") != PackageManager.PERMISSION_GRANTED) { String[] permission = {"android.permission.ACCESS_BACKGROUND_LOCATION"}; ActivityCompat.requestPermissions(this, permission, 0); } } else { Log.i(TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed"); } } }override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == 1) { if (grantResults.size > 1 && grantResults[0] == PERMISSION_GRANTED && grantResults[1] == PERMISSION_GRANTED ) { Log.i( TAG, "onRequestPermissionsResult: apply LOCATION PERMISSION successful" ) } else { Log.i( TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed" ) } } if (requestCode == 2) { if (grantResults.size > 2 && grantResults[2] == PERMISSION_GRANTED && grantResults[0] == PERMISSION_GRANTED && grantResults[1] == PERMISSION_GRANTED ) { Log.i( TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION successful" ) } else { Log.i( TAG, "onRequestPermissionsResult: apply ACCESS_BACKGROUND_LOCATION failed" ) } } if (requestCode == 3) { if (grantResults.isNotEmpty() && grantResults[0] == PERMISSION_GRANTED || grantResults[1] == PERMISSION_GRANTED ) { if (checkSelfPermission( this, "android.permission.ACCESS_BACKGROUND_LOCATION" ) != PERMISSION_GRANTED ) { val permission = arrayOf("android.permission.ACCESS_BACKGROUND_LOCATION") ActivityCompat.requestPermissions(this, permission, 0) } } else { Log.i( TAG, "onRequestPermissionsResult: apply LOCATION PERMISSSION failed" ) } } } 后续步骤均可参考“RequestLocationUpdatesWithCallbackActivity.java”或“RequestLocationUpdatesWithCallbackActivity.kt”文件。
-
在工程的“AndroidManifest.xml”文件中添加精准位置权限和粗略位置权限。
-
创建位置信息提供者客户端和设备设置客户端。
Java示例代码如下:
Kotlin示例代码如下:// 定位交互接入对象 private FusedLocationProviderClient fusedLocationProviderClient; //位置相关设置检查对象 private SettingsClient settingsClient; fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this); settingsClient = LocationServices.getSettingsClient(this);// 定位交互接入对象 private lateinit var fusedLocationProviderClient: FusedLocationProviderClient //位置相关设置检查对象 private lateinit var settingsClient: SettingsClient fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this) settingsClient = LocationServices.getSettingsClient(this) -
创建位置信息请求体。
Java示例代码如下:
Kotlin示例代码如下:LocationRequest mLocationRequest; mLocationRequest = new LocationRequest(); // 设置位置更新的间隔(单位:毫秒) mLocationRequest.setInterval(10000); // 设置定位类型 mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);var mLocationRequest: LocationRequest? = null // 设置位置更新的间隔(单位:毫秒) // 设置定位类型 mLocationRequest = LocationRequest().apply { interval = 1000 needAddress = true priority = LocationRequest.PRIORITY_HIGH_ACCURACY } -
创建结果回调类。
Java示例代码如下:
Kotlin示例代码如下:LocationCallback mLocationCallback; mLocationCallback = new LocationCallback() { @Override public void onLocationResult(LocationResult locationResult) { if (locationResult != null) { List<Location> locations = locationResult.getLocations(); if (!locations.isEmpty()) { for (Location location : locations) { Log.i(TAG,"onLocationResult location[Longitude,Latitude,Accuracy]:" + location.getLongitude() + "," + location.getLatitude() + "," + location.getAccuracy()); } } } } @Override public void onLocationAvailability(LocationAvailability locationAvailability) { if (locationAvailability != null) { boolean flag = locationAvailability.isLocationAvailable(); Log.i(TAG, "onLocationAvailability isLocationAvailable:" + flag); } } };var mLocationCallback: LocationCallback? = null if (null == mLocationCallback) { mLocationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult?) { if (locationResult != null) { val locations: List<Location> = locationResult.locations if (locations.isNotEmpty()) { for (location in locations) { Log.i( TAG, "onLocationResult location[Longitude,Latitude,Accuracy]:${location.longitude} , ${location.latitude} , ${location.accuracy}" ) } } } } override fun onLocationAvailability(locationAvailability: LocationAvailability?) { locationAvailability?.let { val flag: Boolean = locationAvailability.isLocationAvailable Log.i(TAG, "onLocationAvailability isLocationAvailable:$flag") } } } } -
请求位置更新。
Java示例代码如下:
Kotlin示例代码如下:private void requestLocationUpdatesWithCallback() { try { LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); builder.addLocationRequest(mLocationRequest); LocationSettingsRequest locationSettingsRequest = builder.build(); // 请求位置更新前检查设备设置。 settingsClient.checkLocationSettings(locationSettingsRequest) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(LocationSettingsResponse locationSettingsResponse) { Log.i(TAG, "check location settings success"); // 请求位置更新 fusedLocationProviderClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.getMainLooper()).addOnSuccessListener(new OnSuccessListener () { @Override public void onSuccess(Void aVoid) { Log.i(TAG, "requestLocationUpdatesWithCallback onSuccess"); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Log.e(TAG, "requestLocationUpdatesWithCallback onFailure:" + e.getMessage()); } }); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Log.e(TAG, "checkLocationSetting onFailure:" + e.getMessage()); } }); } catch (Exception e) { Log.e(TAG, "requestLocationUpdatesWithCallback exception:" + e.getMessage()); } } private fun requestLocationUpdatesWithCallback() { try { val builder = LocationSettingsRequest.Builder() builder.addLocationRequest(mLocationRequest) val locationSettingsRequest = builder.build() // 请求位置更新前检查设备设置。 val locationSettingsResponseTask: Task= settingsClient.checkLocationSettings(locationSettingsRequest) locationSettingsResponseTask.addOnSuccessListener { locationSettingsResponse: LocationSettingsResponse? -> Log.i(TAG, "check location settings success {$locationSettingsResponse}") // 请求位置更新 fusedLocationProviderClient.requestLocationUpdates( mLocationRequest, mLocationCallback, Looper.getMainLooper() ) .addOnSuccessListener { Log.i(TAG, "requestLocationUpdatesWithCallback onSuccess") } .addOnFailureListener { e -> Log.e( TAG, "requestLocationUpdatesWithCallback onFailure:${e.message}" ) } } .addOnFailureListener { e: Exception -> Log.e(TAG, "checkLocationSetting onFailure:${e.message}") } } catch (e: Exception) { Log.e(TAG, "requestLocationUpdatesWithCallback exception:${e.message}") } } -
移除位置信息更新请求。
Java示例代码如下:
Kotlin示例代码如下:private void removeLocationUpdatesWithCallback() { try { fusedLocationProviderClient.removeLocationUpdates(mLocationCallback) .addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { Log.i(TAG, "removeLocationUpdatesWithCallback onSuccess"); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(Exception e) { Log.e(TAG, "removeLocationUpdatesWithCallback onFailure:" + e.getMessage()); } }); } catch (Exception e) { Log.e(TAG, "removeLocationUpdatesWithCallback exception:" + e.getMessage()); } }private fun removeLocationUpdatesWithCallback() { try { fusedLocationProviderClient.removeLocationUpdates(mLocationCallback) .addOnSuccessListener { Log.i( TAG, "removeLocationUpdatesWithCallback onSuccess" ) } .addOnFailureListener { e -> Log.e( TAG, "removeLocationUpdatesWithCallback onFailure:${e.message}" ) } } catch (e: Exception) { Log.e( TAG, "removeLocationUpdatesWithCallback exception:${e.message}" ) } }
7. 成果展示
您可以点击请求位置更新按钮,并将位置信息打印到当前界面。
剩余