Openzeppelin库详解-AccessControlDefaultAdminRules

这篇具有很好参考价值的文章主要介绍了Openzeppelin库详解-AccessControlDefaultAdminRules。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

AccessControlDefaultAdminRules合约是对AccessControl合约的增强,主要是对默认管理员身份组(默认管理员身份组id就是全0的bytes32)的相关操作进行了控制,具体体现在:

1、“默认管理员”角色组(该角色组具有授权、取消授权其他角色组账户的权力,前提是其他角色组的管理员身份id是默认管理员,如果其他身分组设置了指定的管理员身份组,那就跟默认管理元没有关系了)中最多只能有一个账户;

2、进行“默认管理员”角色组中账户变更时,通过两步完成,原始账户先发起账户变更,同时会设置一个时间延迟,新的账户需要在时间延迟后发起接受,才能完成管理员账户转移。在新账户发起接受之前,原始帐户可以取消账户变更;

3、”默认管理员”角色组中账户变更时的延迟参数,支持实时修改;

4、其他任何身份组无权对“默认管理员”

可用实例如下:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {AccessControlDefaultAdminRules} from "@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
contract MyAccessControlDefaultAdminRules is AccessControlDefaultAdminRules {
    constructor() AccessControlDefaultAdminRules(
    3 days,
    msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder
    ) {}
}

在初始化阶段,指定了延迟时间(3天)和管理员身份组账户(合约创建账户)。

编译后对外暴露接口为:

Openzeppelin库详解-AccessControlDefaultAdminRules,openzepplin库解析,区块链

在解释各个接口之前,大家要先有延迟生效的概念,即发起了账户变更、延迟参数变更,都是需要等待一段时间后才能生效的,保留了一个可以撤回错误操作的窗口期。

按照逻辑先后顺序先对管理员身份组账户变更操作的接口如下:

1、 beginDefaultAdminTransfer:将“默认管理员”身份组的账户变更为新账户,同时设置生效的时间点,只能由当前“默认管理员”身份组账户发起;

2、cancelDefaultAdminTransfer:在新账户接受成为“默认管理员”身份组账户之前,取消上一步的账户变更,只能由当前“默认管理员”身份组账户发起;

3、acceptDefaultAdminTransfer:接受“默认管理员”身份组的账户变更,只有在第一步设置的生效时间之后才能成功发起,且只能由发起变更时指定的新账户发起;

对”默认管理员”身份组的账户变更中使用到的延迟参数操作接口如下:

1、changeDefaultAdminDelay:修改账户变更时“时间延迟”参数,设置本次修改的生效时间点,只能由当前“默认管理员”身份组账户发起,“时间延迟”参数修改后生效也有延迟;

2、rollbackDefaultAdminDelay;取消上一步“时间延迟”参数的修改,只能由当前“默认管理员”身份组账户发起,该操作只有在前一步设置的生效时间点之前才有效;

权限管理相关的接口如下:

1、grantRole:身份组授权,不允许对“默认管理员”身份组操作;

2、revokeRole:身份组解除授权,不允许对“默认管理员”身份组操作;

3、renounceRole:解除账户对某身份组的授权,只能由解除账户自身发起;如果是对“默认管理员”身份组操作,则也是要结合beginDefaultAdminTransfer进行两步操作,通过beginDefaultAdminTransfer将账户变更为address(0),然后由原始的“默认管理员”身份组账户调用renounceRole进行接受,同样只能在设置的生效时间点之后才能才成功发起,要注意因为没有其他角色组能对“默认管理员”管理,在renounceRole“默认管理员”中唯一的有效账户后,没有版本再为“默认管理员”身份组指定新账户(除非还实现了其他方法调用了内部函数_grantRole);

状态变量查看函数如下:

1、DEFAULT_ADMIN_ROLE:查看“默认管理员”角色id

2、defaultAdmin、owner:查看“默认管理员”中的账户;

3、defaultAdminDelay:查看生效中的延迟参数,该参数会参与生效时间点的计算;

4、defaultAdminDelayIncreaseWait:查看增大延迟参数,默认的最大的等待时间,当前设置的是5 days;

5、pendingDefaultAdmin:获取待生效“默认管理员”身份组账户,和生效时间点;

6、pendingDefaultAdminDelay:获取待生效的“时间延迟”参数,和该参数的生效时间点;

最后就是权限管理接口grantRole、revokeRole,除了不能操作“默认管理员”身份组,其他同AccessControl。

举例如下:

