资讯动态

ApiDemos 中 LoaderManager 回调接口 LoaderCallbacks 的调用顺序【学习例子】——用 TaoToken 统一 Key 复现全流程

发布时间:2026/9/28 11:32:13 来源:尧图企业网站定制
1. 从 ApiDemos 的 LoaderCursor 说起回调顺序到底怎么走ApiDemos 里的LoaderCursor这个例子是很多人第一次接触LoaderManager和LoaderCallbacks的入口。它做的事情很朴素用CursorLoader去查系统联系人把结果塞进SimpleCursorAdapter再配合 ActionBar 的搜索框做过滤。但真正让人绕晕的是LoaderCallbacks那三个方法——onCreateLoader、onLoadFinished、onLoaderReset——它们什么时候被调用、谁先谁后、搜索时又怎么重新触发。如果你只是把代码复制进 Android Studio 跑一遍日志里能看到onCreateLoad和onLoadFinished交替出现但一旦加上restartLoader顺序就变得不那么直观了。这篇就围绕 ApiDemos 的LoaderCursor示例把回调时序拆开讲清楚同时用 TaoToken 的统一 Key 和 API 通道把「配置—请求—验证」这条链路也走一遍让你在本地能稳定复现并观察日志。适合谁看正在学 Android Loader 机制、被onLoaderReset触发时机搞懵、或者想找一个可复制的settings.json骨架来统一管理模型调用配置的同学。下面所有步骤都可以跟着做代码和配置都会给全。2. 前置准备用 TaoToken 统一 Key 打通调用通道在开始改 ApiDemos 之前先把调用通道配好。TaoToken 的作用是把模型对话、编码辅助这类请求统一到一个 Key 和一套 API 地址上省得每个工具各配一份。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。你需要先拿到一个 API Key。进入控制台创建即可https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 然后在 API Keys 页面生成https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。Key 生成后只显示一次记得先存到本地环境变量或配置文件里别直接提交到 Git。如果你后面要长期做编码类任务比如让模型帮你读 ApiDemos 源码、生成 Loader 相关代码可以了解下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。想先验证模型能不能正常对话用模型对话页最快https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 。注意Key 属于敏感凭证配置文件建议放在项目外的用户目录或者用.gitignore排除。下面给的settings.json骨架里用占位符你替换成自己的即可。3. 可复制配置settings.json 骨架与 ApiDemos 回调代码3.1 settings.json 骨架把下面这段保存为settings.json放在你习惯的配置目录。它定义了统一的 API 基址、Key 引用和默认模型后续无论是命令行工具还是编辑器插件都可以读这份配置。{ provider: taotoken, api_base: https://taotoken.net/api, api_key: sk-替换成你的Key, default_model: claude-sonnet, timeout_ms: 60000, retry: { max_attempts: 3, backoff_ms: 800 }, logging: { level: info, log_loader_callbacks: true } }api_base不要加 UTM 参数保持干净的接口地址。log_loader_callbacks是我自己加的一个开关用来提醒自己在看日志时重点关注 Loader 回调实际工具不一定认这个字段但作为配置注释留着挺方便。3.2 ApiDemos 回调代码的关键片段ApiDemos 的LoaderCursor里CursorLoaderListFragment实现了LoaderManager.LoaderCallbacksCursor。核心三个方法如下我加了日志标记方便观察顺序Override public LoaderCursor onCreateLoader(int id, Bundle args) { Log.d(LoaderOrder, onCreateLoader id id filter mCurFilter); Uri baseUri; if (mCurFilter ! null) { baseUri Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(mCurFilter)); } else { baseUri Contacts.CONTENT_URI; } String select (( Contacts.DISPLAY_NAME NOTNULL) AND ( Contacts.HAS_PHONE_NUMBER 1) AND ( Contacts.DISPLAY_NAME ! )); return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME COLLATE LOCALIZED ASC); } Override public void onLoadFinished(LoaderCursor loader, Cursor data) { Log.d(LoaderOrder, onLoadFinished id loader.getId() count (data null ? -1 : data.getCount())); mAdapter.swapCursor(data); if (isResumed()) { setListShown(true); } else { setListShownNoAnimation(true); } } Override public void onLoaderReset(LoaderCursor loader) { Log.d(LoaderOrder, onLoaderReset id loader.getId()); mAdapter.swapCursor(null); }初始化在onActivityCreated里getLoaderManager().initLoader(0, null, this);搜索框文字变化时public boolean onQueryTextChange(String newText) { mCurFilter !TextUtils.isEmpty(newText) ? newText : null; getLoaderManager().restartLoader(0, null, this); return true; }这两处是触发回调顺序变化的关键。initLoader和restartLoader的行为差异直接决定了onCreateLoader会不会被再次调用。4. 逐步验证观察回调顺序与请求结果4.1 首次进入initLoader 触发的顺序在onActivityCreated里调用initLoader(0, null, this)后日志顺序是onCreateLoader id0 filternull onLoadFinished id0 countxxinitLoader的逻辑是如果 id 为 0 的 Loader 不存在就创建一个触发onCreateLoaderLoader 在后台线程完成数据加载后回调onLoadFinished。注意onCreateLoader是在主线程调用的但返回的CursorLoader会在后台执行查询所以onLoadFinished会晚一些到。4.2 输入搜索词restartLoader 的顺序当你在搜索框输入文字onQueryTextChange被调用执行restartLoader(0, null, this)。此时日志会变成onCreateLoader id0 filterabc onLoaderReset id0 onLoadFinished id0 countyy这里就是最容易搞混的地方。restartLoader会先销毁旧的 Loader触发onLoaderReset让旧数据不可用同时创建新的 Loader触发onCreateLoader。但onLoaderReset和onCreateLoader的先后在不同 Android 版本和实现里可能略有差异实测下来常见的是onCreateLoader先于onLoaderReset然后新的onLoadFinished最后到。onLoaderReset里必须把mAdapter.swapCursor(null)否则旧 Cursor 被关闭后适配器还持有引用会崩。4.3 用 TaoToken 验证请求通道配置好settings.json后可以用一条简单的 curl 验证通道是否通curl -s https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-替换成你的Key \ -d { model: claude-sonnet, messages: [ {role: user, content: 用一句话说明 LoaderCallbacks 三个方法的调用顺序} ] }返回里能看到模型对顺序的描述说明 Key 和 API 基址都配对了。如果返回 401检查 Key 是否复制完整如果超时看timeout_ms是否设得太短。4.4 回调顺序对照表触发动作onCreateLoaderonLoadFinishedonLoaderReset首次 initLoader调用一次数据就绪后调用不调用restartLoader搜索再次调用新数据就绪后调用旧 Loader 销毁时调用Activity 销毁不调用不调用可能调用释放资源Loader 数据变化不调用再次调用不调用这张表建议对着日志看一遍比死记硬背管用。5. 本篇常见错排查5.1 onLoaderReset 没触发如果你在搜索时只看到onCreateLoader和onLoadFinished没有onLoaderReset先确认是不是用了initLoader而不是restartLoader。initLoader在 Loader 已存在时不会重建自然也不会触发 reset。另外如果旧 Loader 的数据还没加载完就被重启reset 的时机可能被推迟。5.2 日志顺序和预期不一致不同 Android 版本对restartLoader内部实现有差异onCreateLoader和onLoaderReset的先后可能互换。判断标准不是绝对顺序而是新 Loader 创建后旧数据必须通过onLoaderReset置空新数据通过onLoadFinished填入。只要这个语义成立顺序细节可以接受。5.3 请求返回 401 或 403检查settings.json里的api_key是否带了多余空格或者 Key 是否已过期。重新在 API Keys 页面生成一个即可https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。另外确认api_base写的是https://taotoken.net/api不要拼错路径。5.4 Cursor 关闭后仍被使用onLoaderReset里忘记swapCursor(null)或者onLoadFinished里对已关闭的 Cursor 做操作都会抛StaleDataException。养成在 reset 里清空适配器的习惯这是 ApiDemos 示例里明确写了的。5.5 配置文件读取不到如果你把settings.json放在项目根目录但工具读不到检查工作目录是否正确。多数工具从当前工作目录向上查找配置文件建议放在用户主目录下或者用环境变量指定路径。6. 把配置和回调顺序固定下来回调顺序这件事光看文档容易忘最好的办法是自己在 ApiDemos 里加日志跑一遍。onCreateLoader负责造 LoaderonLoadFinished负责填数据onLoaderReset负责清引用三者围绕「数据可用性」转。搜索场景下restartLoader会走一遍「创建新—重置旧—完成新」的流程把日志打出来对照上面的表格基本就不会再混。配置侧用 TaoToken 的统一 Key 和settings.json骨架能把模型调用和本地调试分开管理。需要长期做编码辅助的话Coding Plan 那条通道更适合持续用https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。接入细节和参数说明可以查文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。先把 ApiDemos 的日志跑通再把这套配置套到自己的项目里顺序和通道就都稳了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