- 移动开发
【免费下载链接】Kvaesitso
A search-focused Android launcher
导读
Kvaesitso 是一个以搜索为核心交互的 Android 桌面启动器(launcher),其内置的在线百科搜索能力完全基于 Wikipedia 开放的 MediaWiki API 实现。本文面向希望在 Kvaesitso 中启用、自定义或深度理解该集成的开发者与高级用户,完整讲解:MediaWiki API 的调用方式与查询参数、语言相关默认端点的配置机制(wikipedia_url字符串资源)、自定义 Wikipedia URL 的偏好设置链路,以及从搜索查询到结果展示的完整数据流。读完本文,你将能够定位相关源码模块、看懂端到端调用链,并掌握在任意语言环境或自建 MediaWiki 站点下正确配置 Kvaesitso 在线百科搜索的方法。
集成概述:零配置的 MediaWiki API 客户端
Kvaesitso 通过MediaWiki API(https://www.mediawiki.org/wiki/API:Main_page)从 Wikipedia 加载文章数据。根据官方开发者文档 wikipedia.md 的说明,该集成开箱即用、无需额外配置:
- 默认的 MediaWiki 安装 URL 是语言相关(language-dependent)的;
- 该默认地址可以通过
:i18n模块中的wikipedia_url字符串资源(string resource)进行配置; - 用户在设置界面还可以进一步用自定义 URL 覆盖默认值。
从源码结构看,该功能由三个模块协同完成,各自职责清晰:
| 模块 | 职责 |
|---|---|
:data:wikipedia(data/wikipedia) | MediaWiki API 客户端、搜索仓储、结果模型与序列化 |
:core:i18n(core/i18n) | 提供各语言的wikipedia_url、wikipedia_source等本地化字符串 |
:core:preferences(core/preferences) | 持久化搜索开关与自定义 URL 偏好 |
模块已在 settings.gradle.kts 中注册(include(":data:wikipedia")),并通过 Koin 依赖注入接入应用。
核心实现:data:wikipedia模块的五个文件
整个 Wikipedia 集成集中在de.mm20.launcher2.wikipedia包中,共五个 Kotlin 文件,从网络层到 UI 数据层逐级解耦:
- Module.kt:Koin 依赖注入模块,注册搜索仓储与反序列化器;
- WikipediaApi.kt:基于 Ktor 的 HTTP 客户端,封装 MediaWiki
action=query请求; - WikipediaRepository.kt:搜索入口,负责查询长度校验、端点选择与结果组装;
- Wikipedia.kt:结果数据类,实现
Article接口,提供打开、分享、占位图标等行为; - WikipediaSerialization.kt:结果的 JSON 序列化/反序列化,用于收藏或缓存恢复。
其中 Module.kt 将仓储注册为SearchableRepository<Article>单例,并把wikipedia域(Wikipedia.Domain,见 Wikipedia.kt)映射到专用的反序列化器,使搜索结果可以按类型正确恢复。
网络层:MediaWikiaction=query请求的构造
WikipediaApi.kt 是请求的最终发出者,其search()方法(L69-L89)完整构造了一次generator=search的查询:
suspend fun search(baseUrl: String, query: String, thumbnailSize: Int): WikipediaSearchResult { return httpClient.get { url { takeFrom(baseUrl) if (pathSegments.isEmpty()) { path("w", "api.php") } parameter("action", "query") parameter("generator", "search") parameter("redirects", "true") parameter("gsrlimit", "1") parameter("prop", "extracts|info|pageimages") parameter("exchars", "500") parameter("exintro", "true") parameter("inprop", "url") parameter("format", "json") parameter("gsrsearch", query) parameter("pithumbsize", thumbnailSize) } }.body() }各查询参数的含义与 Kvaesitso 的取值策略如下:
| 参数 | 取值 | 作用 |
|---|---|---|
action | query | 执行一次 MediaWiki 查询 |
generator | search | 以全文搜索为查询生成器 |
redirects | true | 自动跟随重定向到规范条目 |
gsrlimit | 1 | 每个查询仅取 1 条结果,保证搜索框响应迅速 |
prop | extracts\|info\|pageimages | 同时取摘要、页面信息与缩略图 |
exchars | 500 | 摘要文本截取 500 字符 |
exintro | true | 只取条目引言部分作为摘要 |
inprop | url | 返回fullurl与canonicalurl完整链接 |
format | json | 响应格式为 JSON |
gsrsearch | 用户查询词 | 搜索关键词 |
pithumbsize | 屏幕宽度的一半 | 缩略图尺寸(见下文数据层) |
值得注意的实现细节:当传入的baseUrl路径为空时,客户端会在其后拼接w/api.php(L73-L75),因此端点既可以写成https://en.wikipedia.org,也可以直接写成完整的https://en.wikipedia.org/w/api.php。
HTTP 客户端配置:超时、User-Agent 与 JSON 解析
WikipediaApi.kt 中懒加载(by lazy)构造了一个 KtorHttpClient:
- JSON 解析:启用
ContentNegotiation+kotlinx.serialization的Json.Lenient模式,容忍 MediaWiki 响应中的非严格格式; - User-Agent:以
包名/v版本号形式设置,遵循 MediaWiki 官方对客户端标识的要求; - 超时控制:
connectTimeoutMillis = 200、requestTimeoutMillis = 3000、socketTimeoutMillis = 1000。其中连接超时仅 200ms,一旦网络不可用会快速失败,避免搜索框阻塞; - 默认请求:
defaultRequest { url(baseUrl) },但实际每次请求仍通过takeFrom(baseUrl)重新确定端点。
响应体通过三个@Serializable数据类解析(L18-L41):最外层WikipediaSearchResult.query.pages是Map<String, WikipediaSearchResultQueryPage>,每页包含pageid、title、extract、thumbnail.source、fullurl与canonicalurl字段。
数据层:搜索流程、最短查询与端点选择
WikipediaRepository.kt 实现SearchableRepository<Article>接口,其search()方法(L22-L39)是整个搜索链路的入口,包含三个关键策略:
- 最短查询长度:
if (query.length < 4 || !allowNetwork) return flowOf(persistentListOf())—— 少于 4 个字符的查询直接返回空结果,且必须显式允许联网(allowNetwork),否则同样不发起任何网络请求; - 动态端点选择:通过
combineTransform(settings.enabled, settings.customUrl)订阅偏好流,baseUrl的优先级是自定义 URL → 本地化默认 URL:val baseUrl = url.takeIf { !it.isNullOrBlank() } ?: context.getString(R.string.wikipedia_url) - 并发组合:
settings.enabled为 false 时结果流被过滤为空(emit(persistentListOf())),实现设置界面开关的即时生效。
queryWikipedia()(L41-L64)负责实际请求与组装:
- 缩略图宽度取当前屏幕物理像素宽度的一半(
context.resources.displayMetrics.widthPixels / 2),保证搜索卡片清晰且不产生冗余流量; - 请求异常(超时、网络不可用、解析失败)会被
CrashReporter.logException(e)记录并返回null,不会让崩溃打断用户搜索; - 取
pages的第一条结果构造Wikipedia对象,sourceName使用R.string.wikipedia_source(默认文案为 "From Wikipedia",见 strings.xml)。
结果模型与交互:打开、分享与占位图标
Wikipedia.kt 中的Wikipedia数据类(L16-L25)实现了Article接口(定义于 core/base),包含label(条目标题)、text(摘要)、imageUrl(缩略图)、sourceUrl(fullurl)、wikipediaUrl(所用端点)等字段。
- 唯一键:
key = "$domain://$wikipediaUrl:$id"(L33),以「域名 + 端点 + 页面 ID」三元组区分不同实例,避免不同语言/自定义站点结果互相冲突; - 打开行为:
launch()通过Intent.ACTION_VIEW+Uri.parse(sourceUrl)调起浏览器(L46-L52); - 分享行为:
share()(L55-L65)将 HTML 摘要转为纯文本,截取前 200 字符与来源链接一起放入系统分享菜单; - 占位图标:在网络加载完成前,使用
R.drawable.ic_wikipedia配合灰色色调图层生成占位图标(L35-L44)。
持久化:结果序列化与收藏恢复
搜索历史与收藏需要跨会话恢复,WikipediaSerialization.kt 承担这一职责:
WikipediaSerializer(L9-L24)将label、text、id、image、wikipedia_url、url写入 JSON,typePrefix为"wikipedia";WikipediaDeserializer(L26-L40)恢复结果,若序列化数据中缺少url字段,会自动按$wikipediaUrl/wiki?curid=$id的格式重建来源链接——这保证即使文章页面 URL 过期,也能通过curid稳定定位到原条目。
端点配置:语言相关的wikipedia_url字符串资源
官方文档明确指出默认端点「语言相关」,这一点在 core/i18n 的本地化资源中有完整体现。values/strings.xml中默认值为https://en.wikipedia.org(L94-L98),其注释说明了设计意图:
The URL of the Wikipedia instance that is used per default. Can be overridden by user. This should point to the Wikipedia in the given language. If no such Wikipedia exists, a fallback that is suited best for most users of this language should be used (English in most cases).
即:各语言应指向对应语言的 Wikipedia;若该语言没有独立 Wikipedia,则回退到对该语言用户最合适的实例(通常是英文)。仓库中 40 余个语言目录均定义了wikipedia_url,例如:
| 语言资源目录 | wikipedia_url值 |
|---|---|
| values/strings.xml(默认/英文) | https://en.wikipedia.org |
| values-de | https://de.wikipedia.org |
| values-ja | https://ja.wikipedia.org |
| values-ko | https://ko.wikipedia.org |
| values-fr | https://fr.wikipedia.org |
| values-zh-rTW | https://zh.wikipedia.org/zh-tw/ |
| values-zh-rCN | https://en.wikipedia.org(回退到英文) |
| values-ca | https://ca.wikipedia.org/(带尾斜杠) |
注意两个细节:其一,values-zh-rCN遵循「无对应实例则回退英文」的约定,而values-zh-rTW指向繁体中文本地化路径/zh-tw/;其二,部分值带有尾斜杠(如加泰罗尼亚语),结合 WikipediaApi.kt 的路径拼接逻辑,pathSegments为空时才会追加w/api.php,因此这些写法都能正确工作。
由于该字符串位于:i18n模块,修改或新增语言支持时只需在该模块对应values-*目录的strings.xml中调整此值,无需改动任何 Kotlin 代码。
自定义端点:设置界面与偏好存储
除默认语言端点外,Kvaesitso 还允许用户直接在设置中覆盖 Wikipedia URL,链路为「设置界面 → ViewModel → DataStore 偏好」。
偏好层
WikipediaSearchSettings.kt 封装了两个流式偏好:
enabled:读写wikipediaSearchEnabled(DataStore 默认值为true,见 LauncherSettingsData.kt);customUrl:读写wikipediaCustomUrl(默认null,见 LauncherSettingsData.kt),且setCustomUrl()会把空白字符串归一化为null。
两者均基于LauncherDataStore(Jetpack DataStore)持久化,并通过distinctUntilChanged()去重。
设置界面
WikipediaSettingsScreen.kt 使用 Compose 渲染偏好页(标题为preference_search_wikipedia,即 "Wikipedia"),包含:
- 启用开关:
SwitchPreference,说明文字为 "Search the free encyclopedia"(strings.xml); - URL 输入框:
TextPreference,标题为 "Wikipedia URL"(preference_wikipedia_customurl,strings.xml),占位符与空值摘要均显示当前语言默认端点,用户清空输入即回到默认值。
WikipediaSettingsScreenVM.kt 负责在界面与偏好之间桥接,通过stateIn将 Flow 转为 Compose 可观察状态,用户输入经setCustomUrl()写回 DataStore。最终在 WikipediaRepository.kt 中生效:非空的自定义 URL 优先于本地化默认值。
启用在线搜索:网络权限过滤器
出于隐私与流量考虑,Wikipedia 搜索(以及所有在线结果)默认处于关闭状态。官方用户文档 online-results.md 明确说明:在线结果默认禁用,需要开启「Online results」过滤器才能生效。
这一行为在源码中有直接体现:SearchFilters.allowNetwork默认值为false(见 SearchFilters.kt),而 WikipediaRepository.kt 中!allowNetwork会直接短路返回空结果。也就是说,即使 Wikipedia 搜索开关为开,只要当前查询未启用「Online results」过滤器,就不会产生任何网络请求。
启用路径(依据 filters.md):
- 点击搜索栏中的过滤器图标,或使用键盘上方弹出的过滤器栏;
- 开启Online results过滤器;
- 如需每次默认包含在线结果,可在设置 → 搜索 → 默认过滤器中预设;
- 清空搜索框后过滤器会被重置。
需要留意:类型过滤器(如articles)只控制结果类型,不代表允许联网搜索,Wikipedia 属于在线结果,必须同时启用 Online results 过滤器。
扩展与自定义场景
理解了上述机制后,可以衍生出三类实用场景:
- 切换到其他语言的 Wikipedia:无需任何操作,系统语言对应语言环境会自动使用
:i18n中的默认端点; - 指向任意 MediaWiki 站点:在设置中把 Wikipedia URL 改为任何兼容 MediaWiki API 的站点(如
https://wiki.example.org),即把百科搜索接入自建 Wiki; - 二次开发接入新语言:在 core/i18n/src/main/res 下为目标语言目录新增/修改
wikipedia_url,遵循「语言实例优先、无则回退英文」的既有约定即可。
小结
Kvaesitso 的 Wikipedia 集成是一条短小精悍但层次完整的调用链:偏好层(core:preferences)决定「是否启用、用哪个端点」→ 仓储层(data:wikipedia)校验查询长度与网络许可并选择端点 → API 层(Ktor)向 MediaWikiaction=query发起单结果请求 → 结果模型实现Article接口完成打开、分享与序列化。全程无需 API Key,端点完全由语言资源与用户偏好驱动,且所有超时与异常都被妥善处理,保证搜索框体验不被网络状况拖累。
相关文件索引
- 官方开发者文档:docs/docs/developer-guide/external-apis/wikipedia.md
- API 客户端:data/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaApi.kt
- 搜索仓储:data/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaRepository.kt
- 结果模型:data/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/Wikipedia.kt
- 序列化:data/wikipedia/src/main/java/de/mm20/launcher2/wikipedia/WikipediaSerialization.kt
- 偏好设置:core/preferences/src/main/java/de/mm20/launcher2/preferences/search/WikipediaSearchSettings.kt
- 默认端点定义:core/i18n/src/main/res/values/strings.xml
- 设置界面:app/ui/src/main/java/de/mm20/launcher2/ui/settings/wikipedia/WikipediaSettingsScreen.kt
- 网络过滤器:core/base/src/main/java/de/mm20/launcher2/search/SearchFilters.kt
- 用户文档:docs/docs/user-guide/search/online-results.md、docs/docs/user-guide/search/filters.md
- 移动开发
【免费下载链接】Kvaesitso
A search-focused Android launcher
相关推荐
kotaemon工具集成:Wikipedia+Google搜索实战指南
kotaemon工具集成:Wikipedia+Google搜索实战指南 痛点:传统RAG系统的信息局限 你是否遇到过这样的场景?当你的文档问答系统遇到超出本地知
人工智能大模型RAG向量数据库后端在Xinference中配置API端口与Dify集成指南
在Xinference中配置API端口与Dify集成指南 Xinference作为一个开源的模型推理框架,提供了便捷的API接口供其他系统调用。本文将详细介绍如
模型推理服务大模型人工智能本地部署LLM 网关后端storm API完全指南:自定义语言模型与搜索引擎集成
storm API完全指南:自定义语言模型与搜索引擎集成 引言 你是否在构建知识整理系统时遇到以下痛点?现有框架对语言模型(Language Model, LM
人工智能大模型深度研究RAGAI 应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考