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 进阶知识

相关推荐
千里马学框架2 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台2 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone2 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc2 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
liangshanbo12152 天前
React 性能优化实战
性能优化·react
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas