Android框架源码,AccessibilityButtonController.java文件,简单的解读一下

源码路径

https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/accessibilityservice/AccessibilityButtonController.java?hl=zh-cn
代码如下:

package android.accessibilityservice;

import android.annotation.NonNull;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;

import java.util.Objects;

public final class AccessibilityButtonController {
    private static final String LOG_TAG = "A11yButtonController";

    private final IAccessibilityServiceConnection mServiceConnection;
    private final Object mLock;
    private ArrayMap<AccessibilityButtonCallback, Handler> mCallbacks;

    AccessibilityButtonController(@NonNull IAccessibilityServiceConnection serviceConnection) {
        mServiceConnection = serviceConnection;
        mLock = new Object();
    }

    public boolean isAccessibilityButtonAvailable() {
        if (mServiceConnection != null) {
            try {
                return mServiceConnection.isAccessibilityButtonAvailable();
            } catch (RemoteException re) {
                Slog.w(LOG_TAG, "Failed to get accessibility button availability.", re);
                re.rethrowFromSystemServer();
                return false;
            }
        }
        return false;
    }

    public void registerAccessibilityButtonCallback(@NonNull AccessibilityButtonCallback callback) {
        registerAccessibilityButtonCallback(callback, new Handler(Looper.getMainLooper()));
    }
    
    public void registerAccessibilityButtonCallback(@NonNull AccessibilityButtonCallback callback,
            @NonNull Handler handler) {
        Objects.requireNonNull(callback);
        Objects.requireNonNull(handler);
        synchronized (mLock) {
            if (mCallbacks == null) {
                mCallbacks = new ArrayMap<>();
            }

            mCallbacks.put(callback, handler);
        }
    }

    public void unregisterAccessibilityButtonCallback(
            @NonNull AccessibilityButtonCallback callback) {
        Objects.requireNonNull(callback);
        synchronized (mLock) {
            if (mCallbacks == null) {
                return;
            }

            final int keyIndex = mCallbacks.indexOfKey(callback);
            final boolean hasKey = keyIndex >= 0;
            if (hasKey) {
                mCallbacks.removeAt(keyIndex);
            }
        }
    }
    
    void dispatchAccessibilityButtonClicked() {
        final ArrayMap<AccessibilityButtonCallback, Handler> entries;
        synchronized (mLock) {
            if (mCallbacks == null || mCallbacks.isEmpty()) {
                Slog.w(LOG_TAG, "Received accessibility button click with no callbacks!");
                return;
            }

            // Callbacks may remove themselves. Perform a shallow copy to avoid concurrent
            // modification.
            entries = new ArrayMap<>(mCallbacks);
        }

        for (int i = 0, count = entries.size(); i < count; i++) {
            final AccessibilityButtonCallback callback = entries.keyAt(i);
            final Handler handler = entries.valueAt(i);
            handler.post(() -> callback.onClicked(this));
        }
    }

    void dispatchAccessibilityButtonAvailabilityChanged(boolean available) {
        final ArrayMap<AccessibilityButtonCallback, Handler> entries;
        synchronized (mLock) {
            if (mCallbacks == null || mCallbacks.isEmpty()) {
                Slog.w(LOG_TAG,
                        "Received accessibility button availability change with no callbacks!");
                return;
            }

            // Callbacks may remove themselves. Perform a shallow copy to avoid concurrent
            // modification.
            entries = new ArrayMap<>(mCallbacks);
        }

        for (int i = 0, count = entries.size(); i < count; i++) {
            final AccessibilityButtonCallback callback = entries.keyAt(i);
            final Handler handler = entries.valueAt(i);
            handler.post(() -> callback.onAvailabilityChanged(this, available));
        }
    }
    public static abstract class AccessibilityButtonCallback {
        public void onClicked(AccessibilityButtonController controller) {}
        public void onAvailabilityChanged(AccessibilityButtonController controller,
                boolean available) {
        }
    }
}