用A表示当前“默认管理员”角色组账户,A想要发起管理员账户转移,目标账户为B,则可以用账户A调用beginDefaultAdminTransfer(B);账户B想要立刻接受成为管理员账户,所以当天账户B发起了acceptDefaultAdminTransfer(),但是发现接受失败了,因为账户B想要接受成功,必须要等待3天;在第2天的时候,A突然发现其实应该要转移给账户C,于是调用了cancelDefaultAdminTransfer(),将向B转移管理员身份这个动作撤销,然后立刻调用beginDefaultAdminTransfer(C),将管理员身份转移给C;B在等待了3天后,再次发起acceptDefaultAdminTransfer(),仍然失败,因为他已经不是潜在管理员账户;A在发起向C转移管理员的第2天,想让C再多等几天,于是发起了changeDefaultAdminDelay(10 days),但是这个修改时间延迟的动作本身也会延迟生效,延迟时间为5天(系统预设的延迟时间增加时最大等待时间),所以C在beginDefaultAdminTransfer(C)发起的3天后发起acceptDefaultAdminTransfer(),交易能够成功,此刻时间延迟参数修改为10天还没有生效,时间延迟仍然为3天,C成为新的“默认管理员”身份组账户;如果A在发起向C转移管理员的当天,想让C少等几天,于是发起了changeDefaultAdminDelay(1 days),但是这个修改时间延迟的动作本身也会延迟生效,延迟时间为2天(需要等待新的时间延迟域旧的时间延迟之间的差值),所以C在beginDefaultAdminTransfer(C)发起的2天后发起acceptDefaultAdminTransfer(),交易失败,此刻时间延迟参数修改为1天还没有生效,时间延迟仍然为3天;

代码详解

状态变量与构造函数如下:

    // pending admin pair read/written together frequently
    //待生效“默认管理员”身份组账户,在没有账户变更情况下,为address(0)
    address private _pendingDefaultAdmin;
    //管理员账户变更生效时间点,在没有账户变更情况下,为0
    uint48 private _pendingDefaultAdminSchedule; // 0 == unset

    //有效的延迟时间参数,在计算生效时间点时,会参与计算
    uint48 private _currentDelay;

    //当前有效的“默认管理员”身份组账户
    address private _currentDefaultAdmin;

    // pending delay pair read/written together frequently
    //待生效的延迟时间参数
    uint48 private _pendingDelay;

    //待生效的延迟时间参数的生效时间点,即_pendingDelaySchedule时间点之后,_currentDelay在逻辑上(有可能_currentDelay的内容还没有被替换为_pendingDelay的值)的取值是就是_pendingDelay
    uint48 private _pendingDelaySchedule; // 0 == unset

    /**
     * @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
     */
    constructor(uint48 initialDelay, address initialDefaultAdmin) {
        if (initialDefaultAdmin == address(0)) {
            revert AccessControlInvalidDefaultAdmin(address(0));
        }
        _currentDelay = initialDelay;
        _grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
    }

综合看来,核心参数只有两个,一个是“默认管理员”身份组账户,一个是延迟参数,其他都是围绕这两个参数的待生效参数和生效时间点参数。

先看下“默认管理员”账户变更beginDefaultAdminTransfer的逻辑:

    //账户变函数
    function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _beginDefaultAdminTransfer(newAdmin);
    }
    /*账户变更函数具体实现逻辑:
    首先生成生效时间点,在当前时间基础上加上“延迟时间”参数;
    然后将待生效的新账户和待生效的时间点设置到账户待生效相关字段中;
    */
    function _beginDefaultAdminTransfer(address newAdmin) internal virtual {
        uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();
        _setPendingDefaultAdmin(newAdmin, newSchedule);
        emit DefaultAdminTransferScheduled(newAdmin, newSchedule);
    }

    /*计算“时间延迟”参数:
    如果“时间延迟”参数被发起过修改(判定方式是,“时间延迟”参数的待生效时间节                
    点(_pendingDelaySchedule)已经设置,即:_isScheduleSet(schedule)=true);然后判断如果当前 
    时间已经过了生效时间点,即_hasSchedulePassed(schedule)=true,那么说明待生效的“时间延迟”参数 
    已经可以起作用了,所以这时返回待生效的“时间延迟”参数作为真实的延迟时间。
    如果上述条件没有同时满足,则说明目前的“时间延迟”参数_currentDelay是有效的,则返回作为真实的延迟时间
    */
    function defaultAdminDelay() public view virtual returns (uint48) {
        uint48 schedule = _pendingDelaySchedule;
        return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
    }

