当前位置: 代码网 > it编程>App开发>Android > Android APN数据库查询对比分析(APN案例)

Android APN数据库查询对比分析(APN案例)

2025年03月01日 Android 我要评论
功能背景apn 数据通常存储在数据库中,由telephonyprovider提供。当用户进入apn设置界面时,activity会启动,aosp源码通过contentresolver查询apn数据。关键

功能背景

apn 数据通常存储在数据库中,由telephonyprovider提供。当用户进入apn设置界面时,activity会启动,aosp源码通过contentresolver查询apn数据。关键分析点在于这个查询操作是否在主线程执行,因为主线程上的耗时操作会导致anr。

技术演进补充

自android 9(api 28)起,系统对telephony.carriers表的访问增加了权限限制:

  • 需要carrier privilegeswrite_apn_settings权限
  • 查询结果可能返回空cursor,需配合subscriptionmanager处理多sim卡场景

分析

通常,android的contentprovider处理数据请求,而telephonyprovider继承自contentprovider。查询操作可能会在后台线程执行,但需要确认。例如,cursorloader会在后台线程处理查询,但如果是直接使用contentresolver.query,可能就在主线程了。

android源码中

  • 使用loadermanager来异步加载数据,这样查询会在后台进行。
  • 如果用户使用的是直接查询而没有异步处理,那么主线程可能被阻塞。

排查建议

检查apnsettingsactivity的oncreate或onresume方法,看是否触发数据加载。

  • 使用loadermanager.initloader来启动cursorloader,这样查询会在后台线程执行。
  • 否则,如果直接在主线程调用getcontentresolver().query,就会在主线程执行数据库操作。

疑问

q:getcontentresolver().query都是在主线程执行的吗?

a:直接调用getcontentresolver().query取决于调用线程。

根据android文档,contentresolver的query方法默认是在调用线程执行的。如果直接在主线程调用getcontentresolver().query,那么查询会在主线程执行,可能导致ui卡顿甚至anr。然而,当使用cursorloader时,内部会使用asynctask或者类似的机制,在后台线程执行查询,从而避免主线程阻塞。

cursorloader内部封装了异步查询,所以即使调用了contentresolver的query方法,也是在loader管理的后台线程中执行的,而不是主线程。

正确使用loader或现代的替代方案(如room的livedata + viewmodel)来确保数据库操作不在主线程进行。

关键结论

  • 直接调用:若在activity/fragment主线程中直接调用getcontentresolver().query()确实会在主线程执行数据库操作,可能导致anr
  • 通过loader机制:当使用cursorloader时,系统自动在后台线程执行查询,通过handler将结果回调到主线程

线程行为对比表

调用方式执行线程是否阻塞ui推荐场景
直接调用query()调用线程可能阻塞小型数据集/非ui线程调用
cursorloader自动执行queryasynctask线程池无阻塞列表数据加载等标准场景

对疑问的代码执行流程验证

原生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操作。

  • apnsettingsactivity使用了loadermanager来初始化cursorloader。
  • 在oncreateloader方法中创建了cursorloader实例,参数包括contentprovider的uri和查询参数。
  • 当loadermanager启动加载时,cursorloader会在后台线程执行查询,完成后再通过onloadfinished回调主线程更新ui。

 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数据库查询内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com