源码分析

    private static final String LOG_TAG = "A11yButtonController";

    private final IAccessibilityServiceConnection mServiceConnection;
    private final Object mLock;
    private ArrayMap<AccessibilityButtonCallback, Handler> mCallbacks;

    AccessibilityButtonController(@NonNull IAccessibilityServiceConnection serviceConnection) {
        mServiceConnection = serviceConnection;
        mLock = new Object();
    }

  • private static final String LOG_TAG = "A11yButtonController"; 这一行定义了一个私有的、静态的、最终的字符串变量,它的值是"A11yButtonController",用于作为日志的标签。私有的意思是只有这个类自己可以访问这个变量,静态的意思是这个变量不属于任何对象,而是属于这个类,最终的意思是这个变量的值不能被修改。
  • private final IAccessibilityServiceConnection mServiceConnection; 这一行定义了一个私有的、最终的接口类型的变量,它的值是一个实现了IAccessibilityServiceConnection接口的对象,用于表示一个无障碍服务的连接。私有的意思是只有这个类自己可以访问这个变量,最终的意思是这个变量的值不能被修改,但是这个对象的属性可以被修改。
  • private final Object mLock; 这一行定义了一个私有的、最终的对象类型的变量,它的值是一个Object对象,用于作为一个锁对象,用于保护临界区的同步。私有的意思是只有这个类自己可以访问这个变量,最终的意思是这个变量的值不能被修改,但是这个对象的属性可以被修改。
  • private ArrayMap<AccessibilityButtonCallback, Handler> mCallbacks; 这一行定义了一个私有的数组映射类型的变量,它的值是一个ArrayMap对象,用于存储无障碍按钮的回调函数和处理器的键值对。私有的意思是只有这个类自己可以访问这个变量,这个变量的值可以被修改,也就是可以添加或删除键值对。
  • AccessibilityButtonController(@NonNull IAccessibilityServiceConnection serviceConnection) { 这一行定义了一个构造函数,它的参数是一个非空的IAccessibilityServiceConnection类型的对象,用于初始化这个类的对象。
  • mServiceConnection = serviceConnection; 这一行是构造函数的一条语句,它的作用是将参数serviceConnection的值赋给变量mServiceConnection,也就是初始化这个类的无障碍服务连接。
  • mLock = new Object(); 这一行是构造函数的另一条语句,它的作用是创建一个新的Object对象,并将其赋给变量mLock,也就是初始化这个类的锁对象。
    public boolean isAccessibilityButtonAvailable() {
        if (mServiceConnection != null) {
            try {
                return mServiceConnection.isAccessibilityButtonAvailable();
            } catch (RemoteException re) {
                Slog.w(LOG_TAG, "Failed to get accessibility button availability.", re);
                re.rethrowFromSystemServer();
                return false;
            }
        }
        return false;
    }
  • 这个方法的名字是isAccessibilityButtonAvailable,它的返回值类型是布尔型,也就是true或false,它的作用是判断无障碍按钮是否可用。
  • 这个方法的第一行是一个if语句,它的条件是mServiceConnection不等于null,也就是说,如果这个类的无障碍服务连接对象存在,那么就执行if语句的代码块,否则就执行return false语句,也就是返回无障碍按钮不可用的结果。
  • if语句的代码块是一个try-catch语句,它的作用是处理可能发生的异常情况。try语句的代码块是一条return语句,它的作用是调用mServiceConnection对象的isAccessibilityButtonAvailable方法,并将其返回值作为这个方法的返回值,也就是说,如果mServiceConnection对象能够正常地判断无障碍按钮是否可用,那么就返回它的判断结果。
  • catch语句的代码块是用来捕获RemoteException类型的异常,也就是说,如果mServiceConnection对象在调用isAccessibilityButtonAvailable方法的过程中发生了远程通讯的错误,那么就执行catch语句的代码块。catch语句的代码块有三条语句,它们的作用分别是:
    • 调用Slog类的w方法,用来打印一条警告级别的日志信息,表示获取无障碍按钮可用性失败,并将异常对象re作为参数传递,用来显示异常的详细信息。
    • 调用异常对象re的rethrowFromSystemServer方法,用来将异常重新抛出,让系统服务处理。
    • 返回false,表示无障碍按钮不可用。

总之,这个方法的作用是判断无障碍按钮是否可用,它的逻辑是先检查无障碍服务连接对象是否存在,然后尝试调用它的方法来获取无障碍按钮的可用性,如果发生异常,就打印日志,重新抛出异常,并返回false。

public void registerAccessibilityButtonCallback(@NonNull AccessibilityButtonCallback callback) {
        registerAccessibilityButtonCallback(callback, new Handler(Looper.getMainLooper()));
    }
  • 这个方法的名字是registerAccessibilityButtonCallback,它的参数是一个非空的AccessibilityButtonCallback类型的对象,它的返回值类型是void,也就是没有返回值,它的作用是注册一个无障碍按钮的回调函数。
  • 这个方法的第一行是一条方法调用语句,它的作用是调用AccessibilityButtonController类的另一个重载的方法,也叫registerAccessibilityButtonCallback,但是它的参数有两个,分别是一个AccessibilityButtonCallback类型的对象和一个Handler类型的对象。这个方法调用语句的参数是callback和new Handler(Looper.getMainLooper()),也就是说,它将传入的callback对象和一个新创建的Handler对象作为参数传递给另一个方法。
  • 这个方法的逻辑是利用了方法重载的特性,也就是说,同一个类中可以有多个同名但是参数不同的方法,这样可以提供不同的功能或者简化方法的调用。这个方法的作用是提供了一个简化的方法调用,让用户只需要传入一个callback对象,就可以注册一个无障碍按钮的回调函数,而不需要自己创建一个Handler对象。Handler对象的作用是用来处理消息或者运行任务,Looper对象的作用是用来管理消息队列,getMainLooper方法的作用是获取主线程的Looper对象,也就是说,这个方法调用语句创建了一个与主线程关联的Handler对象,用来处理无障碍按钮的回调函数。

总之,这个方法的作用是注册一个无障碍按钮的回调函数,它的逻辑是调用另一个同名但是参数不同的方法,简化了方法的调用,让用户不需要自己创建一个Handler对象。

    public void registerAccessibilityButtonCallback(@NonNull AccessibilityButtonCallback callback,
            @NonNull Handler handler) {
        Objects.requireNonNull(callback);
        Objects.requireNonNull(handler);
        synchronized (mLock) {
            if (mCallbacks == null) {
                mCallbacks = new ArrayMap<>();
            }

            mCallbacks.put(callback, handler);
        }
    }
  • 这个方法的名字是registerAccessibilityButtonCallback,它的参数是两个非空的对象,一个是AccessibilityButtonCallback类型的,一个是Handler类型的,它的返回值类型是void,也就是没有返回值,它的作用是注册一个无障碍按钮的回调函数和一个处理器。
  • 这个方法的第一行和第二行是两条方法调用语句,它们的作用是调用Objects类的requireNonNull方法,分别对callback和handler对象进行非空检查,如果有任何一个对象为空,就抛出一个空指针异常,这样可以保证参数的有效性。
  • 这个方法的第三行是一个同步语句,它的作用是使用mLock对象作为锁,保证同一时间只有一个线程可以执行同步语句的代码块,这样可以避免多线程的并发问题。
  • 同步语句的代码块是一个if语句和一条方法调用语句,它们的作用是:
    • if语句的条件是mCallbacks等于null,也就是说,如果这个类的数组映射对象不存在,那么就执行if语句的代码块,否则就跳过。
    • if语句的代码块是一条赋值语句,它的作用是创建一个新的ArrayMap对象,并将其赋给变量mCallbacks,也就是初始化这个类的数组映射对象。
    • 方法调用语句的作用是调用mCallbacks对象的put方法,将参数callback和handler作为键值对添加到数组映射中,也就是注册一个无障碍按钮的回调函数和处理器。

总之,这个方法的作用是注册一个无障碍按钮的回调函数和处理器,它的逻辑是先对参数进行非空检查,然后使用一个锁对象进行同步,再判断是否需要初始化数组映射对象,最后将参数添加到数组映射中。

    public void unregisterAccessibilityButtonCallback(
            @NonNull AccessibilityButtonCallback callback) {
        Objects.requireNonNull(callback);
        synchronized (mLock) {
            if (mCallbacks == null) {
                return;
            }

            final int keyIndex = mCallbacks.indexOfKey(callback);
            final boolean hasKey = keyIndex >= 0;
            if (hasKey) {
                mCallbacks.removeAt(keyIndex);
            }
        }
    }
  • 这个方法的名字是unregisterAccessibilityButtonCallback,它的参数是一个非空的AccessibilityButtonCallback类型的对象,它的返回值类型是void,也就是没有返回值,它的作用是取消注册一个无障碍按钮的回调函数。
  • 这个方法的第一行是一条方法调用语句,它的作用是调用Objects类的requireNonNull方法,对callback对象进行非空检查,如果对象为空,就抛出一个空指针异常,这样可以保证参数的有效性。
  • 这个方法的第二行是一个同步语句,它的作用是使用mLock对象作为锁,保证同一时间只有一个线程可以执行同步语句的代码块,这样可以避免多线程的并发问题。
  • 同步语句的代码块是一个if语句和一个if-else语句,它们的作用是:
    • if语句的条件是mCallbacks等于null,也就是说,如果这个类的数组映射对象不存在,那么就执行return语句,也就是直接结束这个方法,不做任何操作。
    • if-else语句的条件是hasKey等于true,也就是说,如果数组映射中存在以callback为键的键值对,那么就执行if语句的代码块,否则就执行else语句的代码块。
    • if语句的代码块是一条方法调用语句,它的作用是调用mCallbacks对象的removeAt方法,将参数keyIndex作为索引,从数组映射中删除对应的键值对,也就是取消注册一个无障碍按钮的回调函数。
    • else语句的代码块是空的,也就是不做任何操作。

总之,这个方法的作用是取消注册一个无障碍按钮的回调函数,它的逻辑是先对参数进行非空检查,然后使用一个锁对象进行同步,再判断是否需要从数组映射中删除对应的键值对。

    void dispatchAccessibilityButtonClicked() {
        final ArrayMap<AccessibilityButtonCallback, Handler> entries;
        synchronized (mLock) {
            if (mCallbacks == null || mCallbacks.isEmpty()) {
                Slog.w(LOG_TAG, "Received accessibility button click with no callbacks!");
                return;
            }

            // Callbacks may remove themselves. Perform a shallow copy to avoid concurrent
            // modification.
            entries = new ArrayMap<>(mCallbacks);
        }

        for (int i = 0, count = entries.size(); i < count; i++) {
            final AccessibilityButtonCallback callback = entries.keyAt(i);
            final Handler handler = entries.valueAt(i);
            handler.post(() -> callback.onClicked(this));
        }
    }
  • 这个方法的名字是dispatchAccessibilityButtonClicked,它的参数是空的,它的返回值类型是void,也就是没有返回值,它的作用是分发无障碍按钮的点击事件给已注册的回调函数。
  • 这个方法的第一行是一条变量声明语句,它的作用是声明一个数组映射类型的变量,它的值是空的,用于存储回调函数和处理器的键值对。
  • 这个方法的第二行是一个同步语句,它的作用是使用mLock对象作为锁,保证同一时间只有一个线程可以执行同步语句的代码块,这样可以避免多线程的并发问题。
  • 同步语句的代码块是一个if语句和一条赋值语句,它们的作用是:
    • if语句的条件是mCallbacks等于null或者mCallbacks为空,也就是说,如果这个类的数组映射对象不存在或者没有任何键值对,那么就执行if语句的代码块,否则就跳过。
    • if语句的代码块是一条方法调用语句和一条return语句,它们的作用是:
      • 调用Slog类的w方法,用来打印一条警告级别的日志信息,表示收到了无障碍按钮的点击事件,但是没有任何回调函数。
      • 返回,也就是直接结束这个方法,不做任何操作。
    • 赋值语句的作用是用mCallbacks对象的构造函数,创建一个新的ArrayMap对象,并将其赋给变量entries,也就是对mCallbacks对象进行一个浅拷贝,避免在后续的操作中发生并发修改的问题。
  • 这个方法的第三行是一个for循环语句,它的作用是遍历entries对象中的所有键值对,执行相应的操作。for循环语句的条件是i小于count,其中i是一个整型变量,初始值为0,每次循环后自增1,count是一个整型变量,它的值是entries对象的大小,也就是键值对的个数。
  • for循环语句的代码块是三条变量声明语句和一条方法调用语句,它们的作用是:
    • 第一条变量声明语句的作用是声明一个AccessibilityButtonCallback类型的变量,它的值是entries对象中以i为索引的键,也就是一个回调函数对象。
    • 第二条变量声明语句的作用是声明一个Handler类型的变量,它的值是entries对象中以i为索引的值,也就是一个处理器对象。
    • 第三条变量声明语句的作用是声明一个Runnable类型的变量,它的值是一个Lambda表达式,它的作用是调用回调函数对象的onClicked方法,并将这个类的对象作为参数传递,也就是执行回调函数的逻辑。
    • 方法调用语句的作用是调用处理器对象的post方法,并将Runnable对象作为参数传递,也就是将Runnable对象添加到处理器对象的消息队列中,等待执行。

总之,这个方法的作用是分发无障碍按钮的点击事件给已注册的回调函数,它的逻辑是先检查数组映射对象是否存在或为空,然后使用一个锁对象进行同步,再对数组映射对象进行浅拷贝,最后遍历数组映射对象中的所有键值对,通过处理器对象执行回调函数对象的逻辑。

  void dispatchAccessibilityButtonAvailabilityChanged(boolean available) {
        final ArrayMap<AccessibilityButtonCallback, Handler> entries;
        synchronized (mLock) {
            if (mCallbacks == null || mCallbacks.isEmpty()) {
                Slog.w(LOG_TAG,
                        "Received accessibility button availability change with no callbacks!");
                return;
            }

            // Callbacks may remove themselves. Perform a shallow copy to avoid concurrent
            // modification.
            entries = new ArrayMap<>(mCallbacks);
        }

        for (int i = 0, count = entries.size(); i < count; i++) {
            final AccessibilityButtonCallback callback = entries.keyAt(i);
            final Handler handler = entries.valueAt(i);
            handler.post(() -> callback.onAvailabilityChanged(this, available));
        }
    }
  • 这个方法的名字是dispatchAccessibilityButtonAvailabilityChanged,它的参数是一个布尔型的变量,它的值表示无障碍按钮是否可用,它的返回值类型是void,也就是没有返回值,它的作用是分发无障碍按钮的可用性变化事件给已注册的回调函数。
  • 这个方法的第一行是一条变量声明语句,它的作用是声明一个数组映射类型的变量,它的值是空的,用于存储回调函数和处理器的键值对。
  • 这个方法的第二行是一个同步语句,它的作用是使用mLock对象作为锁,保证同一时间只有一个线程可以执行同步语句的代码块,这样可以避免多线程的并发问题。
  • 同步语句的代码块是一个if语句和一条赋值语句,它们的作用是:
    • if语句的条件是mCallbacks等于null或者mCallbacks为空,也就是说,如果这个类的数组映射对象不存在或者没有任何键值对,那么就执行if语句的代码块,否则就跳过。
    • if语句的代码块是一条方法调用语句和一条return语句,它们的作用是:
      • 调用Slog类的w方法,用来打印一条警告级别的日志信息,表示收到了无障碍按钮的可用性变化事件,但是没有任何回调函数。
      • 返回,也就是直接结束这个方法,不做任何操作。
    • 赋值语句的作用是用mCallbacks对象的构造函数,创建一个新的ArrayMap对象,并将其赋给变量entries,也就是对mCallbacks对象进行一个浅拷贝,避免在后续的操作中发生并发修改的问题。
  • 这个方法的第三行是一个for循环语句,它的作用是遍历entries对象中的所有键值对,执行相应的操作。for循环语句的条件是i小于count,其中i是一个整型变量,初始值为0,每次循环后自增1,count是一个整型变量,它的值是entries对象的大小,也就是键值对的个数。
  • for循环语句的代码块是三条变量声明语句和一条方法调用语句,它们的作用是:
    • 第一条变量声明语句的作用是声明一个AccessibilityButtonCallback类型的变量,它的值是entries对象中以i为索引的键,也就是一个回调函数对象。
    • 第二条变量声明语句的作用是声明一个Handler类型的变量,它的值是entries对象中以i为索引的值,也就是一个处理器对象。
    • 第三条变量声明语句的作用是声明一个Runnable类型的变量,它的值是一个Lambda表达式,它的作用是调用回调函数对象的onAvailabilityChanged方法,并将这个类的对象和参数available作为参数传递,也就是执行回调函数的逻辑。
    • 方法调用语句的作用是调用处理器对象的post方法,并将Runnable对象作为参数传递,也就是将Runnable对象添加到处理器对象的消息队列中,等待执行。

总之,这个方法的作用是分发无障碍按钮的可用性变化事件给已注册的回调函数,它的逻辑是先检查数组映射对象是否存在或为空,然后使用一个锁对象进行同步,再对数组映射对象进行浅拷贝,最后遍历数组映射对象中的所有键值对,通过处理器对象执行回调函数对象的逻辑。

 public static abstract class AccessibilityButtonCallback {
        public void onClicked(AccessibilityButtonController controller) {}
        public void onAvailabilityChanged(AccessibilityButtonController controller,
                boolean available) {
        }
    }
  • 这个类的名字是AccessibilityButtonCallback,它是一个抽象的静态的类,它的作用是定义一个无障碍按钮的回调函数的接口,让其他类可以继承它并实现它的方法,以便在无障碍按钮的点击事件或可用性变化事件发生时,执行相应的逻辑。
  • 这个类的第一行是一个类修饰符,它包含了三个关键字:public,static,abstract,它们的含义分别是:
    • public的意思是这个类是公开的,也就是说,任何其他的类都可以访问这个类。
    • static的意思是这个类是静态的,也就是说,这个类不属于任何对象,而是属于AccessibilityButtonController类,可以直接通过AccessibilityButtonController.AccessibilityButtonCallback来引用这个类,而不需要创建一个AccessibilityButtonController对象。
    • abstract的意思是这个类是抽象的,也就是说,这个类不能被实例化,也就是不能用new关键字创建一个对象,而只能被其他的类继承,并实现它的抽象方法。
  • 这个类的第二行和第三行是两个方法定义,它们都是公开的、抽象的方法,它们的作用是声明两个无障碍按钮的回调函数的接口,让其他类可以根据自己的需求重写它们,它们的名字和参数分别是:
    • onClicked,它的参数是一个AccessibilityButtonController类型的对象,它的作用是在无障碍按钮被点击时,执行相应的逻辑,比如启动一个无障碍服务或者执行一个无障碍操作。
    • onAvailabilityChanged,它的参数是一个AccessibilityButtonController类型的对象和一个布尔型的变量,它的作用是在无障碍按钮的可用性发生变化时,执行相应的逻辑,比如更新无障碍按钮的状态或者提示用户无障碍按钮的可用性。

总之,这个类的作用是定义一个无障碍按钮的回调函数的接口,它的逻辑是声明两个抽象的方法,让其他类可以继承它并实现它的方法,以便在无障碍按钮的事件发生时,执行相应的逻辑。

最后的归纳总结

AccessibilityButtonController.java 这个文件是 Android 框架的一部分,它提供了一个控制器类,用于管理系统导航区域中的辅助功能按钮。这个类的主要逻辑和作用如下:

  • 构造方法:接受一个 IAccessibilityServiceConnection 的参数,用于与辅助功能服务进行通信。
  • isAccessibilityButtonAvailable:返回一个布尔值,表示辅助功能按钮是否对调用服务可用。
  • registerAccessibilityButtonCallback:注册一个 AccessibilityButtonCallback 的回调,用于接收辅助功能按钮的交互和状态变化的通知。可以指定一个 Handler 来指定回调执行的线程。
  • unregisterAccessibilityButtonCallback:取消注册一个已经注册的 AccessibilityButtonCallback 的回调。
  • dispatchAccessibilityButtonClicked:分发辅助功能按钮点击事件给所有注册的回调。这个方法应该在服务的主线程上调用。
  • dispatchAccessibilityButtonAvailabilityChanged:分发辅助功能按钮可用性变化事件给所有注册的回调。这个方法应该在服务的主线程上调用。
  • AccessibilityButtonCallback:一个抽象类,定义了两个方法,分别在辅助功能按钮被点击和可用性变化时被调用。子类可以覆盖这些方法来实现自己的逻辑。
  • 21
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值