it编程 > App开发 > Android

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

16人参与 2025-03-01 Android

功能背景

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

技术演进补充

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

分析

通常,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)来确保数据库操作不在主线程进行。

关键结论

线程行为对比表

调用方式执行线程是否阻塞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操作。

 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)
打赏 微信扫一扫 微信扫一扫

您想发表意见!!点此发布评论

推荐阅读

Android开发中gradle下载缓慢的问题级解决方法

02-27

Android仿微信聊天图片的放大缩小功能

02-24

Android监听应用前台的实现方案

02-23

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

02-21

Android自动化获取卡顿信息的实现方法

02-20

Android避免内存抖动的解决方案

02-20

猜你喜欢

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

发表评论