android.anim和android.anim.lf两个线程解析

Systrace4 SystemServer 解读 一 概述 本文是 Systrace 系列文章的第四篇,主要是对 SystemServer 进行简单介绍,介绍了 SystemServer 中几个比较重要的线程,由于 Input 和 Binder 比较重要,所以单独拿出来讲,在这里就没有再涉及到。 二 窗口动画 Systrace 中的 SystemServer 一个比较重要的地方就是窗口动画,由于窗口归 SystemServer 来管,那么窗口动画也就是由 SystemServer 来进行统一的处理,其中涉及到两个比较重要的线程:Android.Anim 和 阅读详情
AI摘要

本文解答了Android系统中anim和anim.lf线程的区别,介绍了AnimationThread负责旧版窗口动画和启动窗口操作,而SurfaceAnimationThread负责新版本的流畅动画,无需windowmanager锁。文章还提供了源码注释和perfetto/systrace分析作为参考。

背景:

在这里插入图片描述

在学员群中有学员朋友在分析systemserver的systrace和perfetto时候,提出了关于android.anim和android.anim.lf两个线程的相关疑问,主要是android.anim和android.anim.lf两个线程分别用于什么,有啥区别。针对这个疑问其实比较多学员朋友同样也想要知道答案,这里干脆写个文章来解答,分别日后学员朋友好查询,不需要挨个回答。

源码注释解答

首先看一下关于这里两个线程源码寻找方式:

test@test:~/aosp/frameworks/base$ grep "\"android.anim" ./ -rn | grep java
./tools/preload-check/src/com/android/preload/check/PreloadCheck.java:68:        run("com.android.preload.check.Initialized", "android.animation.Animator");
./services/core/java/com/android/server/AnimationThread.java:35:        super("android.anim", THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
./services/core/java/com/android/server/wm/SurfaceAnimationThread.java:35:        super("android.anim.lf", THREAD_PRIORITY_DISPLAY, false /*allowIo*/);

可以看到./services/core/java/com/android/server/AnimationThread.java这个类就是代表的"android.anim"线程
./services/core/java/com/android/server/wm/SurfaceAnimationThread.java代表就是"android.anim.lf"线程
看看对应类的注释:

AnimationThread类

/**
 * Thread for handling all legacy window animations, or anything that's directly impacting
 * animations like starting windows or traversals.
 */
public final class AnimationThread extends ServiceThread {
    private static AnimationThread sInstance;
    private static Handler sHandler;

    private AnimationThread() {
        super("android.anim", THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
    }

注释说明:
AnimationThread主要用于处理一些遗留的窗口动画,货值其他直接会影响动画的,比如启动窗口,或者遍历树等

SurfaceAnimationThread类

/**
 * Thread for running {@link SurfaceAnimationRunner} that does not hold the window manager lock.
 */
public final class SurfaceAnimationThread extends ServiceThread {
    private static SurfaceAnimationThread sInstance;
    private static Handler sHandler;

    private SurfaceAnimationThread() {
        super("android.anim.lf", THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
    }

注释说明:
这个其实就是新版本窗口动画流畅更新主要线程,也课程也讲解过的引入leash这种图层进行动画,不需要在持有 window manager lock的那一把大锁,可以直接对SurfaceControl的图层进行动画操作。所以更加流畅的核心就是不用再持有windowmanagerservice的大锁,所以独立出来了单独的动画线程来执行,减轻android.anim的负担

总结:
其实上面的注释也讲的比较清楚了,核心就是为了动画更加流畅,改进成了动画不需要在单独依赖windowmanagerservice的大锁,所以独立出来了一个单独的动画独立线程android.anim.lf。

这里可能学员朋友又有疑问,窗口动画都被搞到了线程android.anim.lf,那原来的线程android.anim岂不是架空了?android.anim还干些啥?
这个疑问同样AnimationThread的注释也已经说明了,主要干一些遗留窗口动画,还有启动窗口,遍历窗口等工作,具体也可以看一下相关的perfetto

android.anim的perfetto/systrace

trace可以看出android.anim会执行Traverser相关的任务
在这里插入图片描述

代码可以看这里

    void requestTraversal() {
        if (mTraversalScheduled) {
            return;
        }

        // Set as scheduled even the request will be deferred because mDeferredRequests is also
        // increased, then the end of deferring will perform the request.
        mTraversalScheduled = true;
        if (mDeferDepth > 0) {
            mDeferredRequests++;
            if (DEBUG) Slog.i(TAG, "Defer requestTraversal " + Debug.getCallers(3));
            return;
        }
        mService.mAnimationHandler.post(mPerformSurfacePlacement);
    }

当然也有很多地方会触发相关的animate,但是这个animate相关的活,和“动画”关系已经不那么大

可以看到这里的scheduleAnimation注释:

  /**
     * Trigger a call to prepareSurfaces from the animation thread, such that pending transactions
     * will be applied.
     */
    void scheduleAnimation() {
        if (mParent != null) {
            mParent.scheduleAnimation();
        }
    }

这里主要是来触发prepareSrufaces这个方法,让一些pending的事物进行apply

相关的animate方法如下:

private void animate(long frameTimeNs, long vsyncId) {
        scheduleAnimation();

        final RootWindowContainer root = mService.mRoot;
        mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
        mBulkUpdateParams = 0;
        root.mOrientationChangeComplete = true;
        if (DEBUG_WINDOW_TRACE) {
            Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
        }

        ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
        mService.openSurfaceTransaction();
        try {
            // Remove all deferred displays, tasks, and activities.
            root.handleCompleteDeferredRemoval();

            final AccessibilityController accessibilityController =
                    mService.mAccessibilityController;
            final int numDisplays = mDisplayContentsAnimators.size();
            for (int i = 0; i < numDisplays; i++) {
                final int displayId = mDisplayContentsAnimators.keyAt(i);
                final DisplayContent dc = root.getDisplayContent(displayId);
                // Update animations of all applications, including those associated with
                // exiting/removed apps.
                dc.updateWindowsForAnimator();
                dc.prepareSurfaces();
            }

            for (int i = 0; i < numDisplays; i++) {
                final int displayId = mDisplayContentsAnimators.keyAt(i);
                final DisplayContent dc = root.getDisplayContent(displayId);

                dc.checkAppWindowsReadyToShow();
                if (accessibilityController.hasCallbacks()) {
                    accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId,
                            mTransaction);
                }
            }

            cancelAnimation();

            if (mService.mWatermark != null) {
                mService.mWatermark.drawIfNeeded();
            }

        } catch (RuntimeException e) {
            Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
        }

        final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
        final boolean doRequest = (mBulkUpdateParams != 0 || root.mOrientationChangeComplete)
                && root.copyAnimToLayoutParams();
        if (hasPendingLayoutChanges || doRequest) {
            mService.mWindowPlacerLocked.requestTraversal();
        }

        final boolean rootAnimating = root.isAnimating(TRANSITION | CHILDREN /* flags */,
                ANIMATION_TYPE_ALL /* typesToCheck */);
        if (rootAnimating && !mLastRootAnimating) {
            Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
        }
        if (!rootAnimating && mLastRootAnimating) {
            mService.mWindowPlacerLocked.requestTraversal();
            Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
        }
        mLastRootAnimating = rootAnimating;

        final boolean runningExpensiveAnimations =
                root.isAnimating(TRANSITION | CHILDREN /* flags */,
                        ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
                                | ANIMATION_TYPE_RECENTS /* typesToCheck */);
        if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
            // Usually app transitions put quite a load onto the system already (with all the things
            // happening in app), so pause task snapshot persisting to not increase the load.
            mService.mTaskSnapshotController.setPersisterPaused(true);
            mTransaction.setEarlyWakeupStart();
        } else if (!runningExpensiveAnimations && mRunningExpensiveAnimations) {
            mService.mTaskSnapshotController.setPersisterPaused(false);
            mTransaction.setEarlyWakeupEnd();
        }
        mRunningExpensiveAnimations = runningExpensiveAnimations;

        SurfaceControl.mergeToGlobalTransaction(mTransaction);
        mService.closeSurfaceTransaction("WindowAnimator");
        ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");

        if (mRemoveReplacedWindows) {
            root.removeReplacedWindows();
            mRemoveReplacedWindows = false;
        }

        mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
        executeAfterPrepareSurfacesRunnables();
    }

相关的trace:
在这里插入图片描述

更多framework实战干货,请关注下面“千里马学框架”

Android R DisplayManagerService模块(1) 启动,2021年你与字节跳动只差这份笔记 DisplayManagerService.CallbackRecord: DMS内部类,内部持有IDisplayManagerCallback对象,负责执行IDisplayManagerCallback的回调; DisplayManager: 暴露给App层的接口,内部持有DisplayManagerGlobal对象引用,利用DisplayManagerGlobal和DMS进行交互; IDisplayManagerCallback:一个IBinder接口,DMS中发生display相关动作后,会通过该接. 阅读详情

相关推荐

Android卡顿掉帧问题分析之工具篇

从上面的分析可以看到,谷歌原生在Android系统的很多系统关键流程上都添加了相关的,以观察这些流程执行的耗时情况。但是这些信息覆盖的范围毕竟是有限的,有时候并不能满足我们的分析需求。比如我们开发者想观察自己的一些代码逻辑执行的耗时情况,就需要自己定义一些去度量,这个Systrace机制本身是支持的。如何在代码中添加自定义的Systrace tag信息呢?本着知其然、知其所以然的态度,本小节中我们来简单分析一下系统Systrace机制的原理,看看一份Systrace文件到底是怎么生成的?我们知道。

m0_61097519的博客 2617

简单易懂Android之Animation(动画)及Activity动画跳转

动画的分类:    1、帧动画(Frame)        类似电影胶片,由很多帧图片,通过不间断的改变,来产生动画效果。    2、补间动画(Tween)      对View控件进行变换(平移,缩放,旋转,透明度)来产生动画效果。   动画的分类: 常用的属性如下: 帧动画: public class Fram

Toast_qi 1704

Android R DisplayManagerService模块(1) 启动

光有这些思路和搞懂单个知识的应用是还远远不够的,在Android开源框架设计思想中的知识点还是比较多的,想要搞懂还得学会整理和规划:我们常见的**Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、IOC依赖注入框架、最近架构组件Jetpack等等Android第三方开源框架,**这些都是属于Android开源框架设计思想的。如下图所示:这位阿里P8大佬针对以上知识点,熬夜整理出了一本长达1042页的完整版如何解读开源框架设计思想。

2401_84121789的博客 791

WindowManagerService本地窗口动画

一、前言 原文链接:https://www.jianshu.com/p/d41fe965e9e6android的WindowManagerService(简称wms)是系统框架一个非常庞大复杂的一个系统模块,它主要由三大块组成:wms数据结构,wms大遍历,wms的窗口动画wms总体图.pngwms数据结构就是wms的所有WindowState(继承windowconta...

Android技术之家 2461

Android 系统中的 WatchDog 详解

Watchdog字面上是“看门狗”的意思,有做过嵌入式低层的朋友应该知道,为了防止嵌入式系统MCU里的程序因为干扰而跑飞,专门在MCU里设计了一个定时器电路,叫做看门狗。当MCU正常工作的,每隔一段时间会输出一个信号给看门狗,也就是所谓的喂狗。如果程序跑飞,MCU在规定的时间内没法喂狗,这时看门狗就会直接触发一个reset信号,让CPU重新启动。在Android系统的framework中,设计了一个系统服务Watchdog,它类似于一个软件看门狗,用来保护重要的系统服务。

私房菜 2万+

LLCE、PFE模块,二层交换/桥接的通用学习转发机制

NXP 文档里给出的正式全称如下:面向 CAN/CAN-FD、LIN、FlexRay 的“低延迟通信引擎”,用多核 M0 固件做报文收发、过滤、路由,把主 CPU 中断负荷降到几乎 0。“数据包转发引擎”,负责以太网帧的分类、修改、转发、VLAN/NAT/IPsec 等 offload,支持 2 Gbit/s 线速且主机负载 ≈0。两者都跑各自固件,通过内部 AXI 桥可互连,实现“CAN→ETH”零主核参与的硬件级网关功能。是它们都集成在。

qq_25461777的博客 1305

Android Systrace 基础知识(4) - SystemServer 解读

本文是 Systrace 系列文章的第四篇,主要是对 SystemServer 进行简单介绍,介绍了 SystemServer 中几个比较重要的线程,由于 Input 和 Binder 比较重要,所以单独拿出来讲,在这里就没有再涉及到。 本系列的目的是通过 Systrace 这个工具,从另外一个角度来看待 Android 系统整体的运行,同时也从另外一个角度来对 Framework 进行学习。也许你看了很多讲 Framework 的文章,但是总是记不住代码,或者不清楚其运行的流程,也许从 Systrace

Gracker的专栏 1928

单片机笔记十二:华大HC32L136超低功耗设计实战

本文详细介绍了华大HC32L136单片机的超低功耗设计实战,包括深度睡眠模式下仅0.5μA的电流消耗、五种功耗模式的对比与配置、外设使用技巧及硬件级优化方法。通过实际项目案例,展示了如何利用ARM Cortex-M0+内核的HC32L136显著延长电池寿命,适用于智能门锁、额温枪等低功耗设备。

weixin_29189373的博客 406

Android 动画(anim)详解

Android 动画(anim)详解 就我所知,简单阐述一下: Android的animation由四种类型组成:alpha(透明度)、scale(缩放)、translate(位移)、rotate(旋转) XML配置文件中 android:alpha 渐变透明度动画效果 android:scale 渐变缩放动画效果

大叔的愤怒,你驾驭不了 4051

Cadence17.2 > Allegro > 检查PCB Layout信号线组等长

Cadence17.2 > Allegro > 检查PCB Layout等长和配线

DL_62532的专栏 1万+

【Python】torch_npu

Ascend AI处理器核心计算范式与PyTorch的桥接艺术:深入探索torch_npu的基石要真正理解,我们必须首先超越其作为PyTorch后端库的表面认知,深入其所服务的硬件——华为昇腾(Ascend)AI处理器的核心计算范式。这不仅仅是关于如何在NPU上运行PyTorch模型,更是关于NPU如何设计其计算,以及如何巧妙地将这种硬件特性映射到PyTorch的抽象层之上。1. 昇腾AI处理器的达芬奇架构:极致算力的源泉昇腾AI处理器并非传统的CPU或GPU。它采用了华为独创的达芬奇(Da Vinci)架

宅男很神经 5375

Android 动画animation 深入分析

Android 动画animation 深入分析 前言:本文试图通过分析动画流程,来理解android动画系统的设计与实现,学习动画的基本原则,最终希望能够指导动画的设计。 0 本文中用到的一些类图 1 view animation  调用方法:view.startAnimation(animation); public void startAni

farmer_cc的专栏 2万+

【华为OD机试】真题E卷-一种字符串压缩表示的解压(C++)

有一种简易压缩算法:针对全部为小写英文字母组成的字符串,将其中连续超过两个相同字母的部分压缩为连续个数加该字母,其他部分保持原样不变。例如:字符串aaabbccccd 经过压缩变成字符串 3abb4cd请您编写解压函数,根据输入的字符串, 判断其是否为合法压缩过的字符串若输入合法则输出解压缩后的字符串 否则输出字符串!error来报告错误。

u014481728的博客 895

Android开发中属性动画(ObjectAnimator)的应用

昨天看到一个有意思的动画,就想用Android 中的属性动画(ObjectAnimator)给实现出来,顺便复习一下属性动画的使用,完成效果如下: 看完了效果,现在该开始动手干活了~ 首先先分析一下这个动画用Android 中的属性动画该怎么实现: 看上去这个动画很简单,可以用一个方形和两个圆形组成,两个在不同位置上的圆分别改变其X 和 Y 方向上位置就可以了。 先创建正方形和圆形的图...

ToBeTheOnlyOne的博客 621

SAP-ABAP:SAP基础数据校验工具开发系列博客(共5篇) 第一篇:需求梳理与架构设计:从零搭建SAP主数据校验能力底座

摘要: 本系列博客介绍SAP主数据校验工具的开发和设计,旨在解决物料、供应商、客户等主数据录入不规范、关联缺失和合规性问题。第一篇聚焦需求梳理与架构设计,提出分层技术架构(展示层、业务逻辑层、数据访问层),支持实时校验和批量校验两种模式,并详细说明规则引擎设计、SAP系统对接方案及权限管控逻辑。后续篇章将深入规则引擎实现、接口对接、结果可视化和性能优化,帮助构建集中式、可配置的主数据校验能力,提升SAP数据质量。

baidu_35680696的博客 332

【经验&技巧】IBIS AMI模型眼图仿真问题探讨

在ADS的仿真环境中,不论发送和接收的AMI模型是否为true或者false,都可以将通道仿真设置为统计模式,但是,如果有一方的Init函数为false,在统计模式下,处于false状态的模型将不能执行其自带的后处理功能,如下图所示,左边的统计模式和右边的逐比特模式眼图存在明显的差异,原因在于,接收端的init函数为false导致在统计模式中不能对眼图进行DFE处理,换言之,此时统计模式下的眼图就是未经任何处理的“生图”。

一只豌豆象的博客 1767

关于Android提供的Animation动画的实现

Animation动画分为两类: 1. Tween Animation:通过对场景里的对象不断做图像变换(平移、缩放、旋转)产生动画效果,即是一种渐变动画; 2. Frame Animation:顺序播放事先做好的图像,是一种画面转换动画。 动画类型 下面先来看看Android提供的动画类型。Android的animation由四种类型组成 在XML文件中:

a623891391的专栏 682

玩转 InternVL3.5 轻量级实战:从部署到优化的全记录

玩转 InternVL3.5 轻量级实战:从部署到优化的全记录

jacke121的专栏 627

Android Animation和Animator

Android Animation和Animator一、一个很蠢的Bug二、Animation2.1 TranslateAnimation平移动画2.2 RotateAnimation旋转动画2.3 ScaleAnimation缩放动画2.4 AlphaAnimation透明度动画三、Animator 前言:写这一篇文章的目的是记录一个自己犯下的一个蠢萌的问题,顺便复习一下Animation和A...

newcoderzZ的博客 1954

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

android anim动画效果浅析

理论篇上一文转载篇已介绍,本文直接上例子: 1.首先定义一个文件夹anim,再分别定义四个动画效果的xml: 2.四个xml文件分别如下: anim_enter.xml : <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@and

好久不见 1996

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Android转场动画Transition Animation实现记录

Transition动画效果诞生于Android 4.4,主要负责: 捕捉在开始场景和结束场景中每个 View 的状态。 根据视图一个场景移动到另一个场景的差异创建一个 Animator。 应用场景包括: 可以在activity之间跳转的时候添加动画 动画共享元素之间的转换活动 activity中布局元素的过渡动画 我们以前实现Activity切换过程的动画的姿势是这样的: x...

Jerry的博客 1349

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Android 属性动画使用总结

把四大常用效果加动画组合的代码整理了一下下面是一个演示图:简单强调一下:获取ObjectAnimatorObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 1f, 0f, 1f); 第三个参数是 ...无限多的形式 因此可以根据自己的需求传递参数最后上一下代码Acitivity中点击按钮开始动画:public c...

louyulin的博客 268

Android 属性动画来实现帧动画的炫酷效果

最近公司让实现一个很炫的动画,最先想到的是使用帧动画,后来想到图片太多,使用帧动画太耗性能,由于图片多还很可能出现oom,所以决定使用属性动画来实现,下面是动画的效果图         :        下面来说一下思路:        首先根据动画的效果来写一下布局文件: <FrameLayout xmlns:android="http://schemas.android.com/ap

feng8888bbb的博客 2711

android动画基础(一) View动画和属性动画

view动画1.透明度动画//float fromAlpha, float toAlpha AlphaAnimation aa = new AlphaAnimation(0, 1); aa.setDuration(1000); view.startAnimation(aa);2.旋转动画//float fromDegrees, float toDegrees, flo

jiaway的博客 629

Android 动画之属性动画ValueAnimator和ObjectAnimator

布局就是一个ImageView,看看实现代码 public void flip(View view) { ObjectAnimator visibleToInVisable = ObjectAnimator.ofFloat(mImageView, "rotationX", 0.0f, 360.0f); //设置插值器 visibleToInVi

lzy的博客 3290
上一篇: systemserver为啥很少同步跨进程调用app/DeadSystemException相关问题分析套路
下一篇: perfetto结合源码分析input输入的iq,oq,wq部分
千里马学框架
博客等级 码龄6年 1万+粉丝 651原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值