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

 更新时间:2025年03月01日 09:27:12   作者:Dic-  
文章详细介绍了Android中APN数据查询的实现方式,文章说明了如何避免在主线程进行IO操作,从而提高应用的响应性和用户体验,感兴趣的朋友一起看看吧

功能背景

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界面数据优化加载时序图
%% 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数据库查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Android实现用代码简单安装和卸载APK的方法

    Android实现用代码简单安装和卸载APK的方法

    这篇文章主要介绍了Android实现用代码简单安装和卸载APK的方法,涉及Android针对APK文件及package的相关操作技巧,需要的朋友可以参考下
    2016-08-08
  • Android布局耗时监测的三种实现方式

    Android布局耗时监测的三种实现方式

    在Android应用开发中,性能优化是一个至关重要的方面,为了更好地监测布局渲染的耗时,我们需要一种可靠的实现方案,本文将介绍三种针对Android布局耗时监测的实现方案,帮助开发者及时发现并解决布局性能问题,需要的朋友可以参考下
    2024-03-03
  • Android进阶教程之ViewGroup自定义布局

    Android进阶教程之ViewGroup自定义布局

    这篇文章主要给大家介绍了关于Android进阶教程之ViewGroup自定义布局的相关资料,文中通过示例代码介绍的非常详细,对各位Android开发者们具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • Android实现广告图片轮播效果

    Android实现广告图片轮播效果

    这篇文章主要为大家详细介绍了Android实现广告图片轮播效果的相关资料,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-02-02
  • android九宫格锁屏控件使用详解

    android九宫格锁屏控件使用详解

    这篇文章主要为大家详细介绍了android九宫格锁屏控件使用,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • 详解Android用Shape制作单边框图的两种思路和坑

    详解Android用Shape制作单边框图的两种思路和坑

    这篇文章主要介绍了详解Android用Shape制作单边框图的两种思路和坑,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • Android RecycleView和线型布局制作聊天布局

    Android RecycleView和线型布局制作聊天布局

    大家好,本篇文章主要讲的是Android RecycleView和线型布局制作聊天布局,感兴趣的同学赶紧来看一看吧,对你有帮助的话记得收藏一下
    2022-01-01
  • Android的Launcher启动器中添加快捷方式及小部件实例

    Android的Launcher启动器中添加快捷方式及小部件实例

    这篇文章主要介绍了在Android的Launcher启动器中添加快捷方式及窗口小部件的方法,包括在自己的应用程序中添加窗口小部件AppWidget的例子,需要的朋友可以参考下
    2016-02-02
  • Flutter使用Overlay与ColorFiltered新手引导实现示例

    Flutter使用Overlay与ColorFiltered新手引导实现示例

    这篇文章主要介绍了Flutter使用Overlay与ColorFiltered新手引导实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 解析android创建快捷方式会启动两个应用的问题

    解析android创建快捷方式会启动两个应用的问题

    本篇文章是对关于android创建快捷方式会启动两个应用的问题进行了详细的分析介绍,需要的朋友参考下
    2013-06-06

最新评论