Android SurfaceFlinger——动画进程销毁(十七)

news2024/11/18 14:31:00

        在动画播放完成后,对动画相关资源释放的同时还需要销毁动画进程。这里我们就来分析一下动画进程的销毁流程。

一、动画进程销毁

        动画进程的销毁一般是在桌面进程准备显示的时候,而桌面准备显示是在桌面 Activity 的 Resume 生命周期,我们来看一下。

1、ActivityThread

源码位置:/frameworks/base/core/java/android/app/ActivityThread.java

@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
            boolean isForward, String reason) {
    ……
    Looper.myQueue().addIdleHandler(new Idler());
}

        在执行完 Resume 之后,会在当前线程的消息队列(Looper)中添加一个空闲处理器( IdleHandler),让进程空闲执行,当消息队列中暂时没有待处理的消息时,系统会调用这个空闲处理器的回调方法,允许执行一些轻量级的任务或者进行资源清理等工作。

private class Idler implements MessageQueue.IdleHandler {
    @Override
    public final boolean queueIdle() { 执行空闲时需要处理的逻辑
        ActivityClientRecord a = mNewActivities;
        boolean stopProfiling = false;
        ……    
        if (a != null) {
            mNewActivities = null;
            final ActivityClient ac = ActivityClient.getInstance();
            ActivityClientRecord prev;
            // 处理Activity空闲逻辑
            do {
                if (a.activity != null && !a.activity.mFinished) {
                    ac.activityIdle(a.token, a.createdConfig, stopProfiling);
                    a.createdConfig = null;
                }
                prev = a;
                a = a.nextIdle;
                prev.nextIdle = null;
            } while (a != null);
        }
        if (stopProfiling) {
            // 停止性能分析器管理
            mProfiler.stopProfiling();
        }
        return false;
    }
}

        这里在 Activity 空闲时,通知系统该 Activity 已经进入空闲状态,并清除其创建时的配置信息。

2、ActivityClient

源码位置:/frameworks/base/core/java/android/app/ActivityClient.java

/** 报告活动恢复后主线程处于空闲状态*/
public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
    try {
        getActivityClientController().activityIdle(token, config, stopProfiling);
    } catch (RemoteException e) {
        e.rethrowFromSystemServer();
    }
}

private static IActivityClientController getActivityClientController() {
    final IActivityClientController controller = INTERFACE_SINGLETON.mKnownInstance;
    return controller != null ? controller : INTERFACE_SINGLETON.get();
}

3、ActivityClientController

源码位置:/frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java

private final ActivityTaskSupervisor mTaskSupervisor;

@Override
public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
    final long origId = Binder.clearCallingIdentity();
    try {
        synchronized (mGlobalLock) {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
            final ActivityRecord r = ActivityRecord.forTokenLocked(token);
            if (r == null) {
                return;
            }
            mTaskSupervisor.activityIdleInternal(r, false /* fromTimeout */, false /* processPausingActivities */, config);
            if (stopProfiling && r.hasProcess()) {
                r.app.clearProfilerIfNeeded();
            }
        }
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        Binder.restoreCallingIdentity(origId);
    }
}

        该函数位于 ActivityManagerService 相关服务类中,负责处理 Activity 进入空闲状态时的一系列操作。调用 ActivityTaskSupervisor 协调执行必要的生命周期管理、资源回收以及性能监控的清理工作。

4、ActivityTaskSupervisor 

源码位置:/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

void activityIdleInternal(ActivityRecord r, boolean fromTimeout, boolean processPausingActivities, Configuration config) {
    if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + r);

    if (r != null) {
        mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
        r.finishLaunchTickingLocked();
        if (fromTimeout) {
            // 结束Activity启动时的计时监控
            reportActivityLaunched(fromTimeout, r, INVALID_DELAY, -1 /* launchState */);
        }

        // 配置更新
        if (config != null) {
            r.setLastReportedGlobalConfiguration(config);
        }

        // 标记空闲状态
        r.idle = true;

        // 判断系统是否正在启动(isBooting())且所有活动都已空闲
        if ((mService.isBooting() && mRootWindowContainer.allResumedActivitiesIdle()) || fromTimeout) {
            // 检查并完成系统的启动流程
            checkFinishBootingLocked();
        }

        // 重启动作标志清除
        r.mRelaunchReason = RELAUNCH_REASON_NONE;
    }
    ……
    // 后台应用的资源回收操作
    mService.mH.post(() -> mService.mAmInternal.trimApplications());
}

        该方法不仅标记了 Activity 的空闲状态,还执行了一系列系统维护操作,其中的checkFinishBootingLocked() 方法就会检测 BootAnimation 是否关闭。