再看下接受“默认管理员”账户变更acceptDefaultAdminTransfer的逻辑:

    //控制了该方法只能由待生效账户调用
    function acceptDefaultAdminTransfer() public virtual {
        (address newDefaultAdmin, ) = pendingDefaultAdmin();
        if (_msgSender() != newDefaultAdmin) {
            // Enforce newDefaultAdmin explicit acceptance.
            revert AccessControlInvalidDefaultAdmin(_msgSender());
        }
        _acceptDefaultAdminTransfer();
    }
    //先判定账户待生效的时间点是否已经满足
    //满足之后,进行“默认管理员”身份组账户变更具体操作,包括:
    //解除旧帐户在“默认管理员”身份组账户中的授权
    //向“默认管理员”身份组进行新账户授权
    //初始化账户变更待生效相关参数
    //可能有人会疑问为何没有将newAdmin赋值给_currentDefaultAdmin,其实在_grantRole中已经包含了这个步骤
    function _acceptDefaultAdminTransfer() internal virtual {
        (address newAdmin, uint48 schedule) = pendingDefaultAdmin();
        if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
            revert AccessControlEnforcedDefaultAdminDelay(schedule);
        }
        _revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
        _grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
        delete _pendingDefaultAdmin;
        delete _pendingDefaultAdminSchedule;
    }
    //重写解除授权内部函数,在操作“默认管理员”身份组账户时,还要进行当前管理员账户的初始化
    function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
            delete _currentDefaultAdmin;
        }
        return super._revokeRole(role, account);
    }
    //重写授权内部函数,在操作“默认管理员”身份组账户时,控制只有现在有效管理员账户为初始化状态时,才能进行后续操作,有效管理员账户为初始化状态只有两种情况,一种是新账户调用了_revokeRole解除了旧帐户授权,一种是旧帐户解除自身授权:将账户比那更为address(0),然后调用了renounceRole进行生效,renounceRole中调用了super.renounceRole(role, account),然后super.renounceRole中有调用内部函数_revoke,因为_revoke在当前合约被重写了,所以会采用当前合约的版本,所以进行delete _currentDefaultAdmin;
    //将当前有效管理账户赋值为传入的新账户地址
    function _grantRole(bytes32 role, address account) internal virtual override returns (bool) {
        if (role == DEFAULT_ADMIN_ROLE) {
            if (defaultAdmin() != address(0)) {
                revert AccessControlEnforcedDefaultAdminRules();
            }
            _currentDefaultAdmin = account;
        }
        return super._grantRole(role, account);
    }

 然后看下取消账户变更cancelDefaultAdminTransfer的逻辑:

    //撤销已经发起的“默认管理员”身份组账户变更操作,只有当前有效管理员账户才能发起,这个操作在逻辑上只有新账户还没有接受之前才是有效的,否则因为onlyRole(DEFAULT_ADMIN_ROLE)限制,旧账户不再有权限调用该方法
    function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _cancelDefaultAdminTransfer();
    }

    //通过将待生效账户和待生效的时间点均初始化为0实现撤销变更,即刻生效
    function _cancelDefaultAdminTransfer() internal virtual {
        _setPendingDefaultAdmin(address(0), 0);
    }

    //设置待生效的“默认管理员”身份组新账户、新账户生效的时间点
    function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {

        //获取存量的待生效时间点参数
        (, uint48 oldSchedule) = pendingDefaultAdmin();

        _pendingDefaultAdmin = newAdmin;
        _pendingDefaultAdminSchedule = newSchedule;

        // An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.
        
        //如果待生效时间点有设置内容,说明之前发起过账户变更,只是还未生效(包括时间未到和新账户还未接受两种情况),因此抛出账户变更撤销事件
        if (_isScheduleSet(oldSchedule)) {
            // Emit for implicit cancellations when another default admin was scheduled.
            emit DefaultAdminTransferCanceled();
        }
    }

最后看下解除自身授权renounceRole的逻辑:

    //解除账户对某个身份组的授权,必须要由被解除账户自身发起,发起该操作后,默认管理员账户为0,所有默认管理账户账户能发起的方法均无效
    //如果操作的是“默认管理员”身份组,那么则需要通过两步来完成,第一步,通过beginDefaultAdminTransfer将管理员地址变更为address(0),第二步,在有效时间点之后调用renounceRole进行确认
    function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
        //如果是“默认管理员”身份组,则要求目的账户得是原始的管理员账户
        if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
            //获取待生效的新账户和待生效时间点
            (address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
            //必须同时满足待生效账户是0、待生效时间点已经设置且待生效时间点已经过去
            if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
                revert AccessControlEnforcedDefaultAdminDelay(schedule);
            }
            //在上述条件都满足后,初始化待生效时间点
            delete _pendingDefaultAdminSchedule;
        }
        //调用父类renounceRole,这里要注意:父类的renounceRole实现了两个功能,一个是进行对当前方法调用地址的控制,保证只能由解除授权的账户自身发起renounceRole操作;另一个是调用了_revokeRole,因为当前合约重写了_revokeRole,所以要看当前_revokeRole的实现逻辑:在父类的_revokeRole的基础上,增加了对当前默认管理员账户初始化为0的操作
        super.renounceRole(role, account);
    }

