16人参与 • 2025-03-01 • Android
apn 数据通常存储在数据库中,由telephonyprovider提供。当用户进入apn设置界面时,activity会启动,aosp源码通过contentresolver查询apn数据。关键分析点在于这个查询操作是否在主线程执行,因为主线程上的耗时操作会导致anr。
自android 9(api 28)起,系统对telephony.carriers
表的访问增加了权限限制:
carrier privileges
或write_apn_settings
权限subscriptionmanager
处理多sim卡场景通常,android的contentprovider处理数据请求,而telephonyprovider继承自contentprovider。查询操作可能会在后台线程执行,但需要确认。例如,cursorloader会在后台线程处理查询,但如果是直接使用contentresolver.query,可能就在主线程了。
android源码中
检查apnsettingsactivity的oncreate或onresume方法,看是否触发数据加载。
q:getcontentresolver().query都是在主线程执行的吗?
a:直接调用getcontentresolver().query取决于调用线程。
根据android文档,contentresolver的query方法默认是在调用线程执行的。如果直接在主线程调用getcontentresolver().query,那么查询会在主线程执行,可能导致ui卡顿甚至anr。然而,当使用cursorloader时,内部会使用asynctask或者类似的机制,在后台线程执行查询,从而避免主线程阻塞。
cursorloader内部封装了异步查询,所以即使调用了contentresolver的query方法,也是在loader管理的后台线程中执行的,而不是主线程。
正确使用loader或现代的替代方案(如room的livedata + viewmodel)来确保数据库操作不在主线程进行。
关键结论
getcontentresolver().query()
,确实会在主线程执行数据库操作,可能导致anrcursorloader
时,系统自动在后台线程执行查询,通过handler
将结果回调到主线程线程行为对比表
调用方式 | 执行线程 | 是否阻塞ui | 推荐场景 |
---|---|---|---|
直接调用query() | 调用线程 | 可能阻塞 | 小型数据集/非ui线程调用 |
cursorloader自动执行query | asynctask线程池 | 无阻塞 | 列表数据加载等标准场景 |
原生cursorloader实现原理(简化版):
// framework简化的loader线程切换逻辑 public class cursorloader extends asynctaskloader<cursor> { @override public cursor loadinbackground() { // 在asynctask线程执行 return getcontext().getcontentresolver().query(uri, ...); } @override protected void onstartloading() { forceload(); // 触发异步加载 } }
主线程验证代码:
// 验证调用线程的测试代码 new handler(looper.getmainlooper()).post(() -> { // 在主线程执行查询 cursor cursor = getcontentresolver().query(carriers.content_uri, ...); log.d("threadtest", "mainthread query: " + (looper.mylooper() == looper.getmainlooper())); }); // 输出结果:threadtest: mainthread query: true
优化设想
用户打开界面,activity初始化loader,loadermanager启动cursorloader,cursorloader在后台线程执行查询,通过contentresolver调用telephonyprovider的query方法,最终获取apn数据并返回给主线程更新ui。
%% apn settings界面数据加载时序图 sequencediagram participant user participant apnsettingsactivity participant loadermanager participant cursorloader participant telephonyprovider participant database user->>apnsettingsactivity: 启动apn设置界面 activate apnsettingsactivity apnsettingsactivity->>loadermanager: initloader(apn_loader_id) loadermanager->>cursorloader: 创建新loader实例 activate cursorloader cursorloader->>telephonyprovider: 异步执行query() activate telephonyprovider telephonyprovider->>database: 执行sql查询 activate database database-->>telephonyprovider: 返回apn数据cursor deactivate database telephonyprovider-->>cursorloader: 返回查询结果 deactivate telephonyprovider cursorloader-->>loadermanager: 交付结果 deactivate cursorloader loadermanager->>apnsettingsactivity: onloadfinished() apnsettingsactivity->>apnsettingsactivity: 更新ui列表 deactivate apnsettingsactivity note right of cursorloader: 关键路径说明<br/>1. cursorloader自动处理后台线程<br/>2. 数据库查询在asynctask线程池执行<br/>3. 结果通过handler返回主线程
如下是优化方案的案例,但是原生逻辑并不是直接一个activity
package com.android.settings.network.apn; // apn数据库查询不会阻塞主线程,通过cursorloader机制实现 // 实际查询发生在asynctask线程(asynctask.thread_pool_executor) // 结果回调通过handler机制返回主线程 // apnsettings.java 核心逻辑 public class apnsettings extends preferenceactivity implements loadermanager.loadercallbacks<cursor> { @override protected void oncreate(bundle savedinstancestate) { getloadermanager().initloader(apn_loader_id, null, this); // 启动异步加载 } @override public loader<cursor> oncreateloader(int id, bundle args) { return new cursorloader(this, telephony.carriers.content_uri, projection, null, null, telephony.carriers.default_sort_order); } @override public void onloadfinished(loader<cursor> loader, cursor data) { madapter.swapcursor(data); // 主线程更新ui } }
以上符合android的最佳实践,即避免在主线程进行io操作。
aosp
packages/apps/settings/src/com/android/settings/network/apn/apnsettings.java
/** handle each different apn setting. */ public class apnsettings extends restrictedsettingsfragment implements preference.onpreferencechangelistener { static final string tag = "apnsettings";
到此这篇关于android 数据库查询对比(apn案例)的文章就介绍到这了,更多相关android apn数据库查询内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论