新闻详情

新闻详情

首页 / 资讯中心 / 详情

LoaderManager源码分析之二:LoadTask、AsyncTask与CursorLoader的协作链路拆解

发布时间:2026/10/2 20:29:49来源:尧图网络
LoaderManager源码分析之二:LoadTask、AsyncTask与CursorLoader的协作链路拆解
1. 从一次联系人列表卡顿说起LoadTask 到底在背后做了什么如果你写过 Android 联系人列表、短信会话列表或者任何用CursorLoader加载数据库的页面大概率遇到过这样的困惑明明只调用了getLoaderManager().initLoader()数据怎么就自己回来了数据库一改界面怎么又自己刷新了这背后其实是一条相当长的链路——LoaderManager负责调度LoaderInfo负责记账LoadTask负责跑腿AsyncTask负责线程CursorLoader负责真正查库ForceLoadContentObserver负责监听变化。这篇就聚焦这条链路里最容易被忽略、但最核心的一环LoadTask如何借助AsyncTask驱动CursorLoader完成异步加载。我会把任务创建、启动、回调、销毁四个阶段拆开配上可对照源码复现的验证步骤。适合已经用过 Loader 但没读过源码的 Android 开发者也适合想搞清楚「异步加载 数据监听」这套组合拳怎么落地的人。读完之后你应该能自己在本地把LoadTask的生命周期完整跑一遍而不是停留在「大概知道它是个 AsyncTask」的层面。2. 前置准备TaoToken 接入与源码阅读环境读源码这件事最怕的是环境没搭好翻到一半被编译错误打断。我建议先把两件事准备好一个是能快速验证模型对源码理解的环境另一个是能直接跳转的 AOSP 或 support 库源码。如果你手头没有现成的 API Key可以先去 TaoToken 官网注册一个账号然后在控制台创建 API Key。整个过程不复杂注册后在 console 页面点「API Keys」就能生成。拿到 Key 之后你可以用它来跑一些辅助理解源码的对话比如把AsyncTaskLoader的片段贴进去问「这里的 mTask 在 cancel 之后为什么还能复用」比自己硬啃快很多。具体入口我列一下方便你按需跳转用途地址官网首页https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 接入地址https://taotoken.net/api模型对话问源码https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite控制台建 Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewriteAPI Keys 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite源码这边LoaderManager、LoadTask、AsyncTaskLoader都在androidx.loader或老版本的android.support.v4.content包里。用 Android Studio 打开任意一个用了 Loader 的项目在initLoader上按住 Ctrl 点进去就能一路跟到LoadTask。如果你习惯看在线源码AOSP 的frameworks/support/loader目录也能找到对应文件。注意不同 AndroidX 版本里LoadTask的字段名可能略有差异比如mTask、mCancelled这些但整体结构是一致的。读的时候以你项目里实际依赖的版本为准。3. LoadTask 三部曲doInBackground、onPostExecute、onCancelled 的可复制配置LoadTask是AsyncTaskLoader的内部类继承自AsyncTask。它重写了三个方法doInBackground、onPostExecute、onCancelled。注意它没有重写onProgressUpdate所以进度更新这条路是断的——这也是为什么 Loader 不适合做带进度条的加载。3.1 doInBackground真正查库的地方doInBackground跑在线程池的线程里这是整条链路里唯一不在主线程执行的环节。它的核心逻辑是调用mLoader.loadInBackground()而CursorLoader对这个方法的实现就是一次ContentResolver.query。// CursorLoader.loadInBackground 的核心片段 public Cursor loadInBackground() { synchronized (this) { if (isLoadInBackgroundCanceled()) { throw new OperationCanceledException(); } mCancellationSignal new CancellationSignal(); } try { Cursor cursor getContext().getContentResolver().query( mUri, mProjection, mSelection, mSelectionArgs, mSortOrder, mCancellationSignal); if (cursor ! null) { try { cursor.getCount(); // 确保 cursor window 被填充 cursor.registerContentObserver(mObserver); } catch (RuntimeException ex) { cursor.close(); throw ex; } } return cursor; } finally { synchronized (this) { mCancellationSignal null; } } }这里有两个细节值得注意。第一cursor.getCount()不是可有可无的它强制把 CursorWindow 填满否则后面读数据可能触发二次查询。第二registerContentObserver把ForceLoadContentObserver挂到了 Cursor 上这是后面数据库变化能通知到 UI 的关键。有些项目会像ContactEntryListFragment那样重写onLoadInBackground但只是包一层 try-catch 打个 log实际逻辑还是走 super。这种重写对链路没有影响读源码时可以直接跳过。3.2 onCancelled主线程上的清理onCancelled跑在主线程负责在任务被取消时关闭 Cursor。CursorLoader对它的实现很直接Override public void onCanceled(Cursor cursor) { if (cursor ! null !cursor.isClosed()) { cursor.close(); } }这一步很重要。如果 Cursor 不关CursorWindow 的内存就泄漏了。所以 Loader 被 reset 或 destroy 时这条路径必须走到。3.3 onPostExecute把结果递回去onPostExecute同样在主线程它调用mLoader.deliverResult(data)而deliverResult又会触发mListener.onLoadComplete(this, data)。这个mListener是谁就是LoaderInfo。public void deliverResult(D data) { if (mListener ! null) { mListener.onLoadComplete(this, data); } }到这里数据从后台线程回到了主线程并且交给了LoaderInfo。接下来就是回调的分发。4. 注册与回调LoaderInfo 如何把结果送到 onLoadFinishedLoaderInfo是LoaderManager的内部类它实现了两个接口Loader.OnLoadCompleteListener和Loader.OnLoadCanceledListener。在LoaderInfo.start()里会把自己注册给 LoadermLoader.registerListener(mId, this); mLoader.registerOnLoadCanceledListener(this);registerListener的实现很简单就是把mListener指向传入的 listener同时记下 idpublic void registerListener(int id, OnLoadCompleteListenerD listener) { if (mListener ! null) { throw new IllegalStateException(There is already a listener registered); } mListener listener; mId id; }当onLoadComplete被触发时LoaderInfo会调用callOnLoadFinished最终走到mCallbacks.onLoadFinished(loader, data)。这个mCallbacks就是你在 Activity 或 Fragment 里实现的LoaderManager.LoaderCallbacks接口。所以整条回调链是LoadTask.onPostExecute→Loader.deliverResult→LoaderInfo.onLoadComplete→LoaderInfo.callOnLoadFinished→LoaderCallbacks.onLoadFinished你可以用下面这段代码在onLoadFinished里打个断点然后顺着调用栈往上翻就能看到完整的链路Override public void onLoadFinished(LoaderCursor loader, Cursor data) { Log.d(LoaderChain, onLoadFinished, loader loader , cursor data); // 在这里打断点查看调用栈 mAdapter.swapCursor(data); }实测下来调用栈里会依次出现LoadTask.onPostExecute、ModernAsyncTask.finish、LoaderInfo.onLoadComplete这几层和源码结构完全对得上。5. 观察者模式数据库一变UI 怎么知道前面提到CursorLoader.loadInBackground里注册了ForceLoadContentObserver。这个类是Loader的内部类继承自ContentObserverpublic final class ForceLoadContentObserver extends ContentObserver { public ForceLoadContentObserver() { super(new Handler()); } Override public boolean deliverSelfNotifications() { return true; } Override public void onChange(boolean selfChange) { onContentChanged(); } }onContentChanged会触发onForceLoad而AsyncTaskLoader.onForceLoad的实现是Override protected void onForceLoad() { super.onForceLoad(); cancelLoad(); mTask new LoadTask(); executePendingTask(); }也就是说数据库一变旧的 LoadTask 被 cancel新的 LoadTask 被创建并丢进线程池重新查询。查完之后再走一遍onPostExecute→onLoadFinishedUI 就更新了。这就是「数据库变化自动刷新」的完整闭环。你可以这样验证在联系人列表页面打开后用 adb 往数据库插一条记录观察 logcat 里是否出现onForceLoad和新的onLoadFinished。adb shell content insert --uri content://com.android.contacts/raw_contacts \ --bind account_name:s:test --bind account_type:s:test如果链路正常你会看到列表自动多出一项同时 logcat 里有对应的回调日志。6. 本篇常见错排查问题一onLoadFinished 不回调。先检查initLoader是否传了正确的 id以及LoaderCallbacks是否实现完整。如果onCreateLoader返回 null链路直接断掉。问题二Cursor 泄漏警告。多半是onCancelled没走到或者onLoadFinished里 swapCursor 之后没有 close 旧 cursor。用 StrictMode 可以快速定位。问题三数据库变了但 UI 不刷新。检查registerContentObserver是否执行。如果loadInBackground里 query 返回 null注册就不会发生。另外如果 ContentProvider 没有正确 notifyChange观察者也不会收到通知。问题四LoadTask 重复创建导致查询抖动。频繁触发onForceLoad时每次都会 cancel 旧任务再建新任务。如果数据库变化很频繁可以考虑加防抖或者改用其他加载方案。问题五AsyncTask 线程池被占满。LoadTask用的是AsyncTask的线程池如果同时有大量 Loader 在跑可能排队。这种情况一般出现在多 Tab 同时加载的场景需要评估是否要限制并发。如果你在排查过程中需要快速验证某个 API 的行为可以用 TaoToken 的模型对话把源码片段贴进去问比翻文档快。接入方式参考文档里的说明API 地址是https://taotoken.net/apiKey 在控制台的 API Keys 页面生成。7. 把链路跑通之后下一步做什么读到这里你应该能把LoadTask的生命周期完整串起来了initLoader触发LoaderInfo.startstart创建LoadTask并注册监听LoadTask.doInBackground调CursorLoader.loadInBackground查库并注册观察者查完onPostExecute把结果递给LoaderInfoLoaderInfo再回调onLoadFinished。数据库一变ForceLoadContentObserver触发onForceLoad整个流程重跑一遍。如果你想继续深入可以顺着LoaderManagerImpl看initLoader和restartLoader的区别或者对比AsyncTaskLoader和Loader在取消逻辑上的差异。长期做 Android 编码和 Agent 相关开发的话可以考虑用 Coding Plan 把这类源码阅读和验证流程固化下来减少重复搭环境的时间。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Vue el-table 行拖拽:vuedraggable 禁拖与输入冲突实践 2026/10/2 20:29:46

Vue el-table 行拖拽:vuedraggable 禁拖与输入冲突实践

拖拽排序这个交互,放在普通列表里可能三五行代码就收工了,一旦搬进 el-table,麻烦会成倍冒出来:行拖拽的 DOM 归属、拖拽后数据与视图对不上、某些单元格里还塞着输入框和可划选的文字。这篇围绕 Vue 项目里用 vuedraggable 做拖拽…

阅读更多 →
如何对Ornith-1.5-35B-A3B-GGUF做本地微调与领域适配:Unsloth生态实战指南 2026/10/2 20:29:40

如何对Ornith-1.5-35B-A3B-GGUF做本地微调与领域适配:Unsloth生态实战指南

如何对Ornith-1.5-35B-A3B-GGUF做本地微调与领域适配:Unsloth生态实战指南 【免费下载链接】Ornith-1.5-35B-A3B-GGUF 项目地址: https://ai.gitcode.com/hf_mirrors/ornith-ai/Ornith-1.5-35B-A3B-GGUF 本指南带你完成 Ornith-1.5-35B-A3B-GGUF 大模型的本…

阅读更多 →
我小红书的评论好像全都发到马来西亚去了 2026/10/2 20:29:40

我小红书的评论好像全都发到马来西亚去了

几乎全都发到马来西亚去了,所以现在只好先快刀暂乱麻