checkFinishBootingLocked

final ActivityTaskManagerService mService;

private void checkFinishBootingLocked() {
    // 获取当前启动状态
    final boolean booting = mService.isBooting();
    boolean enableScreen = false;
    // 设置启动状态为已完成
    mService.setBooting(false);
    if (!mService.isBooted()) {
        // 标记系统已启动
        mService.setBooted(true);
        // 标记启用屏幕
        enableScreen = true;
    }
    // 调度完成启动的后续操作
    if (booting || enableScreen) {
        mService.postFinishBooting(booting, enableScreen);
    }
}

        这里就是判断并设置全局标志位,最终调用到 ATMS 中的 postFinishBooting() 方法处理启动完成后的收尾工作。

5、ActivityTaskManagerService

源码位置:/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

final ActivityTaskManagerInternal mInternal;

void postFinishBooting(boolean finishBooting, boolean enableScreen) {
    mH.post(() -> {
        if (finishBooting) {
            // 调用AMS中的对应方法,继续启动完成的收尾工作
            mAmInternal.finishBooting();
        }
        if (enableScreen) {
            // 启用屏幕
            mInternal.enableScreenAfterBoot(isBooted());
        }
    });
}

        我们接着看一下  enableScreenAfterBoot() 函数,调用自身对应方法。

enableScreenAfterBoot

WindowManagerService mWindowManager;

@Override
public void enableScreenAfterBoot(boolean booted) {
    writeBootProgressEnableScreen(SystemClock.uptimeMillis());
    mWindowManager.enableScreenAfterBoot();
    synchronized (mGlobalLock) {
        updateEventDispatchingLocked(booted);
    }
}

        此时就会走到 WMS 中的 enableScreenAfterBoot() 方法。WMS 作为窗体管理服务,肯定有渲染相关的服务在里面。

6、WindowManagerService

源码位置:/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public void enableScreenAfterBoot() {
    synchronized (mGlobalLock) {
        ……
        if (mSystemBooted) {
            return;
        }
        mSystemBooted = true;
        // 隐藏显示在屏幕上的启动消息或动画
        hideBootMessagesLocked();
        // 如果30秒后屏幕仍然没有出现,放弃并打开它
        mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
    }
    // 通知策略服务系统已启动
    mPolicy.systemBooted();
    // 执行屏幕启用操作
    performEnableScreen();
}

        该方法主要用于在系统启动后启用屏幕显示。

performEnableScreen

private void performEnableScreen() {
    synchronized (mGlobalLock) {
        ……
        try {
            IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
            if (surfaceFlinger != null) {
                ProtoLog.i(WM_ERROR, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");
                Parcel data = Parcel.obtain();
                data.writeInterfaceToken("android.ui.ISurfaceComposer");
                surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                        data, null, 0);
                data.recycle();
            }
        } catch (RemoteException ex) {
            ……
        }
        ……
    }

    try {
        mActivityManager.bootAnimationComplete();
    } catch (RemoteException e) {
    }
    ……
}

        关键是通信到 SurfaceFlinger 中,传入了 FIRST_CALL_TRANSACTION 命令,而这个命令实际上就是 BOOT_FINISHED。

ISurfaceComposer.h

源码位置:/frameworks/native/libs/gui/include/gui/ISurfaceComposer.h

BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION

        接下来我们看一下 SurfaceFlinger 中对于 BOOT_FINISHED 消息做了什么处理。这里使用的是 Binder 通信,实际的消息处理是在基类 ISurfaceComposer 中。

7、ISurfaceComposer.cpp

源码位置:/frameworks/native/libs/gui/ISurfaceComposer.cpp

case BOOT_FINISHED: {
    CHECK_INTERFACE(ISurfaceComposer, data, reply);
    bootFinished();
    return NO_ERROR;
}

        其实这里调用的就是 SurfaceFlinger 中的 bootFinished() 方法。 

8、SurfaceFlinger.cpp

源码位置:/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::bootFinished() {
    ……
    // 等待启动属性设置线程结束
    if (mStartPropertySetThread->join() != NO_ERROR) {
        ALOGE("Join StartPropertySetThread failed!");
    }

    ……
    // 链接WMS服务的死亡监听
    const String16 name("window");
    mWindowManager = defaultServiceManager()->getService(name);
    if (mWindowManager != 0) {
        mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
    }

    // 停止启动动画
    // 这种方式比直接杀死进程更优雅,因为启动动画进程可以根据这个信号选择动画停止的位置。
    property_set("service.bootanim.exit", "1");

    // 记录停止启动动画的日志事件
    const int LOGTAG_SF_STOP_BOOTANIM = 60110;
    LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
    ……
}

        这里 SurfaceFlinger 绑定了对 WMS 的 Binder 死亡代理。不过关键还是设置了 service.bootanim.exit 这个全局属性。而这个属性刚好就是 BootAnimation 在 checkExit 方法中不断循环检测。