上面是“默认管理员”身份组账户管理相关操作,下面看下“时间延迟参数”相关操作:

修改“时间延迟参数”changeDefaultAdminDelay的逻辑如下:

    //修改“时间延迟参数”,即状态变量_currentDelay和_pendingDelay
    function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _changeDefaultAdminDelay(newDelay);
    }

    //内部方法
    function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {
        //设置“时间延迟参数”的待生效时间点,在当前时间基础上加上等待时间,等待时间通过_delayChangeWait获得
        uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);
        _setPendingDelay(newDelay, newSchedule);
        emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);
    }

    //计算“时间延迟参数”变更时需要延迟的时间
    function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {
        //获取当前生效的延迟时间
        uint48 currentDelay = defaultAdminDelay();

        // When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up
        // to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day
        // to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new
        // delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like
        // using milliseconds instead of seconds.
        //
        // When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees
        // that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled.
        // For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.

        //如果新设置的“时间延迟参数”大于当前生效中的“时间延迟参数”,那么返回新设置“时间延迟参数”和系统预设的延迟参数增大时的默认最大值两者中的较小值;
        //如果新设置的“时间延迟参数”小于等于当前生效中的“时间延迟参数”,则需要等待新参数和旧参数的时间差值再生效“时间延迟参数”,这保证延迟参数的生效不会影响已经在进行中的账户转移等待时间,比如旧的延时时间为10天,发起账户转移后,新账户要等待10天才能发起接受,这是基于旧的“时间延迟参数”达成的共识。举个例子来看为何能够保证如何保证上述逻辑:在发起账户转移后,立即修改延迟参数为1天,因为要等待9天这个新参数才能生效,所以新账户等待时间在9天内还是保持为旧的“时间延迟参数”,第十天虽然已经变更新为参数,但新参数仍然要求等待一天,所以总的等待时间仍不短与10天,如果在账户转移的第二天修改延迟参数为1天,新账户等待时间子账户转移发起的10天内还是保持为旧的“时间延迟参数”,也不影响总延时时间为10天
        return
            newDelay > currentDelay
                ? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48
                : currentDelay - newDelay;
    }

    //设置待生效的“时间延迟参数”和生效时间点,
    function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {
        //先判断旧的生效时间是否有配置,如果有配置,后续要判断是否要先生效旧的待生效“时间延迟参数”
        uint48 oldSchedule = _pendingDelaySchedule;

        if (_isScheduleSet(oldSchedule)) {
            if (_hasSchedulePassed(oldSchedule)) {
                // Materialize a virtual delay
                //如果当前时间晚于生效时间点,则先生效旧的待生效“时间延迟参数”
                _currentDelay = _pendingDelay;
            } else {
                // Emit for implicit cancellations when another delay was scheduled.
                emit DefaultAdminDelayChangeCanceled();
            }
        }

        _pendingDelay = newDelay;
        _pendingDelaySchedule = newSchedule;
    }

取消“时间延迟参数”rollbackDefaultAdminDelay的逻辑为:文章来源地址https://www.toymoban.com/news/detail-825105.html

    //只有有效管理员账户才能发起,即刻生效
    function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
        _rollbackDefaultAdminDelay();
    }

    //修改待生效时间延迟参数和时间延迟参数生效时间点为0
    function _rollbackDefaultAdminDelay() internal virtual {
        _setPendingDelay(0, 0);
    }