阅读更多 →
PROFINET掉站闪断响应慢?现场排查路径与发那科机器人避坑指南 2026/10/2 20:29:40

PROFINET掉站闪断响应慢?现场排查路径与发那科机器人避坑指南

“设备又掉站了”“闪断一下,整条线都停了”“PN通讯老是慢半拍”……这些词只要是搞过PROFINET的人,十有八九都听过。PROFINET作为当前工业以太网通讯的主流协议,稳定性是它的招牌,但真出了“掉站、闪断、响应慢”这类间歇性故障…

阅读更多 →
RK3588双路YOLOv5s部署:线程池调度与NPU并发实战 2026/10/2 20:29:40

RK3588双路YOLOv5s部署:线程池调度与NPU并发实战

做嵌入式AI部署的人应该都有同感:单路跑通检测只是入门,真正折磨人的是双路甚至多路视频流同时稳定运行。香橙派5这块RK3588板子,NPU算力标称6 TOPS,单路跑一个INT8量化的YOLOv5s模型,帧率轻松破百,但你要是…

阅读更多 →
电赛智能送药小车硬件设计拆解:从STM32选型到电源与传感器调试 2026/10/2 20:29:40

电赛智能送药小车硬件设计拆解:从STM32选型到电源与传感器调试

2021年电赛的智能送药小车,几乎可以说是当年控制类题目里最考验综合硬件功底的一道题。不少队伍在视觉算法上调得风生水起,最后却栽在硬件上:电机一转单片机就重启、灰度传感器数值飘得没法用、OpenMV和STM32之间通信时不时抽风。这些问题九成…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