Skip to content

接入谷歌搜索

本文说明如何在应用中接入 search-service(统一搜索 AAR),在 Entity Service 基础上使用 Google 搜索的能力。

  1. Google 搜索建立在 Entity Service 之上并尽量保持接口的一致性。引入Google 搜索后,在Google能力范围内走Google 搜索,一旦不支持或出现网络问题,会fallback到泰为的搜索。

  2. 文档中 TN only 表示此功能Google不支持,会使用泰为搜索来完成相关功能。

  3. 使用Google 搜索需要遵循Google UI规范,具体要求请联系泰为设计团队。

  4. 以下国家不支持Google搜索,会自动fallback到泰为搜索 (中国,越南,叙利亚,朝鲜,伊朗,古巴,俄罗斯,乌克兰)。

Telenav Entity Service 通用搜索能力(云端 / Hybrid、EntityClient 等)见同目录下的 搜索概述开始。本文侧重谷歌搜索的可用性与泰为搜索的依赖关系。

说明

SearchService.initialize() 与各 execute()同步阻塞调用,禁止在主线程执行,否则可能 ANR;详见下文 §2.4 线程模型


1. 概览

search-service 负责初始化、获取 SearchClient、Google 可用性及释放资源;在支持的区域与场景下由服务在 谷歌与泰为 之间自动选择或 fallback,并返回统一的 SearchResponse(用 providergoogle / tn来表示搜索结果来源)。

核心能力:

