最小实现
本文给出仅包含 主 SDK、NavigationService、MapView 三者的最小集成示例,全部在 一个 Activity 中完成,便于快速验证工程是否可用。更完整的选项说明见 初始化。
1. 模块与职责
| 模块 |
所在类 |
初始化 |
反初始化 |
| 主 SDK |
MapActivity |
SDK.getInstance().initialize(...),后台子线程,Region = EU |
SDK.getInstance().dispose(true) |
| NavigationService |
MapActivity |
NavigationService.Factory.createInstance(),主 SDK 成功后;主线程或后台线程均可 |
navigationService.dispose() |
| MapView |
MapActivity |
mapView.initialize(config),主 SDK 初始化成功后(主线程) |
mapView.onDestroy() |
2. 权限说明(必读)
在调用 SDK.initialize 之前,必须先完成运行时权限申请。
主 SDK 需要读写缓存目录;Hybrid / Onboard 还需访问本地地图数据路径(常为外部存储)。若在未授权时初始化,可能导致缓存写入失败、地图数据无法读取或初始化异常。
集成时应:
- 在
Activity.onCreate 中先调用 requestPermissions(),一次性申请定位、网络、存储等权限(参考示例 MainActivity.checkUserPermission())。
- Android 11(API 30)及以上,若使用
sdcard 等外部目录,另引导用户开启「所有文件访问」。
- 仅在权限流程结束后再执行
SDK.initialize(建议后台线程)。
- 仅在主 SDK 初始化成功后再创建
NavigationService(主线程或后台线程均可)。
- 仅在主 SDK 初始化成功后再调用
MapView.initialize(主线程)。
| 权限就绪 → SDK.initialize → NavigationService.createInstance → MapView.initialize
|
3. 流程总览
3.1 初始化
| MapActivity.onCreate
├─ setContentView(布局含 TnMapView)
└─ requestPermissions() ← 必须先于 SDK
└─(已授权 / onRequestPermissionsResult)
└─ 子线程:SDK.initialize(EU)
└─(可选:同线程或主线程)NavigationService.createInstance()
└─ runOnUiThread → initMapView() ← SDK 成功后再初始化地图
└─ onResume / onPause 转发
|
3.2 反初始化
| MapActivity.onDestroy
├─ mapView.onDestroy()
└─ 子线程
├─ navigationService.dispose()(若已创建)
└─ SDK.getInstance().dispose(true)
|
4. 布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.telenav.map.views.TnMapView
android:id="@+id/mapView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
|
5. 初始化示例(MapActivity)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 | class MapActivity : AppCompatActivity() {
private val permissionRequestCode = 12335
private lateinit var binding: ActivityMapBinding
lateinit var navigationService: NavigationService
private set
@Volatile
private var isSdkReady = false
private var isMapViewInitialized = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMapBinding.inflate(layoutInflater)
setContentView(binding.root)
requestPermissions() // ① 先申请权限
}
// ---------- ① 权限(先于 SDK) ----------
private fun requestPermissions() {
val permissionsRequired = mutableListOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_NETWORK_STATE,
Manifest.permission.INTERNET,
)
permissionsRequired.add(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
)
permissionsRequired.forEach { permission ->
if (ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_DENIED) {
ActivityCompat.requestPermissions(this, permissionsRequired.toTypedArray(), permissionRequestCode)
return
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
})
}
initSdkAndNavigationServiceAsync()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == permissionRequestCode) {
initSdkAndNavigationServiceAsync() // ② 权限回调后再初始化 SDK
}
}
// ---------- ② 主 SDK(后台线程);NavigationService 可在本线程或主线程 ----------
private fun initSdkAndNavigationServiceAsync() {
if (isSdkReady) return
thread(name = "sdk-init") {
val cacheDir = File(cacheDir, "sdk_cache").apply { mkdirs() }
val sdkOptions = SDKOptions.builder()
.setApiKey("<YOUR_EU_API_KEY>")
.setApiSecret("<YOUR_EU_API_SECRET>")
.setCloudEndPoint("<YOUR_EU_CLOUD_ENDPOINT>")
.setSdkCacheDataDir(cacheDir.absolutePath)
.setLocale(Locale.GERMAN)
.setRegion(Region.EU.name)
.setUserId("<YOUR_USER_ID>")
.setDeviceGuid("<YOUR_DEVICE_GUID>")
// .setSdkDataDir("<YOUR_EU_MAP_DATA_DIR>") // Hybrid / Onboard
.build()
val navSDKOptions = NavSDKOptions.builder(
sdkOptions,
VehicleInfoConfig.Builder().build()
)
.setTrafficRefreshTime(120)
.setTrafficExpireTime(120)
.build()
if (SDK.getInstance().initialize(this@MapActivity, navSDKOptions) != 0) return@thread
isSdkReady = true
runOnUiThread {
navigationService = NavigationService.Factory.createInstance() // 主线程创建亦可
initMapView() // ③ SDK 成功后再初始化 MapView
}
}
}
// ---------- ③ MapView(主 SDK 成功后,主线程) ----------
private fun initMapView() {
if (isMapViewInitialized) return
binding.mapView.initialize(
MapViewInitConfig(
context = applicationContext,
lifecycleOwner = this,
readyListener = MapViewReadyListener { /* 地图就绪后再操作相机、车标 */ }
)
)
isMapViewInitialized = true
}
override fun onResume() {
super.onResume()
if (isMapViewInitialized) binding.mapView.onResume()
}
override fun onPause() {
if (isMapViewInitialized) binding.mapView.onPause()
super.onPause()
}
}
|
6. 反初始化示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | override fun onDestroy() {
if (isMapViewInitialized) {
binding.mapView.onDestroy()
}
thread(name = "sdk-dispose") {
if (::navigationService.isInitialized) {
navigationService.stopNavigation()
navigationService.dispose()
}
SDK.getInstance().dispose(true)
isSdkReady = false
}
super.onDestroy()
}
|
| 模块 |
时机 |
API |
| MapView |
onDestroy |
mapView.onDestroy() |
| NavigationService |
onDestroy(子线程,若已创建) |
navigationService.dispose() |
| 主 SDK |
onDestroy(子线程) |
SDK.getInstance().dispose(true) |
mapView.onPause() / onResume() 须与 Activity 生命周期成对调用。