ANR系列之ContentProvider ANR原理

成体系学习ANR系列,代码是基于 Android 15。如果你想成体系学习 ANR,可以关注并点击文章下面的合集

ContentProvider Timeout是位于ActivityManager线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。ContentProvider 超时为CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s.

ContentProvider的 ANR 检测跟 Service 不太一样,他是在每次应用启动进程启动时进行的检测。

埋炸弹

埋炸弹的过程其实是在进程创建的过程,进程创建后会调用attachApplicationLocked() 进入system_server进程的ActivityManagerService中:

ini 复制代码
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
    ProcessRecord app;
    if (pid != MY_PID && pid >= 0) {
        synchronized (mPidsSelfLocked) {
            app = mPidsSelfLocked.get(pid); // 根据pid获取ProcessRecord
        }
    } 
        ...

    //系统处于ready状态或者该app为FLAG_PERSISTENT进程则为true
    boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
    // APP 是否有Providers
    List<ProviderInfo> providers = normalMode ?
                generateApplicationProvidersLocked(app) : null;

    //app进程是否是新创建,则发送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG=10
    //的超时检测消息
    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
    }
    // 通知 ActivityThread 的 bindApplication
    thread.bindApplication(...);
    ...
}
arduino 复制代码
// 10s
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;

阶段总结下上面的代码逻辑

  1. 应用进程启动时,ActivityManagerService会检测当前的应用是否有声明ContentProvider
  2. 检测当前的应用进程是否是新建,是否有需要被启动的ContentProvider
  3. 如果有ContentProvider类型的任务,则开始抛出一个CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000 10s 的延时消息,这个消息会出发 ANR 检测,相当于是埋雷阶段

拆炸弹

我们知道应用进程在启动时候,调用installContentProviders安装对应的 Providers,如下

java 复制代码
    @UnsupportedAppUsage
    private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<ContentProviderHolder> results = new ArrayList<>();

        for (ProviderInfo cpi : providers) {
            ......
            // 安装 provider
            ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            // 通知 AMS 安装成功
            ActivityManager.getService().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }

ContentProvider安装 install 成功后,就会通过 publishContentProviders通知 AMS 拆除ANR 检测炸弹,如下是 AMS 中的publishContentProviders方法:

java 复制代码
    @Override
    public final void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) {
        ......
        ......
        try {
            mCpHelper.publishContentProviders(caller, providers);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

在 Android 15 上把 publish 的逻辑都封装到了ContentProviderHelper 类中

ini 复制代码
    void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {

        synchronized (mService) {
            final ProcessRecord r = mService.getRecordForAppLOSP(caller);
            .......
            
            boolean providersPublished = false;
            for (int i = 0, size = providers.size(); i < size; i++) {
                ContentProviderHolder src = providers.get(i);
                ......
              
                // 是否publish 成功
                boolean wasInLaunchingProviders = false;
                for (int j = 0, numLaunching = mLaunchingProviders.size(); j < numLaunching; j++) {
                    if (mLaunchingProviders.get(j) == dst) {
                        // 将该provider移除mLaunchingProviders队列
                        mLaunchingProviders.remove(j);
                        wasInLaunchingProviders = true;
                        j--;
                        numLaunching--;
                    }
                }
                // 成功pubish则移除该消息
                if (wasInLaunchingProviders) {
                    mService.mHandler.removeMessages(
                            ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG, dst);
                    // 移除ANR 检测任务
                    mService.mHandler.removeMessages(
                            ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
                }
                ......
            }
            ......
        }
    }

上面代码阶段总结下

  1. 应用进程在 bindApplication 阶段会现调用installContentProviders安装对应的 Providers
  2. 安装完成后,应用进程通过调用 AMS的publishContentProviders通知 AMS 安装完成,并移除 ANR 检测任务,也就是拆除炸弹

引爆炸弹

上面说了正常的流程是那样的,但是如果不正常呢?也就是炸弹没有被拆除,那怎么搞呢?我们看下 ASM 中如果接收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG. 时,会干啥。其中MainHandlerAMS 的内部类

java 复制代码
final class MainHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
                ...
                ProcessRecord app = (ProcessRecord)msg.obj;
                synchronized (ActivityManagerService.this) {
                    // 处理ContentProvider ANR 的情况
                    mCpHelper.processContentProviderPublishTimedOutLocked(app);
                }
            } break;
                ...
        }
        ...
    }
}

继续看ContentProviderHelper 类中的processContentProviderPublishTimedOutLocked方法:

typescript 复制代码
    @GuardedBy("mService")
    void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
        // 移除一些Provider 相关的东西
        cleanupAppInLaunchingProvidersLocked(app, true);
        // 处理 ANR
        mService.mProcessList.removeProcessLocked(app, false, true,
                ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
                ApplicationExitInfo.SUBREASON_UNKNOWN,
                "timeout publishing content providers");
    }

阶段总结下:

  1. 如果没有及时移除CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG表示发生了 ANR,AMS 会执行processContentProviderPublishTimedOutLocked方法
  2. 清除处理一些和ContentProvider相关的逻辑
  3. 杀死应用进程
  4. 注意:这里不像其他的类型 ANR,发生 ANR 后,并不会调用appNotResponding()

关注 "代码和生活手记" 系列学习 Android 进阶知识

相关推荐
weixin_443566982 小时前
网页的性能优化
性能优化
矛取矛求6 小时前
苹果与安卓,鸿蒙下跨设备,应用分享
android·华为·harmonyos
QING6188 小时前
Android 动态权限——添加拦截器
android·kotlin·app
QING6188 小时前
Kotlin 委托与扩展函数——新手入门
android·kotlin·app
QING6188 小时前
Android 动态权限封装——新手入门使用指南
android·kotlin·app
tangweiguo030519879 小时前
Android Kotlin 中使用 MPAndroidChart 绘制优雅的曲线图:封装与优化实践
android·kotlin
闲倚一枝藤9 小时前
海思烧录工具HITool电视盒子刷机详解
android·电视盒子·智能电视·电视机·tv
郭涤生10 小时前
第10章:优化数据结构_《C++性能优化指南》notes
数据结构·c++·笔记·性能优化
氦客10 小时前
kotlin知识体系(四) : inline、noinline、crossinline 关键字对应编译后的代码是怎样的 ?
android·开发语言·kotlin·inline·noinline·crossinline·编译后的代码
89315196011 小时前
Android开发BasePagerAdapter
android·android开发·viewpager·android教程·viewpageradapte