9、BootAnimation

源码位置: /frameworks/base/cmds/bootanimation/BootAnimation.cpp

void BootAnimation::checkExit() {
    // 检测是否应该结束启动动画过程
    char value[PROPERTY_VALUE_MAX];
    property_get(EXIT_PROP_NAME, value, "0");
    int exitnow = atoi(value);
    if (exitnow) {
        requestExit();
    }
}

        requestExit 其实就是退出该 BootAnimation 线程的 threadLoop 方法,这样整个 main 方法就不会阻塞住,就会一直运行整个 main 到底结束整个进程。

总结

        这里用一幅图总结系统启动动画的整个流程:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1867624.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

江科大—读写内部闪存FLASH读取芯片ID

读写内部闪存FLASH 右下角是OLED&#xff0c;然后左上角在PB1和PB11两个引脚&#xff0c;插上两个按键用于控制。下一个代码读取芯片ID&#xff0c;这个也是接上一个OLED&#xff0c;能显示测试数据就可以了。 STM32-STLINK Utility 本节的代码调试&#xff0c;使用辅助软件…

Sparse4Dv2

Sparse4D: Multi-view 3D Object Detection with Sparse Spatial-Temporal Fusion 相关内容&#xff1a;总览&#xff0c;Sparse4D v1&#xff0c;Sparse4D v3&#xff0c; 单位&#xff1a;地平线(Sparse4D v1 原班人马) GitHub&#xff1a;https://github.com/HorizonRobo…

计算机网络之数据通信原理(中)

上节内容传送口&#xff1a;数据通信原理基础 1.数据传输方式 1.1并行传输 并行传输: 字符编码的各个比特同时传输 特点&#xff1a; 一个比特时间内可传输一个字符&#xff0c;传输速度快&#xff0c;每个比特传输要求一个单独的信道支持&#xff0c;通信成本高&#xf…

基于单片机和 Arduino 平台的六自由度可控机械手臂

摘 要 : 为了降低机械手臂的设计开发难度 &#xff0c; 并使之尽早地投入应用 &#xff0c; 设计一种基于单片机和 Arduino 平台的六自由度可控机械手臂 。提出六自由度可控机械手臂的控制方案&#xff0c; 给出机械手臂控制系统的结构框图 。 详细设计六自由度可控机械手臂…

《UDS协议从入门到精通》系列——图解0x35:请求上传

《UDS协议从入门到精通》系列——图解0x35&#xff1a;请求上传 一、简介二、数据包格式2.1 服务请求格式2.2 服务响应格式2.2.1 肯定响应2.2.2 否定响应 三、通信示例 Tip&#x1f4cc;&#xff1a;本文描述中但凡涉及到其他UDS服务的&#xff0c;将陆续提供链接跳转方式以便快…

Power BI可视化表格矩阵如何保持样式导出数据?

故事背景&#xff1a; 有朋友留言询问&#xff1a;自己从Power BI可视化矩阵表格中导出数据时&#xff0c;导出的表格样式会发生改变&#xff0c;需要线下再手动调整&#xff0c;重新进行透视组合成自己想要的格式。 有没有什么办法让表格导出来跟可视化一样&#xff1f; Po…

pd虚拟机 Parallels Desktop 19 for Mac 破解版小白安装使用指南

Parallels Desktop 19 for Mac 乃是一款适配于 Mac 的虚拟化软件。它能让您在 Mac 计算机上同时运行多个操作系统。您可借此创建虚拟机&#xff0c;并于其中装设不同的操作系统&#xff0c;如 Windows、Linux 或 macOS。使用 Parallels Desktop 19 mac 版时&#xff0c;您可在 …

编译正则表达式模式re.compile

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 编译正则表达式模式 re.compile [太阳]选择题 根据给定的Python代码&#xff0c;哪个选项是正确的&#xff1f; import re pattern re.compile(r\d) print(f"【显示】pattern{patter…

eBPF 如何塑造 Linux 和平台工程的未来

当Docker 于 2013 年突然出现时&#xff0c;Linux 容器似乎一夜成名。但容器&#xff08;以及微服务和Kubernetes&#xff09;的演变实际上是基于 Linux 操作系统中的内核原语而进行的&#xff0c;历时数十年。 Docker 使用这些原语&#xff08;即 cgroups 和命名空间&#xf…