到了这里,关于Openzeppelin库详解-AccessControlDefaultAdminRules的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • openzeppelin erc20各种接口

    这套接口、合约和应用程序都与 ERC20 代币标准相关。 有一些核心合约实现了 EIP 中指定的功能和定义: IERC20:包含所有ERC20应该实现的接口 IERC20Metadata:对ERC20接口的扩展,包括Token名称,符号,和精度。 ERC20:对ERC20接口的实现,将名称,符号和精度这些可选的标准扩展引入到

    2024年02月03日
    浏览(6)
  • 11U 以太坊 ethereum OpenZeppelin : 升级智能合约

    • 升级中有什么 • 使用升级插件升级 • 升级如何运作 • 初始化 • 升级 • 测试 • OpenZeppelin Tutorials 教程 • Contact 联系方式 使用OpenZeppelin 升级插件部署的智能合约可以升级以修改其代码,同时保留其地址、状态和余额。这使您可以迭代地向项目添加新功能,或修复您在

    2023年04月08日
    浏览(17)
  • 基于Hardhat和Openzeppelin开发可升级合约(二)

    基于Hardhat和Openzeppelin开发可升级合约(二)

    在本章我将开始介绍和演示 基于 Openzeppelin 的可升级合约解决方案 根据设计,智能合约是不可变的。但随着新的客户需求和产品设计的升级迭代,合约也需要升级。 Openzeppelin 的基础可升级合约解决方案是将合约数据与逻辑分离。 代理合约(Proxy) 负责转发交易到逻辑合约,

    2024年01月19日
    浏览(29)
  • 基于openzeppelin编写solidity可升级的智能合约

    基于openzeppelin编写solidity可升级的智能合约

            现代软件的设计原则是“敏捷开发,迅速迭代”,功能升级或bug修复是所有软件系统都要面对的问题。甚至可以说软件质量在很大程度上依赖于升级和修补源代码的能力。当然Dapp(去中心化应用)也不例外,尤其Dapp一切都是透明的,这使得任何级别的bug都会被成

    2024年01月18日
    浏览(15)
  • 11M 以太坊 ethereum OpenZeppelin : 开发智能合约

    • 设置项目 • 第一份合同 • 编译 Solidity • 添加更多合约 • 使用 OpenZeppelin 合约 • OpenZeppelin Tutorials 教程 • Contact 联系方式 创建项目后的第一步是安装开发工具。 以太坊最流行的开发框架是Hardhat,我们用ethers.js介绍了它最常见的用途。下一个最受欢迎的是使用web3.js的

    2024年02月11日
    浏览(14)
  • 使用VSCode引用OpenZeppelin库编写solidity合约时报错解决办法

    使用VSCode引用OpenZeppelin库编写solidity合约时报错解决办法

    本文针对的 调试 Solidity 代码的插件为 最近在使用 VS Code 编写 solidity 合约的时候,引用 OpenZeppelin 库之后出现了如下错误: 报错为: Expected string literal (path), \\\"*\\\" or alias list. 或者还有可能会出现这样的错误: 报错为: Source \\\"@openzeppelin/contracts/token/ERC721/ERC721.sol\\\" not found: File i

    2024年02月02日
    浏览(15)
  • 11O 以太坊 ethereum OpenZeppelin : 部署智能合约并与之交互

    • 建立本地区块链 • 部署智能合约 • 从控制台交互 • 以编程方式交互 • 获取合约实例 • 调用合约 • 发送交易 • OpenZeppelin Tutorials 教程 • Contact 联系方式 在开始之前,我们首先需要一个可以部署合约的环境。以太坊区块链(通常称为“主网”,表示“主网络”)需要

    2023年04月08日
    浏览(10)
  • 【区块链技术开发】OpenZeppelin智能合约库:提高智能合约的安全性和可靠性,加速去中心化应用DApp的开发与部署。

    专栏:【区块链技术开发】 前期文章: 【区块链技术开发】使用Infura连接以太坊节点和OpenZeppelin库来构建安全、可靠的智能合约 【区块链技术开发】 Solidity使用Truffle Box工具实现预构建模板、自动化部署、创建智能合约示例代码 【区块链技术开发】 Solidity使用truffle工具创建

    2023年04月24日
    浏览(47)
  • 【WinAPI详解】<CreateWindowEx详解>

    函数原型: 目录 1.        DWORD        dwExStyle        //窗口的扩展风格(加强版专有) 2.        DWORD        dwStyle        //窗口的基本风格 3.        LPCTSTR        lpClassName        //已经注册的窗口类名称 4.        实例 参数解析: 参数

    2024年02月12日
    浏览(10)
  • EventBus详解 (详解 + 原理)

    EventBus详解 (详解 + 原理)

    EventBus简介 EventBus是一个开源库,由GreenRobot开发而来,是用于Android开发的 “事件发布 — 订阅总线”, 用来进行模块间通信、解藕。 它可以使用很少的代码,来实现多组件之间的通信。 Android系统内置的事件通讯存在缺点: Android系统中的事件通信则是 handler ( 消息机制 ) 和

    2024年02月02日
    浏览(10)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包