能力 说明
统一入口 SearchService 是进程内单例,负责初始化、获取搜索接口、释放资源
增强 Entity Service 统一提供文本搜索、分类搜索、地图范围搜索、详情、RGC、自动补全等能力
Google 搜索增强 在支持的区域和场景下使用 Google 搜索能力补充地点搜索、分类搜索、自动补全和详情结果
智能结果源 服务自动选择最合适的结果源,集成方通常只需要消费统一结果
Google 搜索可用性控制 根据位置、网络、配置和服务状态判断 Google 搜索能力是否可用;listener 与 getGoogleSearchAvailabilityState() 提供 reason / detail 诊断
统一响应 所有公开搜索接口固定返回 SearchResponse;列表/详情类业务数据位于 SearchBody.EntitySearch.value(类型为 EntitySearchResponse
Entity SDK 请求模型 与 Entity Service 相同:searchRequest()suggestionPredictionRequest()getDetailRequest() 等 builder;GeoPointSearchFiltersCategoryFilter

SearchService.initialize(context, SearchServiceInitOptions) 的完整流程如下:先初始化 TN Entity Service;再根据 googleSearchEnabled 决定是否拉取 Google 配置并加载 WebView / Google 库。Google 侧访问为异步,initialize() 返回 true 后即可 getClient() 发起搜索;位置就绪后调用 refreshGoogleAvailability(lat, lon) 更新 Google 可用性。

Search 初始化时序

阶段 说明
发起初始化 应用层调用 SearchService.initialize(context, SearchServiceInitOptions)(同步阻塞,须在后台线程)
TN 初始化 初始化 TN Entity Service,完成后回调
Google 开关 googleSearchEnabled = false 时不启用 Google 路径;为 true 时拉取 API Key、UI Kit URI、区域等配置
异步加载 初始化 WebView 并加载 Google 库,异步访问 Google 服务;不阻塞后续 getClient()
可用性监听 可选 setGoogleSearchAvailabilityListener(...);配置/WebView 完成后在主线程回调 onGoogleSearchAvailabilityChanged(...)
返回值 true 表示 TN 与增强结果源均成功;false 表示失败,无法继续搜索。拿到位置后调用 refreshGoogleAvailability(lat, lon) 动态更新 Google 可用性

searchRequest().build().execute() 的完整流程如下:先分析请求类型并判断 Google 是否可用;不可用则仅走 TN;可用则并行发起 Google 与 TN 搜索,优先采用 Google 结果,失败或超时后 fallback 到 TN。

Search 请求流程

阶段 说明
发起请求 HybridEntitySearchRequest.execute()HybridSearchClient.executeSearch()
请求分析 SearchRequestAnalyzer.analyze() 判断 Operation 类型及是否支持 Google 路径
可用性判断 shouldUseGoogle():不支持或不可用时直接走 TN
并行执行 hybridGoogleFirst() 协程同时发起 Google 与 TN 两路请求
结果选择 Google 在 timeout 内成功则取消 TN 并返回 Google;否则 fallback 到 TN;双路均失败则返回错误

getDetailRequestsuggestionPredictionRequest 等接口的路由策略见 §3.7

使用规则:

规则 说明
先初始化 未调用 initialize 时调用 getClient() 会抛异常
单例生命周期 进程内复用,Activity.onDestroy()dispose();见 §2.5
检查返回值 initialize() 返回 Booleanfalse 表示 TN 或增强结果源初始化失败,无法继续搜索,应提示用户并排查配置(见 §3.1
同步阻塞 initialize()execute() 均为同步阻塞调用,不得在主线程调用;见 §2.4 线程模型
响应 SearchResponse.body 为类型化 SearchBody;列表/详情等API使用 EntitySearchResponse,Autocomplete 使用 EntitySuggestionPredictionResponse,其他专项接口使用对应 payload

集成前提与依赖清单见 §2.1 依赖清单与环境


2. 快速上手

目标:复制以下代码即可跑通「初始化 → 搜索 → 释放」最小链路。完整参数说明见 §3.1

2.1 依赖清单与环境

集成前请按下列清单逐项准备。search-service 对外提供统一搜索能力(Entity Service + Google Service),下表所列均为本服务的集成依赖,而非可选附加项。

构建与依赖

说明 责任方
主 AAR Maven 坐标:com.telenav.search:search-service,须在 Maven 中配置 Telenav 制品仓库地址,引用AAR包
Android 环境 与 Entity SDK 要求一致的 minSdk / 权限等(按宿主 App 现有集成)

Maven 仓库与依赖配置见 工程搭建 — 配置 SDK 仓库与依赖

申请的资源

以下凭据与项目标识须联系 Telenav 团队获取,集成方无法自行生成:

资源 用途 填入位置
apiKey / apiSecret 云端搜索鉴权 SDKOptions.setApiKey / setApiSecret
endpoint 在线搜索服务地址 SDKOptions.setCloudEndPoint
projectKey 云端项目地址 ApplicationInfo.applicationName
region 部署区域(如 EUNA SDKOptions.setRegion

开通服务

以下项须在 联系 Telenav 完成开通与绑定 后,对应能力方可正常使用;集成方仅使用已下发的凭据初始化客户端:

配置项 说明 未开通时的表现
云端网关与项目资源配置 Telenav 云端为该项目绑定搜索网关、谷歌增强结果源等资源配置 增强结果源初始化或拉取配置失败,服务 fallback 至 TN Entity Service
区域与 endpoint 匹配 下发的 endpointregion 须与目标部署区域一致 鉴权失败或结果异常

集成前动作:联系 Telenav 团队完成项目创建、server 侧开通,并获取上表中的鉴权信息与 projectKey

运行环境要求

要求
网络 设备须能访问 Telenav 云端 endpoint,且网络环境须能访问 Google 服务(增强结果依赖Google 侧资源);系统网络变化时调用 setNetworkMode 同步给搜索服务
区域一致性 regionendpoint、初始化位置(setCurrentLocation)应保持一致
位置与可用性 国家/区域切换后须调用 refreshGoogleAvailability(lat, lon) 更新增强结果源可用性(受国家 ban 列表约束)
Android System WebView 须安装 Android System WebView,主版本号 ≥ 90;版本过低可能导致增强结果源初始化失败或结果异常

2.2 最小调用链

initialize()execute() 会阻塞当前线程,须在后台线程执行(推荐协程 Dispatchers.IO,见 §2.4)。

 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
// Activity / Fragment 示例;需依赖 kotlinx-coroutines-android
lifecycleScope.launch {
    // 1. 初始化(IO 线程;必须检查返回值)
    val initOk = withContext(Dispatchers.IO) {
        val sdkOptions = SDKOptions.builder()
            .setApiKey(apiKey)
            .setApiSecret(apiSecret)
            .setCloudEndPoint(endpoint)
            .setRegion("EU")
            .setCurrentLocation(lat, lon)
            .setUserId(userId)
            .setDeviceGuid(deviceGuid)
            .setApplicationInfo(ApplicationInfo.builder(projectKey, appVersion).build())
            .setSdkDataDir(filesDir.absolutePath)
            .setSdkCacheDataDir(cacheDir.absolutePath)
            .build()

        SearchService.initialize(
            context,
            SearchServiceInitOptions(
                sdkOptions = sdkOptions,
                searchSettings = SearchSettings(
                    // 以下四字段必填;allowOffer / onlyOnBoardSearch 可省略(§3.1)
                    timeout = 5000,
                    googleSearchEnabled = true,
                    userIsExpired = false,
                    rgcIsOnboard = false
                ),
                // enableNavPoint = true   // 可选,默认 false;求路场景见 §3.1
            )
        )
    }
    if (!initOk) {
        showInitError("SearchService.initialize failed")   // 已在 Main,可更新 UI
        return@launch
    }

    // 2. 网络与可用性(轻量调用,可在 Main)
    SearchService.getClient().setNetworkMode(
        if (isNetworkConnected) NetworkMode.CONNECTED else NetworkMode.DISCONNECTED
    )
    SearchService.setGoogleSearchAvailabilityListener { available, state ->
        updateUi(available, state)   // 回调在 Main 线程;state.reason 可展示不可用原因
    }
    SearchService.refreshGoogleAvailability(lat, lon)

    // 3. 搜索(IO 线程)
    val response = withContext(Dispatchers.IO) {
        SearchService.getClient().searchRequest()
            .setQuery("coffee")
            .setLocation(GeoPoint(lat, lon))
            .setLimit(20)
            .build()
            .execute()
    }

    // 4. 读取结果并更新 UI(回到 Main;handleSearchResponse 见 §2.3)
    handleSearchResponse(response)
}
// 不在此 dispose — Activity 销毁时勿调用;见 §2.5

结果读取范式见 §2.3;线程说明见 §2.4dispose 时机见 §2.5;响应 body 字段见 返回对象;响应码见 §5.1

2.3 读取结果

execute() 返回的 SearchResponse 已包含服务内部 Google → TN fallback 的最终结果:集成方无需自行重试 TN,只需读 response.coderesponse.provider 并按 operation 选择正确的 payload 解析方法。

读取顺序

步骤 检查项 说明
1 response.code SearchResponseCodes.isSuccess(response)true12200 / 12206)才视为成功;12204NO_CONTENT)表示无匹配结果
2 response.operation 决定使用 entitySearchOrNull() 还是 autocompleteOrNull()
3 payload 非 null entitySearchOrNull() / autocompleteOrNull()body 类型不匹配时返回 null(如对 Autocomplete 响应误调 entitySearchOrNull()
4 results 是否为空 成功时 results 仍可能为空列表;须单独判断 isNotEmpty()
5 response.provider "google""tn",标识最终采用的来源;fallback 后通常为 "tn"

列表 / 详情 / RGC(entitySearchOrNull

 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
sealed class SearchUiState {
    data class Results(val places: List<Entity>, val provider: String) : SearchUiState()
    object Empty : SearchUiState()
    data class Error(val code: Int, val message: String?) : SearchUiState()
}

fun handleSearchResponse(response: SearchResponse): SearchUiState {
    // 1. 按 code 分支
    when (response.code) {
        SearchResponseCodes.SUCCESS,
        SearchResponseCodes.PARTIAL_SUCCESS -> {
            // 2. 按 operation 取正确 payload(类型不匹配时为 null)
            val entitySearch = response.entitySearchOrNull()
                ?: return SearchUiState.Error(
                    response.code,
                    "Unexpected body for operation=${response.operation}"
                )

            // 3. 成功但无结果
            val results = entitySearch.results.orEmpty()
            if (results.isEmpty()) {
                return SearchUiState.Empty
            }

            // 4. 正常消费;provider 标识最终来源(含 Google 超时后 fallback 到 TN 的情况)
            return SearchUiState.Results(
                places = results,
                provider = response.provider   // "google" | "tn"
            )
        }
        SearchResponseCodes.NO_CONTENT -> {
            return SearchUiState.Empty
        }
        else -> {
            return SearchUiState.Error(response.code, response.message)
        }
    }
}

// 使用示例
when (val state = handleSearchResponse(response)) {
    is SearchUiState.Results -> {
        // state.places: List<Entity>
        // state.provider == "google" 时可展示 Google 来源标识
        showList(state.places)
    }
    SearchUiState.Empty -> showEmpty()
    is SearchUiState.Error -> showError(state.code, state.message)
}

Autocomplete(autocompleteOrNull

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fun handleAutocompleteResponse(response: SearchResponse) {
    if (!SearchResponseCodes.isSuccess(response)) {
        showError(response.code, response.message)
        return
    }
    val body = response.autocompleteOrNull()
        ?: return showError(response.code, "Unexpected body for operation=${response.operation}")
    val suggestions = body.results.orEmpty()
    // suggestions 为建议项列表(非 POI Entity);字段见 返回对象.md#推荐返回对象
    if (suggestions.isEmpty()) showEmpty() else showSuggestions(suggestions)
}

关于 Google → TN fallback

现象 含义 集成方处理
provider == "google" 增强结果源成功返回 正常展示;UI 可标注 Google 来源
provider == "tn"code 成功 可能为 TN 原生结果,或 Google 超时/失败后 服务内部已 fallback 正常展示 TN 结果,无需集成方再次发起 TN 请求
provider == "tn"code 失败 Google 与 TN 均未返回可用结果 展示 message,可查 §5.1
code == SUCCESSresults 为空 请求成功但无 POI 匹配 展示空态,与 NO_CONTENT 类似

execute() 为同步阻塞调用,须在 Dispatchers.IO 等后台线程执行后再回到 Main 更新 UI(见 §2.4)。

2.4 线程模型

API / 环节 线程 说明
SearchService.initialize() 后台Dispatchers.IO 初始化 Entity SDK、增强结果源;可能访问磁盘与网络,耗时可达数秒
SearchClient.*.execute() 后台Dispatchers.IO 等待云端 / WebView 返回;Hybrid 模式下可能阻塞至 SearchSettings.timeout
setNetworkMode / refreshGoogleAvailability Main 或后台均可 轻量状态同步
setGoogleSearchAvailabilityListener 回调 Main(SDK 保证) 参数为 (available, state),可直接更新 Google 标识与不可用原因文案
handleSearchResponse / 列表渲染 Main withContext 结束后已切回 lifecycleScope 的 Main

协程模板(推荐)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
lifecycleScope.launch {
    val response = withContext(Dispatchers.IO) {
        SearchService.getClient().searchRequest()
            .setQuery("coffee")
            .setLocation(GeoPoint(lat, lon))
            .setLimit(20)
            .build()
            .execute()
    }
    // 此处已回到 Main,可安全更新 UI
    handleSearchResponse(response)
}

线程池替代(无协程时)

1
2
3
4
5
6
7
8
9
executor.execute {
    val response = SearchService.getClient().searchRequest()
        .setQuery("coffee")
        .setLocation(GeoPoint(lat, lon))
        .setLimit(20)
        .build()
        .execute()
    runOnUiThread { handleSearchResponse(response) }
}

切勿在 Main 线程调用 initialize()execute(),否则会触发 ANR,尤其是增强结果源等待 WebView 响应时。

2.5 生命周期与 dispose

SearchService进程内单例initialize() 成功后实例存活至 dispose() 或进程结束。与 Activity 生命周期解耦——多个 Activity / Fragment 可共享同一已初始化的实例。

推荐调用时机

场景 是否 dispose() 说明
Application.onCreateinitialize(),各页面搜索 最常见:全进程复用,不要Activity.onDestroy() 中 dispose
Activity 旋转、跳转、返回 dispose 后其他页面 getClient() 可能抛异常或需重新初始化
用户登出 / 切换账号,需更换 apiKeyuserId dispose 后用新凭据重新 initialize()
切换搜索配置(如 TN-only 开关、regionendpoint 变更) 先 dispose,再 initialize()(见 §3.1
App 进程被系统回收 不必 进程结束时资源由系统回收;下次冷启动重新 initialize() 即可
集成方主动下线搜索模块(功能开关永久关闭) 释放 WebView 与监听,节省内存

反模式(避免)

1
2
3
4
5
// ❌ 不要在 Activity/Fragment 销毁时 dispose
override fun onDestroy() {
    SearchService.dispose()   // 旋转屏幕、压栈返回会导致其他页面搜索失败
    super.onDestroy()
}

推荐模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// ✅ Application 或依赖注入容器中初始化一次
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        // 可在 IO 线程 initialize;须检查返回值
    }
}

// ✅ 仅在「需要更换配置 / 登出」时 dispose 并可选地重新初始化
fun onUserLogout() {
    SearchService.setGoogleSearchAvailabilityListener(null)
  // 若注册了网络监听,此处一并注销
    SearchService.dispose()
}

fun onSearchConfigChanged(newOptions: SearchServiceInitOptions) {
    SearchService.dispose()
    val ok = SearchService.initialize(appContext, newOptions)
    // ...
}

dispose() 会释放 TN 客户端、增强结果源 WebView、Google 可用性监听等;调用后须重新 initialize() 才能搜索。详见 §3.3


3. API 参考

3.1 服务初始化(SearchService.initialize

说明:初始化统一搜索服务、搜索配置、可用性策略和网络状态;配置鉴权、区域、Google 开关等。通常一个进程只需要初始化一次。

参数列表:

参数 类型 说明
context android.content.Context Android 上下文,内部使用 applicationContext
options SearchServiceInitOptions 初始化参数,包含鉴权、环境、搜索设置等

SearchServiceInitOptions 构造函数签名(com.telenav.searchservice.api.SearchServiceInitOptions):

1
2
3
4
5
SearchServiceInitOptions(
    sdkOptions: SDKOptions,           // 必填
    searchSettings: SearchSettings,   // 必填
    enableNavPoint: Boolean = false   // 可选,Nav Point,见下方
)
字段 类型 是否必填 默认值 说明
sdkOptions SDKOptions Entity Service 鉴权、目录、位置、区域等基础配置
searchSettings SearchSettings 搜索行为配置(超时、Google 开关、RGC 策略等),见下方 SearchSettings
enableNavPoint Boolean false 是否启用 Google 导航点(Nav Point),见下方专节

enableNavPoint(Google 导航点)

集成方若将搜索结果用于算路 / 导航,建议了解此开关:

说明
作用 true 时,增强结果源 WebView 加载参数 enable_navpoint=true,返回地点坐标优先使用 Google 导航点(道路可达点),路线规划比默认扎点更精准
生效条件 客户端与服务端须同时为 trueSearchServiceInitOptions.enableNavPoint == true Telenav 云端 projects.json 中该项目的 enable_nav_point == true(见 §2.1 服务端开通项)
默认 false;一般列表展示可不传;导航类 App 在服务端已开通后再设 true
修改时机 写入 WebView 配置,仅在 initialize() / 网络恢复重绑时生效;变更后须 dispose() 再重新 initialize()
1
2
3
4
5
SearchServiceInitOptions(
    sdkOptions = sdkOptions,
    searchSettings = searchSettings,
    enableNavPoint = true   // 须服务端 enable_nav_point 也为 true
)

SDKOptions 常用配置:

配置 说明
setApiKey(...) / setApiSecret(...) Entity Service 鉴权信息
setCloudEndPoint(...) 在线搜索服务 endpoint
setRegion(...) Entity Service 区域
setCurrentLocation(lat, lon) 初始化时的当前位置
setUserId(...) / setDeviceGuid(...) 用户与设备标识
setApplicationInfo(...) **第一个参数须填APP Name,第二个参数为应用版本
setSdkDataDir(...) / setSdkCacheDataDir(...) SDK 数据目录与缓存目录
setCustomContext(...) 可选;高级场景下通过 customContext 传入 Google 搜索 URL 配置(见下方)

Google 搜索配置由 sdkOptions 在内部自动推导,集成方无需单独传入:

内部字段 来源
endpoint sdkOptions.cloudEndPoint
apiKey sdkOptions.apiKey
apiSecret sdkOptions.apiSecret
projectKey sdkOptions.applicationInfo.applicationName
region sdkOptions.region(未设置或为空时内部默认 "NA"
urlSource sdkOptions.customContext["google_ui_kit_url_source"],默认 auto
customUrl sdkOptions.customContext["google_ui_kit_server_url_custom"],默认 null

SDKOptions.setRegion(...) 支持/推荐值:

区域 说明
NA North America 默认值,适用于北美环境
EU Europe 适用于欧洲环境
ANZ Australia & New Zealand 适用于澳新环境
SEA Southeast Asia 适用于东南亚环境
MEA Middle East & Africa 适用于中东非洲环境
SA South America 适用于南美环境
ISC Indian Subcontinent 适用于印度次大陆环境
ISR Israel 适用于以色列环境
TUR Turkey 适用于土耳其环境

setRegion(...) 应与在线服务 endpoint、初始化位置(setCurrentLocation)保持一致。例如使用 EU endpoint 时,setRegion("EU"),初始化位置也应位于欧洲区域内。区域代码大小写均可识别,建议统一使用大写(如 NASEAISR)。变更区域时须 dispose() 后重新 initialize()

SearchSettings 为 Kotlin data class仅在 SearchService.initialize() 时传入一次,不随每次搜索传递。构造函数签名(com.telenav.searchservice.api.SearchSettings):

1
2
3
4
5
6
7
8
SearchSettings(
    allowOffer: Boolean? = null,      // 可选
    timeout: Int,                     // 必填
    googleSearchEnabled: Boolean,     // 必填
    userIsExpired: Boolean,           // 必填
    rgcIsOnboard: Boolean,           // 必填
    onlyOnBoardSearch: Boolean = false  // 可选
)

使用命名参数时,四个必填字段timeoutgoogleSearchEnableduserIsExpiredrgcIsOnboard)必须显式传入;allowOfferonlyOnBoardSearch 可省略(见下表默认值)。若用位置参数且省略 allowOffer,第一个实参即为 timeout

字段 类型 是否必填 默认值 说明
allowOffer Boolean? null(内部按 false 处理) 是否向 TN 云搜请求 Offer facet;多数第三方场景保持默认即可
timeout Int 等待Google结果源的时间(毫秒),超时后 fallback TN;同时作为 TN hybridSelectionTimeout须 > 0,常用 5000
googleSearchEnabled Boolean 是否允许走增强结果源搜索路径;falseisGoogleSearchAvailable()false、请求仅走 TN(WebView 仍会初始化,见 §4
userIsExpired Boolean 用户订阅是否过期;true 时关闭 TN 在线搜索(映射为 TelenavSearchEnabled=false)。第三方云集成通常传 false
rgcIsOnboard Boolean refreshGoogleAvailability / 内部 RGC 是否走 onboard 客户端;第三方云环境通常 false(走云端 RGC)
onlyOnBoardSearch Boolean false true 时仅初始化 onboard Entity,不初始化云 EntityClient;纯离线场景使用,默认 false

第三方云集成推荐值(与 §2.2 示例一致):

字段 推荐值
timeout 5000
googleSearchEnabled true(若不需要增强结果源则 false
userIsExpired false
rgcIsOnboard false
onlyOnBoardSearch false(省略即可)
allowOffer 省略(null

最小示例:

 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
val sdkOptions = SDKOptions.builder()
    .setApiKey(apiKey)
    .setApiSecret(apiSecret)
    .setCloudEndPoint(endpoint)
    .setRegion("EU")
    .setCurrentLocation(lat, lon)
    .setUserId(userId)
    .setDeviceGuid(deviceGuid)
    .setApplicationInfo(ApplicationInfo.builder(projectKey, appVersion).build())
    .setSdkDataDir(filesDir.absolutePath)
    .setSdkCacheDataDir(cacheDir.absolutePath)
    .build()

val options = SearchServiceInitOptions(
    sdkOptions = sdkOptions,
    searchSettings = SearchSettings(
        timeout = 5000,              // 必填
        googleSearchEnabled = true,     // 必填
        userIsExpired = false,        // 必填
        rgcIsOnboard = false          // 必填
    ),
    // enableNavPoint = true   // 可选,;导航/算路场景且服务端已开通时,true 表示使用google导航点,求路更准确
)

val initOk = SearchService.initialize(context, options)
if (!initOk) {
    // 初始化失败:提示用户,排查 §2.1 依赖清单与 SDKOptions 配置
    return
}
// initOk == true 后再调用 getClient()、refreshGoogleAvailability 等

返回值:

类型 说明
Boolean true = TN Entity Service 增强结果源均初始化成功;false = 任一侧失败

false 时常见原因与处理

原因 处理
apiKey / apiSecret / endpoint 错误或未开通 核对 Telenav 下发的凭据与 §2.1 服务端开通项
sdkDataDir / sdkCacheDataDir 无写权限 检查应用目录权限与路径
增强结果源环境不满足(WebView 版本、无法访问 Google 服务等) 检查 §2.1 客户端运行环境
重复初始化前未 dispose() SearchService.dispose(),修正配置后再次 initialize()

注意:即使返回 falsegetClient() 也可能不抛异常(内部实例已创建),但后续 execute() 极易失败。必须以返回值为据决定是否进入搜索流程。

3.2 获取搜索客户端(SearchService.getClient

说明:获取统一搜索入口 SearchClient。搜索、详情、Autocomplete、RGC、分类树、子类目/品牌/地点发现、高速出口等均通过 Entity SDK 同名 builder 入口调用;execute() 返回 SearchResponse(含 providergoogle / tn)。

参数列表:无。

返回值:

类型 说明
SearchClient EntityClient 对齐的 builder 客户端;未初始化时抛异常

典型用法:

 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
val client = SearchService.getClient()

val searchResponse = client.searchRequest()
    .setQuery("coffee")
    .setLocation(GeoPoint(lat, lon))
    .setLimit(20)
    .setSort(SortType.BEST_MATCH)
    .setSearchOptions(SearchOptions.builder().setShowAddressLines(true).build())
    .build()
    .execute()

val autocompleteResponse = client.suggestionPredictionRequest()
    .setQuery("star")
    .setLocation(GeoPoint(lat, lon))
    .build()
    .execute()

val detailResponse = client.getDetailRequest()
    .setEntityIds(listOf(placeId))
    .setDetailOptions(
        GetDetailOptions.builder()
            .setDetailLevel(GetDetailOptions.EntityDetailLevel.FULL)
            .setShowAddressLines(true)
            .build()
    )
    .setLocation(lat, lon)
    .build()
    .execute()

3.3 释放资源(SearchService.dispose

说明:释放搜索服务、Google 可用性监听、增强结果源 WebView 等资源。 Activity 生命周期回调的常规步骤;何时调用见 §2.5 生命周期与 dispose

释放范围:

资源 行为
HybridSearchClient / TN 后端 释放
GoogleSearchAvailabilityListener 取消注册,不再回调
增强结果源 WebView 释放

参数列表:无。

返回值:

类型 说明
Unit 无返回数据

调用后 getClient() 将抛异常,直至再次 initialize() 成功。

3.4 刷新 Google 可用性(SearchService.refreshGoogleAvailability

说明:刷新 Google 搜索能力是否可用;国家或区域切换后须重新调用,以确保 Google 可用性状态正确。请确保在初始化完成后再调用。

提供两种重载:

3.4.1 按经纬度(内部 RGC)

集成方已有 GPS 坐标、希望由 Search Service 通过 TN RGC 解析国家时使用。

参数 类型 说明
lat Double 当前纬度
lon Double 当前经度

国家 ban 判定使用 google_search_control 下发的 ban_countries(拉取失败时回退 AAR 内置默认列表)。

3.4.2 按国家码(无 RGC)

集成方已知道当前国家(例如车机系统属性、地图 SDK 等)时使用;不发起 RGC,直接比对禁止国家列表并更新可用性。

参数 类型 说明
countryCode String 当前国家 ISO 3166-1 alpha-3 码(如 "USA"
prohibitedCountryCodes List<String>? 可选,禁止 Google 搜索的国家列表;未传时使用 AAR 内置默认:CHNVNMSYRPRKIRNCUBRUSUKR

示例:

1
2
3
4
5
6
7
val defaultProhibitedCountryCodes = listOf(
    "CHN", "VNM", "SYR", "PRK", "IRN", "CUB", "RUS", "UKR"
)
// 使用自定义禁止列表
SearchService.refreshGoogleAvailability("USA", defaultProhibitedCountryCodes)
// 使用 AAR 内置默认禁止列表
SearchService.refreshGoogleAvailability("USA")

返回值(两种重载相同):

类型 说明
Unit 无返回数据;结果通过 isGoogleSearchAvailablegetGoogleSearchAvailabilityState 查询或 listener 接收

3.5 查询 Google 可用性(SearchService.isGoogleSearchAvailable

说明:查询当前是否允许使用 Google 搜索能力,用于决定请求走 Google 还是 TN 路径。

参数列表:

参数 类型 说明
keywords String? 可选搜索词,用于过滤经纬度搜索等特殊场景

返回值:

类型 说明
Boolean true 表示当前可使用 Google 搜索能力

3.6 Google 可用性监听(SearchService.setGoogleSearchAvailabilityListener

说明:注册或取消 Google 搜索可用性变化回调;状态变化时在主线程通知 UI 刷新 Google 标识或展示不可用原因。

参数列表:

参数 类型 说明
listener GoogleSearchAvailabilityListener? listener 实例;传 null 表示取消注册

GoogleSearchAvailabilityListener 回调参数:

参数 类型 说明
available Boolean SearchService.isGoogleSearchAvailable() 一致
state GoogleSearchAvailabilityState 结构化可用性状态,含 reason 与可选 detail;见 §3.6.1

触发时机:初始化完成、refreshGoogleAvailability、WebView ready。

返回值:

类型 说明
Unit 无返回数据

升级说明(自较新版本 search-service 起):

场景 说明
Kotlin 旧写法 { available -> ... } 仍可编译,但无法读取 reason;建议改为 { available, state -> ... }
Java 实现类须实现双参数方法 onGoogleSearchAvailabilityChanged(boolean, GoogleSearchAvailabilityState)
UI 建议 available == true 时展示 Google 标识;false 时用 state.reason(及可选 state.detail)展示诊断文案

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
SearchService.setGoogleSearchAvailabilityListener { available, state ->
    googleBadge.isVisible = available
    if (!available) {
        statusText.text = formatUnavailableReason(state)  // 映射 state.reason 为本地化文案
    } else {
        statusText.isVisible = false
    }
}

// 注册后若需立即刷新 UI,可同步查询一次:
val state = SearchService.getGoogleSearchAvailabilityState()
updateUi(state.available, state)

3.6.1 查询 Google 可用性详情(SearchService.getGoogleSearchAvailabilityState

说明:返回当前 Google 搜索可用性的结构化诊断信息,便于 UI 展示不可用原因;与 listener 回调中的 state 字段一致。

参数列表:

参数 类型 说明
keywords String? 可选搜索词;经纬度类关键词会返回 TN-only 状态

返回值:GoogleSearchAvailabilityState

字段 类型 说明
available Boolean 是否可用
reason GoogleSearchUnavailabilityReason 不可用主因枚举;可用时为 AVAILABLE
detail String? 可选补充信息(HTTP 码、WebView 版本、国家码等)

GoogleSearchUnavailabilityReason 判定优先级(从高到低):

GOOGLE_SEARCH_DISABLEDNETWORK_DISCONNECTEDGOOGLE_SERVICES_UNREACHABLECONFIG_PROJECTS_JSON_FAILED / CONFIG_ASSEMBLE_FAILEDHTML_LOAD_FAILEDWEBVIEW_VERSION_TOO_LOWCONTROL_DISABLEDREGION_BANNEDCOUNTRY_UNKNOWNWEBVIEW_NOT_READYAVAILABLE

常见 reason 与集成方处理建议:

reason 含义 建议
NETWORK_DISCONNECTED 设备无网络 隐藏 Google 标识;网络恢复后调用 setNetworkMode(CONNECTED)refreshGoogleAvailability
COUNTRY_UNKNOWN 尚未 RGC 到国家 有 GPS 时调用 refreshGoogleAvailability(lat, lon)
WEBVIEW_NOT_READY WebView 尚未加载完成 等待 listener 再次回调,勿重复 initialize
REGION_BANNED 当前国家在 ban 列表 正常走 TN,可提示用户该区域无 Google 增强结果
WEBVIEW_VERSION_TOO_LOW System WebView 版本过低 提示用户升级 Android System WebView

3.7 搜索客户端概览(SearchClient

SearchService.getClient() 返回的 SearchClient 是各类搜索能力的统一入口,与 Entity SDK 的 EntityClient 对齐:通过 builder 拼参数,.build().execute() 返回 SearchResponse。服务内部自动选择 Google / TN,并在 response.provider 中标注来源(google / tn)。

Builder 典型场景 Google
searchRequest() 文本、分类、矩形框选、RGC、路线 corridor、品牌/多边形等 部分(见 §4
suggestionPredictionRequest() 地点 Autocomplete
getDetailRequest() POI 详情(Google 结果 ID 以 P-G 开头)
wordPredictionRequest() 关键词建议 TN only
getCategoriesRequest() 分类树 TN only
discoverCategoryRequest() 子类目发现 TN only
discoverBrandRequest() 品牌发现 TN only
discoverPlaceRequest() 按类目发现地点 TN only
searchByExitRequest() 高速出口 / 服务区附近搜索 TN only

通用调用模式:

1
2
3
4
5
6
SearchService.getClient()
    .searchRequest()          // 或其他 builder
    .setLocation(GeoPoint(lat, lon))
    // ... 其他 set 方法 ...
    .build()
    .execute()               // → SearchResponse

HybridEntitySearchRequestBuilder 常用方法:setQuery(String)setQuery(MultiboxQuery)setLocationsetAnchorsetFilterssetLimitsetSort(SortType)setSearchOptionssetFacetParameterssetLocale


3.8 文本搜索(searchRequest

根据输入的文本进行搜索,返回包含地址基本信息的列表。

文本搜索输入

搜索结果列表

1
2
3
4
5
6
7
8
val response = SearchService.getClient().searchRequest()
    .setQuery("coffee")
    .setLocation(GeoPoint(lat, lon))
    .setLimit(20)
    .setSort(SortType.BEST_MATCH)
    .setSearchOptions(SearchOptions.builder().setShowAddressLines(true).build())
    .build()
    .execute()
返回值 说明
SearchResponse operation=textSearchbodySearchBody.EntitySearchentitySearchOrNull()?.resultsList<Entity>

多条件搜索(Multibox):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
val multibox = MultiboxQuery.builder()
    .addTag(Tag.WHAT, "coffee")
    .addTag(Tag.WHERE, "San Francisco")
    .build()

client.searchRequest()
    .setQuery(multibox)
    .setLocation(GeoPoint(lat, lon))
    .setLimit(20)
    .build()
    .execute()

3.9 分类搜索(searchRequest

根据类别进行搜索,返回包含地址基本信息的列表。

分类搜索结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
val categoryFilter = CategoryFilter.builder()
    .setCategories(listOf("241"))   // Coffee,见 [§3.14 分类 ID](#314-其他-searchclient-buildertn-only)
    .build()
val geoFilter = RadiusGeoFilter.builder(2_000).build()
val filters = SearchFilters.builder()
    .setCategoryFilter(categoryFilter)
    .setGeoFilter(geoFilter)
    .build()

client.searchRequest()
    .setLocation(GeoPoint(lat, lon))
    .setFilters(filters)
    .setLimit(20)
    .setSort(SortType.DISTANCE)
    .build()
    .execute()
返回值 说明
SearchResponse operation=categorySearch

可选:在 SearchFilters 中设置 EvFilter(Entity SDK)、BrandFilterCorridorGeoFilter沿途搜索 / Along Route,TN only)等,用法与 Entity Service SDK 一致。分类 ID 见 §3.14


3.10 矩形框选搜索(searchRequest

根据给定区域进行文本/类别搜索,返回包含地址基本信息的列表。

文本框选:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
val bBox = BBox.builder()
    .setBottomLeft(GeoPoint(blLat, blLon))
    .setTopRight(GeoPoint(trLat, trLon))
    .build()
val filters = SearchFilters.builder()
    .setGeoFilter(BBoxGeoFilter.builder(bBox).build())
    .build()

client.searchRequest()
    .setQuery("coffee")
    .setLocation(GeoPoint(lat, lon))
    .setFilters(filters)
    .setLimit(20)
    .build()
    .execute()

分类框选:在 SearchFilters 中同时设置 CategoryFilterBBoxGeoFilteroperation=categoryBoundingBoxSearch)。


3.11 逆地理编码 RGC(searchRequest

根据经纬度搜索,返回所在地址信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
val rgcOptions = SearchOptions.builder()
    .setIntent(SearchOptions.Intent.REVERSE_GEOCODING)
    .setShowAddressLines(true)
    .build()

client.searchRequest()
    .setLocation(GeoPoint(lat, lon))
    .setSearchOptions(rgcOptions)
    .build()
    .execute()
返回值 说明
SearchResponse operation=rgcresults[0].address.country 可用于国家判断

SearchSettings.rgcIsOnboard = true 时走 onboard RGC;第三方云环境通常为 false


3.12 地址自动补全(suggestionPredictionRequest

根据关键字精准匹配,返回的列表信息可通过 ID 再请求详情。

地点自动补全

1
2
3
4
5
6
client.suggestionPredictionRequest()
    .setQuery("star")
    .setLocation(GeoPoint(lat, lon))
    .setLimit(10)
    .build()
    .execute()
返回值 说明
SearchResponse operation=autocompleteautocompleteOrNull()EntitySuggestionPredictionResponse;返回的结果已包含 id,可根据 id 请求地址的详细信息

响应 body 与推荐项字段见 返回对象


3.13 详情搜索(getDetailRequest

根据 ID 获取地点的详细信息。

POI 详情页

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
client.getDetailRequest()
    .setEntityIds(listOf(entityId))
    .setDetailOptions(
        GetDetailOptions.builder()
            .setDetailLevel(GetDetailOptions.EntityDetailLevel.FULL)
            .setShowAddressLines(true)
            .build()
    )
    .setLocation(lat, lon)
    .setFacetParameters(facetParameters)   // 可选
    .build()
    .execute()
返回值 说明
SearchResponse operation=detailentitySearchOrNull()?.results 通常仅一条

Google POI 的 entity ID 以 P-G 开头时走 Google 详情;否则走 TN。


3.14 其他 SearchClient builder(TN only)

词建议wordPredictionRequest()

词建议

1
2
3
4
5
client.wordPredictionRequest()
    .setQuery("cof")
    .setLocation(GeoPoint(lat, lon))
    .build()
    .execute()

分类树getCategoriesRequest()

1
client.getCategoriesRequest().build().execute()

分类 ID

CategoryFilter.setCategories(...)discoverBrandRequest().setCategory(...) 传入分类 ID 字符串。常用示例:

分类 ID 说明
241 Coffee
600 Parking
771 EV charging
811 Gas station
226 Restaurant
2040 Food and drink(父分类)
595 Hotel
794 ATM
915 Bank
318 Pharmacy
288 Hospital
588 Airport

全量分类请调用上方 getCategoriesRequest().build().execute()

品牌发现discoverBrandRequest()

1
2
3
4
5
6
7
client.discoverBrandRequest()
    .setLocation(lat, lon)
    .setLimit(20)
    .setCategory("241")   // 可选
    // .setFilters(DiscoverFilters.builder().setRadiusGeoFilter(2000).build())  // 可选,半径(米)
    .build()
    .execute()

子类目发现discoverCategoryRequest()

1
2
3
4
5
6
client.discoverCategoryRequest()
    .setLocation(lat, lon)
    .setCategory("2040")
    .setLimit(5)
    .build()
    .execute()

按类目发现地点discoverPlaceRequest()

1
2
3
4
5
6
client.discoverPlaceRequest()
    .setLocation(lat, lon)
    .addCategory("771")
    .setLimit(10)
    .build()
    .execute()

高速出口 / 服务区searchByExitRequest()

1
2
3
4
5
6
7
client.searchByExitRequest()
    .setLocation(lat, lon)
    .setCategories(listOf("771"))
    .setRadiusInMeter(1609.3)
    .setExits(exitPoints)   // List<ExitPoint>,Entity SDK 类型
    .build()
    .execute()

以上接口 operation 分别为 wordSuggestioncategoriesdiscoverCategoriesdiscoverBrandsdiscoverPlacessearchByExitbody 多为 SearchBody.Sdk,具体结构见 Entity SDK 文档。


3.15 网络与生命周期(SearchClient

说明:维护搜索客户端运行态——在系统网络变化时同步 TN 云搜与 Google WebView 状态,更新语言,并在退出时释放资源。

方法 说明
setNetworkMode(NetworkMode.CONNECTED \| DISCONNECTED) 同步 TN 云搜与 Google WebView 网络状态;集成方应在系统网络变化时调用
updateLocale(Locale) 更新 Entity SDK locale
isEnableCloudService() TN 在线搜索是否可用(参与 isGoogleSearchAvailable 判定)
dispose() 释放资源;通常通过 SearchService.dispose() 调用

Google 可用性刷新与查询推荐使用 SearchService 静态方法(§3.4–3.6),也可调用 SearchClient 上等价方法。


3.16 统一响应(SearchResponse

说明:所有 builder 的 execute() 均返回 SearchResponse;通过 codeprovideroperation 判断成功与否与结果来源,再按类型解析 body 中的业务数据(无需解析 JSON 字符串)。

字段 类型 说明
code Int 响应码;常用值见 §5.1 响应码
message String? 响应说明
responseTime Long? 耗时(毫秒)
provider String "google""tn";前端可据此展示 Google 标识
operation String textSearchcategorySearchdetailautocomplete
body SearchBody EntitySearch / Autocomplete / Sdk

便捷方法:

  • entitySearchOrNull()EntitySearchResponse(列表、分类、详情、RGC 等)
  • autocompleteOrNull()EntitySuggestionPredictionResponse

EntitySearchResponse 主要字段:

字段 说明
results List<Entity>;POI 为 PLACE,地址/RGC 为 ADDRESS
code Entity SDK ResponseCode(如 SUCCESS
responseType CLOUDONBOARD
hasMore 是否还有更多结果

更完整说明见 返回对象

推荐读取范式:见 安全读取结果(含 code 判断、entitySearchOrNull() 为 null、空 results、fallback 后 provider 的端到端示例)。

成功判断:优先使用 SearchResponseCodes.isSuccess(response)(等价于 code == SUCCESS || code == PARTIAL_SUCCESS)。NO_CONTENT12204)不算成功,应走空结果分支。


4. Google 搜索能力说明

SearchService 在支持的区域和场景下,对下列 builder 请求尝试 Google 搜索;超时(SearchSettings.timeout)或失败时自动 fallback TN。集成方统一消费 SearchResponse,通过 provider 区分来源。

Builder / 场景 Google 支持
searchRequest() + 文本 query
searchRequest() + CategoryFilter + RadiusGeoFilter
searchRequest() + BBoxGeoFilter(含分类框选)
suggestionPredictionRequest()
getDetailRequest()P-G ID)
searchRequest() + CorridorGeoFilter沿途搜索 / Along Route ❌ TN only
searchRequest() + PolygonGeoFilter ❌ TN only
searchRequest() + RGC intent ❌ TN only
searchRequest() + 仅 BrandFilter ❌ TN only
setFacetParameters TN 全量;Google 忽略
wordPredictionRequest / getCategoriesRequest / discoverCategoryRequest / discoverBrandRequest / discoverPlaceRequest / searchByExitRequest ❌ TN only

限制与说明:

说明
沿途搜索 使用 CorridorGeoFilter(Along Route);Google 不支持,仅走 TN Entity Service
EV 筛选 Google 支持 EvFilter.connectorTypesminPowermaxPower 目前不支持
返回条数 Google 单页上限约 20 条
坐标关键词 isGoogleSearchAvailable("37.7,-122.4") 返回 false,走 TN
国家 ban 须先 refreshGoogleAvailability;详见 README

前端:response.provider == "google" 时展示 Google 来源标识。

更细的 Entity SDK 类型与 Google/TN 字段对照见 请求参数


5. 附录

5.1 响应码

常用 SearchResponseCodes

常量 含义
SUCCESS 12200 成功
NO_CONTENT 12204 列表搜索或补全搜索无内容
PARTIAL_SUCCESS 12206 部分成功
INVALID_REQUEST 12400 非法请求
INVALID_APIKEY_OR_SIGNATURE 12401 鉴权失败
ENTITY_NOT_FOUND 12404 详情搜索ID未找到
SERVICE_TIMEOUT_ERROR 12504 超时
SERVICE_DATA_ERROR 12505 数据错误

5.2 排序

searchRequest() 上使用 setSort(SortType)

SortType 说明
SortType.BEST_MATCH 按相关性(文本搜索默认)
SortType.DISTANCE 按距离(分类搜索常用)