前端框架中的前端安全性(Front-end Security)

聚沙成塔每天进步一点点 本文回顾 ⭐ 专栏简介前端框架中的前端安全性&#xff08;Front-end Security&#xff09;1. 引言2. 常见的前端安全威胁2.1 跨站脚本攻击&#xff08;XSS&#xff09;2.1.1 防御措施 2.2 跨站请求伪造&#xff08;CSRF&#xff09;2.2.1 防御措施 2.3 …

YOLOv8改进 | 添加注意力篇 | 结合Mamba注意力机制MLLA助力YOLOv8有效涨点(全网独家首发)

一、本文介绍 本文给大家带来的改进机制是结合号称超越Transformer架构的Mamba架构的最新注意力机制MLLA&#xff0c;本文将其和我们YOLOv8进行结合&#xff0c;MLLA&#xff08;Mamba-Like Linear Attention&#xff09;的原理是通过将Mamba模型的一些核心设计融入线性注意力…

【毛毛虫案例-拖拽 Objective-C语言】

一、这个毛毛虫案例啊,是这个样子的, 1.首先,你这个脑袋,这个蓝色的脑袋,它是可以拽起来的, 下面的红色球,一个一个中心点之间,相互去附着, 其他的红色球,是拖不起来的, 只有这个蓝色的东西,可以拽起来,这个蓝色的View,还有重力, 这个蓝色的View,我在拖动它…

Spring底层原理之proxyBeanMenthod实例 动态代理 反射 Bean的拦截

proxyBeanMenthod 假设我们要进行一个系统的二次开发 然后第一次开发我们实用的是XML声明bean 二次开发的时候要用注解 我们如何把bean都加载上来呢 我们首先创建一个全新的配置类 package com.bigdata1421.config;public class SpringConfig32 { } 我们创建一个APP 加载…

R语言数据分析案例37-旅游景点聚类分析

一、研究背景 近年来&#xff0c;随着旅游业的迅猛发展&#xff0c;旅游景点的竞争日益激烈。如何在众多景点中脱颖而出&#xff0c;吸引更多游客&#xff0c;成为各大景点管理者关注的焦点。通过对旅游景点进行深入的数据分析&#xff0c;可以帮助管理者更好地了解景点的优势…

HashMap第5讲——resize方法扩容源码分析及细节

put方法的源码和相关的细节已经介绍完了&#xff0c;下面我们进入扩容功能的讲解。 一、为什么需要扩容 这个也比较好理解。假设现在HashMap里的元素已经很多了&#xff0c;但是链化比较严重&#xff0c;即便树化了&#xff0c;查询效率也是O(logN)&#xff0c;肯定没有O(1)好…

最新MDYS14源码影视视频网站模板/苹果CMS系统/附搭建教程

最新MDYS14源码影视视频网站模板/苹果CMS系统/附搭建教程 基本介绍&#xff1a; 1、后台增加自定义参数&#xff0c;对应会员升级页面&#xff0c;以及积分充值 2、视频&#xff0c;演员&#xff0c;专题&#xff0c;收藏&#xff0c;会员系统模块齐全&#xff0c;支持子分类…

本地读取classNames txt文件

通过本地读取classNames,来减少程序修改代码,提高了程序的拓展性和自定义化。 步骤: 1、输入本地路径,分割字符串。 2、将className按顺序放入vector容器中。 3、将vector赋值给classNmaes;获取classNames.size(),赋值给CLASSES;这样,类别个数和类别都已经赋值完成。…

阀门盘根的介绍

盘根&#xff08;编制盘根&#xff09;&#xff08;packing&#xff09;也叫密封填料&#xff0c;通常由较柔软的线状物编织而成&#xff0c;通常截面积是正方形或长方形、圆形的条状物填充在密封腔体内,从而实现密封。填料密封最早是以棉麻等纤维塞在泄漏通道内来阻止液流泄漏…

牛客挑战赛75 D. 不存在的玩家(sg图dp)

题目 思路来源 灵茶山群群友 https://blog.csdn.net/Code92007/article/details/110354429 题解 其实想了想&#xff0c;和20年小米邀请赛决赛这个G题的dp思路是一样的&#xff0c;姑且称为sg图dp吧 按sg值从大到小dp&#xff0c;每次补上全局sg值-1的这些点&#xff0c; …

XML简介XML 使用教程XML的基本结构XML的使用场景

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中……&#xff09; 4、牛逼哄哄的 IDEA编程利器技巧(编写中……&#xff09; 5、面经吐血整理的 面试技…