g_object_notify的使用方法

本文详细介绍了 g_object_notify 的使用方法,包括属性注册、信号发射、信号连接及回调函数的具体实现过程。

引用自:

http://blog.youkuaiyun.com/hwizhao/archive/2009/02/10/3873577.aspx

 

 

g_object_notify的使用方法

0、注册部分

     g_object_class_install_property(gobject_class,

                                                        PROP_MODAL,

               g_param_spec_boolean("modal", P_("Modal"), P_("if true, the window is modal( other windows are not usable while this one is up)"),   FALSE, GTK_PARAM_READWRITE)

                                                       );

1、emit部分

     g_object_notify(G_OBJECT(window), "modal");

// 0,1 两步骤属于属性的注册和信号发射,放在同一个类里。

 

2.  connect部分

     g_signal_connect(window, "notify::modal", G_CALLBACK(notify_modal_received), NULL);

3、回调函数

     void notify_modal_received(GtkWidget *window, GParamSpec *pspec)

     {

         printf("notify modal received");

      }

// 2,3 属于信号获取,可以放在需要的类里

#include "dms_interface.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include "json_api.h" #include "libcompat.h" static arp_scanner_t *g_scanner = NULL; static dms_handler_t *g_dms_handler = NULL; int dms_interface_init(arp_scanner_t *scanner) { g_scanner = scanner; // 创建DMS handler g_dms_handler = dms_create_handler(ARP_SCANNER_UID); if (!g_dms_handler) { fprintf(stderr, "Failed to create DMS handler\n"); return -1; } // 注册消息回调 dms_attach_event(g_dms_handler, MSG_SET_CONFIG, dms_message_callback); dms_attach_event(g_dms_handler, MSG_GET_CONFIG, dms_message_callback); dms_attach_event(g_dms_handler, MSG_START_SCAN, dms_message_callback); dms_attach_event(g_dms_handler, MSG_STOP_SCAN, dms_message_callback); dms_attach_event(g_dms_handler, MSG_GET_STATUS, dms_message_callback); dms_attach_event(g_dms_handler, MSG_GET_RESULT, dms_message_callback); dms_attach_event(g_dms_handler, MSG_CLEAR_RESULT, dms_message_callback); // 注册DS配置 ds_register_arp_config(); // 从DS加载配置 ds_load_config(&g_scanner->config); return 0; } int dms_interface_cleanup() { if (g_dms_handler) { dms_destroy_handler(g_dms_handler); g_dms_handler = NULL; } return 0; } S32 dms_message_callback(dms_handler_t *handler, U8 *mbuf, U32 mlen, U32 sender_dms_id) { if (!mbuf || mlen < sizeof(U32)) { return -1; } U32 msg_type = *(U32 *)mbuf; U8 *data = mbuf + sizeof(U32); U32 data_len = mlen - sizeof(U32); switch (msg_type) { case MSG_SET_CONFIG: return handle_set_config(handler, data, data_len, sender_dms_id); case MSG_GET_CONFIG: return handle_get_config(handler, data, data_len, sender_dms_id); case MSG_START_SCAN: return handle_start_scan(handler, data, data_len, sender_dms_id); case MSG_STOP_SCAN: return handle_stop_scan(handler, data, data_len, sender_dms_id); case MSG_GET_STATUS: return handle_get_status(handler, data, data_len, sender_dms_id); case MSG_GET_RESULT: return handle_get_result(handler, data, data_len, sender_dms_id); case MSG_CLEAR_RESULT: return handle_clear_result(handler, data, data_len, sender_dms_id); default: fprintf(stderr, "Unknown message type: %u\n", msg_type); return -1; } } S32 handle_set_config(dms_handler_t *handler, U8 *data, U32 len, U32 sender) { if (!g_scanner || len < sizeof(arp_ds_config_t)) { return -1; } arp_ds_config_t *ds_config = (arp_ds_config_t *)data; pthread_mutex_lock(&g_scanner->lock); g_scanner->config.enabled = ds_config->enabled; g_scanner->config.scan_interval = ds_config->scan_interval; g_scanner->config.validity_period = ds_config->validity_period; g_scanner->config.packet_interval = ds_config->packet_interval; g_scanner->config.start_ip = ip_to_int(ds_config->start_ip); g_scanner->config.end_ip = ip_to_int(ds_config->end_ip); pthread_mutex_unlock(&g_scanner->lock); // 保存配置到DS ds_save_config(&g_scanner->config); // 发送响应 U32 response = 0; // 成功 S32 ret = dms_sendto(handler, dms_get_sockfd(handler), MSG_SET_CONFIG, (U8 *)&response, sizeof(response), sender); if (ret != 0) { fprintf(stderr, "Failed to send response for set_config: %d\n", ret); } return 0; } S32 handle_get_config(dms_handler_t *handler, U8 *data, U32 len, U32 sender) { if (!g_scanner) { return -1; } arp_ds_config_t ds_config; pthread_mutex_lock(&g_scanner->lock); ds_config.enabled = g_scanner->config.enabled; ds_config.scan_interval = g_scanner->config.scan_interval; ds_config.validity_period = g_scanner->config.validity_period; ds_config.packet_interval = g_scanner->config.packet_interval; int_to_ip(g_scanner->config.start_ip, ds_config.start_ip); int_to_ip(g_scanner->config.end_ip, ds_config.end_ip); pthread_mutex_unlock(&g_scanner->lock); // 发送响应 dms_sendto(handler, dms_get_sockfd(handler), MSG_GET_CONFIG, (U8 *)&ds_config, sizeof(ds_config), sender); if (ret != 0) { fprintf(stderr, "Failed to send response for set_config: %d\n", ret); } return 0; } S32 handle_start_scan(dms_handler_t *handler, U8 *data, U32 len, U32 sender) { if (!g_scanner) { return -1; } int result = arp_scanner_start(g_scanner); // 发送响应 U32 response = (result == 0) ? 0 : 1; dms_sendto(handler, dms_get_sockfd(handler), MSG_START_SCAN, (U8 *)&response, sizeof(response), sender); return 0; } S32 handle_stop_scan(dms_handler_t *handler, U8 *data, U32 len, U32 sender) { if (!g_scanner) { return -1; } int result = arp_scanner_stop(g_scanner); // 发送响应 U32 response = (result == 0) ? 0 : 1; dms_sendto(handler, dms_get_sockfd(handler), MSG_STOP_SCAN, (U8 *)&response, sizeof(response), sender); return 0; } S32 handle_get_status(dms_handler_t *handler, U8 *data, U32 len, U32 sender) { if (!g_scanner) { return -1; } struct { BOOL enabled; BOOL scanning; U32 entry_count; time_t last_scan_time; } status; pthread_mutex_lock(&g_scanner->lock); status.enabled = g_scanner->config.enabled; status.scanning = g_scanner->scanning; status.entry_count = g_scanner->entry_count; status.last_scan_time = g_scanner->last_scan_time; pthread_mutex_unlock(&g_scanner->lock); dms_sendto(handler, dms_get_sockfd(handler), MSG_GET_STATUS, (U8 *)&status, sizeof(status), sender); return 0; } S32 handle_get_result(dms_handler_t *handler, U8 *data, U32 len, U32 sender) { if (!g_scanner) { return -1; } pthread_mutex_lock(&g_scanner->lock); // 构建JSON格式的结果 JSON_OBJPTR root = json_object_new_object(); JSON_OBJPTR entries = json_object_new_array(); for (int i = 0; i < g_scanner->entry_count; i++) { JSON_OBJPTR entry = json_object_new_object(); char ip_str[MAX_IP_LEN]; int_to_ip(g_scanner->entries[i].ip, ip_str); json_object_object_add(entry, "ip", json_object_new_string(ip_str)); json_object_object_add(entry, "mac", json_object_new_string(g_scanner->entries[i].mac)); json_object_object_add(entry, "first_seen", json_object_new_int64(g_scanner->entries[i].first_seen)); json_object_object_add(entry, "last_seen", json_object_new_int64(g_scanner->entries[i].last_seen)); json_object_array_add(entries, entry); } json_object_object_add(root, "entries", entries); json_object_object_add(root, "count", json_object_new_int(g_scanner->entry_count)); const char *json_str = json_object_to_json_string(root); dms_sendto(handler, dms_get_sockfd(handler), MSG_GET_RESULT, (U8 *)json_str, strlen(json_str) + 1, sender); json_object_put(root); pthread_mutex_unlock(&g_scanner->lock); return 0; } S32 handle_clear_result(dms_handler_t *handler, U8 *data, U32 len, U32 sender) { if (!g_scanner) { return -1; } pthread_mutex_lock(&g_scanner->lock); g_scanner->entry_count = 0; pthread_mutex_unlock(&g_scanner->lock); U32 response = 0; dms_sendto(handler, dms_get_sockfd(handler), MSG_CLEAR_RESULT, (U8 *)&response, sizeof(response), sender); return 0; } int ds_register_arp_config() { // 注册ARP扫描器配置表 DS_TBL_DESC arp_config_table = { .name = "arp_config", .data_size = sizeof(arp_ds_config_t), .max_num = 1, .option = NULL}; return ds_register_table_to("nsd", &arp_config_table, 1); } int ds_save_config(const arp_config_t *config) { arp_ds_config_t ds_config; ds_config.enabled = config->enabled; ds_config.scan_interval = config->scan_interval; ds_config.validity_period = config->validity_period; ds_config.packet_interval = config->packet_interval; int_to_ip(config->start_ip, ds_config.start_ip); int_to_ip(config->end_ip, ds_config.end_ip); return ds_save_single_usr_cfg("nsd", "arp_config", (U8 *)&ds_config, sizeof(ds_config)); } int ds_load_config(arp_config_t *config) { arp_ds_config_t ds_config; U32 len = sizeof(ds_config); if (ds_load_usr_cfg() == 0) { // 从DS加载配置 if (ds_array_transfer("nsd", "arp_config", NULL) == 0) { config->enabled = ds_config.enabled; config->scan_interval = ds_config.scan_interval; config->validity_period = ds_config.validity_period; config->packet_interval = ds_config.packet_interval; config->start_ip = ip_to_int(ds_config.start_ip); config->end_ip = ip_to_int(ds_config.end_ip); return 0; } } // 默认配置 config->enabled = FALSE; config->scan_interval = 60; /* s */ config->validity_period = 300; /* s */ config->packet_interval = 100; /* ms */ config->start_ip = ip_to_int("192.168.1.100"); config->end_ip = ip_to_int("192.168.1.200"); return 0; }这个里面如果要用ds_read和ds_write函数实现怎么办 #include "ds_common.h" #include <sys/shm.h> /* for shmget() */ #include <sys/sem.h> /* for semget() */ #include "sslApi.h" #ifdef DEBUG #define PRINT(fmt, ...) printf("[%s:%u] "fmt"\n", __FUNCTION__, __LINE__, ## __VA_ARGS__) #else #define PRINT(fmt, ...) #endif /* 根据相同的key值,能够找到同一块共享内存,或者同一个信号量 */ #define DS_SHM_KEY 0x6c796c #define DS_SEM_KEY 0x6c796c #ifdef _SEM_SEMUN_UNDEFINED union semun { S32 val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ U16 *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */ }; #endif LOCAL DS_HANDLE* g_libds_handle = NULL; /* 初始化信号量,在使用信号量前必须这样做 */ LOCAL int set_sem_val(int sem_id) { union semun sem_union; sem_union.val = 1; return semctl(sem_id, 0, SETVAL, sem_union); } /* yl todo: 要考虑同一个进程多次调用的情况 */ DS_HANDLE* ds_create_shm(U32 data_size) { int shm_id = -1; BIN_AREA *bin_area = NULL; int sem_id = -1; g_libds_handle = DS_MALLOC(sizeof(DS_HANDLE)); if (NULL == g_libds_handle) { PRINT("DS handle malloc failed.\n"); return NULL; } memset(g_libds_handle, 0, sizeof(DS_HANDLE)); /* 分配共享内存块。如不存在,则新建。 * 新建的内存块已初始化为0 --from "man shmget" */ shm_id = shmget(DS_SHM_KEY, data_size + sizeof(BIN_AREA), 0640 | IPC_CREAT); if (-1 == shm_id) { PRINT("Get shared memory failed, errno.%02d is: %s", errno, strerror(errno)); goto error_return; } /* 将共享内存块附加到进程自己的地址空间,内核自己决定一个合适的地址位置 */ bin_area = shmat(shm_id, NULL, 0); if ((void *)-1 == bin_area) { PRINT("Map shared memory failed, errno.%02d is: %s", errno, strerror(errno)); goto error_return; } /* 新建内存块时要记录大小 */ if (0 != data_size) { /* 将共享内存显式初始化为0,防止重启时shm存在导致动态配置没有重置 */ memset((void*)bin_area, 0, data_size + sizeof(BIN_AREA)); bin_area->data_size = data_size; } g_libds_handle->cfg_blob_buf = (U8 *)bin_area->data; g_libds_handle->cfg_blob_buf_size = bin_area->data_size; /* 分配信号量 */ sem_id = semget(DS_SEM_KEY, 1, 0666); if (-1 == sem_id) { if (ENOENT != errno) { PRINT("Get semaphore failed, errno.%02d is: %s", errno, strerror(errno)); goto error_return; } /* 信号量不存在,则创建 */ PRINT("Create semaphore"); sem_id = semget(DS_SEM_KEY, 1, 0666 | IPC_CREAT); if (-1 == sem_id) { PRINT("Create semaphore failed, errno.%02d is: %s", errno, strerror(errno)); goto error_return; } /* 初始化信号量 */ if (-1 == set_sem_val(sem_id)) { PRINT("Set semaphore failed, errno.%02d is: %s", errno, strerror(errno)); goto error_return; } } g_libds_handle->sem_id = sem_id; return g_libds_handle; error_return: ds_destroy_shm(); return NULL; } S32 ds_destroy_shm() { /* 将共享内存块从进程自己的地址空间删掉 */ if (NULL == g_libds_handle) { return ERROR; } if (NULL != g_libds_handle->cfg_blob_buf) { /* 从记录的data地址算出area的地址 */ shmdt((void *)((u_int32_t)g_libds_handle->cfg_blob_buf - sizeof(BIN_AREA))); g_libds_handle->cfg_blob_buf = NULL; } DS_FREE(g_libds_handle); return 0; } DS_HANDLE* ds_get_handle() { return g_libds_handle; } /* 对信号量减1操作,即等待P(SV) */ int ds_lock() { struct sembuf sem_b; /* 预防进程未调用ds_create_shm初始化就开始使用DS */ if (NULL == g_libds_handle) { PRINT("DS uninitialized"); return -1; } sem_b.sem_num = 0; sem_b.sem_op = -1; /* P() */ sem_b.sem_flg = SEM_UNDO; return semop(g_libds_handle->sem_id, &sem_b, 1); } /* 释放操作,使信号量变为可用,即V(SV) */ int ds_unlock() { struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; /* V() */ sem_b.sem_flg = SEM_UNDO; return semop(g_libds_handle->sem_id, &sem_b, 1); } /* 计算BLOB ID */ void ds_calc_id(const char *path, BLOB_ID *blob_id) { Md5 md5; if ((NULL == path) || (NULL == blob_id)) { return; } tpssl_InitMd5(&md5); tpssl_Md5Update(&md5, (U8 *)path, strlen(path)); tpssl_Md5Final(&md5, blob_id->u8); /*PRINT("ID [%s] => %08x%08x%08x%08x", path, ntohl(blob_id->u32[0]), ntohl(blob_id->u32[1]), ntohl(blob_id->u32[2]), ntohl(blob_id->u32[3]));*/ } BOOL ds_id_exist(BLOB_ID *id, U32 num, const BLOB_ID *blob_id) { U32 index = 0; for (index = 0; index < num; index++, id++) { if ((id->u32[0] == blob_id->u32[0]) && (id->u32[1] == blob_id->u32[1]) && (id->u32[2] == blob_id->u32[2]) && (id->u32[3] == blob_id->u32[3])) { return TRUE; } } return FALSE; } BOOL ds_path_id_exist(BLOB_ID *id, U32 num, const char *path) { BLOB_ID blob_id; ds_calc_id(path, &blob_id); return ds_id_exist(id, num, &blob_id); } /**************************************************************************** Function : ds_check_buf_modification Description : 对比blob中的旧据和准备写入的data_buf,检查是否有修改 Input : blob为待覆盖的旧数据,data_buf为新数据,size为data_buf的长度 Output : 如果有修改返回TRUE,否则返回FALSE *****************************************************************************/ BOOL ds_check_buf_modification(BLOB_DATA *blob, void *data_buf, U32 size) { if(0 != memcmp(data_buf, blob->data, size)) { return TRUE; } return FALSE; } BLOB_DATA* ds_find_blob(BIN_AREA *bin_area, const BLOB_ID *blob_id) { BLOB_DATA *blob = NULL; if ((NULL == bin_area) || (NULL == blob_id)) { return NULL; } /* 在二进制数据区里按key查找对应的二进制数据块 */ /* 表格blob要继续深入解析 */ for (blob = (BLOB_DATA *)bin_area->data; blob < (BLOB_DATA *)((U32)bin_area->data + bin_area->data_size); blob = (BLOB_TYPE_TABLE == blob->type) ? SUB_BLOB(blob) : NEXT_BLOB(blob)) { /*PRINT("BLOB %#x ID %08x%08x%08x%08x size %d|%d", (U32)blob, ntohl(blob->id.u32[0]), ntohl(blob->id.u32[1]), ntohl(blob->id.u32[2]), ntohl(blob->id.u32[3]), blob->data_size, ALIGN4(blob->data_size));*/ if ((blob->id.u32[0] == blob_id->u32[0]) && (blob->id.u32[1] == blob_id->u32[1]) && (blob->id.u32[2] == blob_id->u32[2]) && (blob->id.u32[3] == blob_id->u32[3])) { return blob; } } return NULL; } /* 从外部指定的二进制数据区读取二进制数据 */ U32 ds_ext_read(BIN_AREA *bin_area, const char *path, void *data_buf, U32 data_size) { BLOB_DATA *blob = NULL; U32 size = 0; BLOB_ID blob_id; if ((NULL == bin_area) || (NULL == path) || (NULL == data_buf)) { return 0; } ds_calc_id(path, &blob_id); /* 寻找blob */ if (NULL == (blob = ds_find_blob(bin_area, &blob_id))) { PRINT("No match segment in [%s]", path); return 0; } /* 不够空间返回,可能是前一版的数据块,只返回前面部分 */ size = min(blob->data_size, data_size); memcpy(data_buf, blob->data, size); /* 返回实际读取大小 */ return size; } /* 二进制读 */ /* yl todo: 传入版本号实现前、后向兼容读取? 先不考虑传入table的情况 */ U32 ds_read(const char *path, void *data_buf, U32 data_size) { BLOB_DATA *blob = NULL; U32 size = 0; BLOB_ID blob_id; if ((NULL == path) || (NULL == data_buf)) { return 0; } ds_calc_id(path, &blob_id); if (-1 == ds_lock()) { PRINT("Lock failed, errno.%02d is: %s", errno, strerror(errno)); return 0; } /******************** 临界区开始 ********************/ /* 寻找blob */ if (NULL == (blob = ds_find_blob((BIN_AREA *)((U32)g_libds_handle->cfg_blob_buf - sizeof(BIN_AREA)), &blob_id))) { PRINT("No match segment in [%s]", path); goto unlock_return; } /* 不够空间返回,可能是前一版的数据块,只返回前面部分 */ size = min(blob->data_size, data_size); memcpy(data_buf, blob->data, size); /******************** 临界区结束 ********************/ unlock_return: ds_unlock(); /* 返回实际读取大小 */ return size; } /* 二进制写 */ U32 ds_write(const char *path, void *data_buf, U32 data_size) { BLOB_DATA *blob = NULL; U32 size = 0; BLOB_ID blob_id; if ((NULL == path) || (NULL == data_buf)) { return 0; } ds_calc_id(path, &blob_id); if (-1 == ds_lock()) { PRINT("Lock failed, errno.%02d is: %s", errno, strerror(errno)); return 0; } /******************** 临界区开始 ********************/ /* 寻找blob */ if (NULL == (blob = ds_find_blob((BIN_AREA *)((U32)g_libds_handle->cfg_blob_buf - sizeof(BIN_AREA)), &blob_id))) { PRINT("No match segment in [%s]", path); goto unlock_return; } /* 不够空间填满,可能是前一版的数据块,只回写已修改的部分 */ size = min(blob->data_size, data_size); if(FALSE == ds_check_buf_modification(blob, data_buf, size)) { goto unlock_return; } memcpy(blob->data, data_buf, size); /******************** 临界区结束 ********************/ unlock_return: ds_unlock(); /* 返回实际写入大小 */ return size; } U32 ds_advanced_write(const char *path, void *data_buf, U32 data_size, U32 flags) { BLOB_DATA *blob = NULL; U32 size = 0; U32 bin_area_size = 0; BOOL is_list_table = FALSE; BOOL data_change = TRUE; BLOB_ID blob_id; BIN_AREA *bin_area = NULL; TABLE_DESC *table = NULL; BLOB_DATA *table_blob = NULL; if ((NULL == path) || (NULL == data_buf)) { return 0; } ds_calc_id(path, &blob_id); if (-1 == ds_lock()) { PRINT("Lock failed, errno.%02d is: %s", errno, strerror(errno)); return 0; } /******************** 临界区开始 ********************/ /* 寻找blob */ if (NULL == (blob = ds_find_blob((BIN_AREA *)((U32)g_libds_handle->cfg_blob_buf - sizeof(BIN_AREA)), &blob_id))) { /******************** 临界区结束 ********************/ ds_unlock(); PRINT("No match segment in [%s]", path); goto ret; } /* 不够空间填满,可能是前一版的数据块,只回写已修改的部分 */ size = min(blob->data_size, data_size); /* 针对 DS_FLAG_SAVE_FLASH 检查待写入数据和内存中数据是否变化,没有变化则跳过后续操作, * 但对 DS_FLAG_NOTIFY ,如果数据未修改,则记录标记位,后续不进行写flash操作 */ if(FALSE == ds_check_buf_modification(blob, data_buf, size)) { if (flags & DS_FLAG_NOTIFY) { data_change = FALSE; } else { /******************** 临界区结束 ********************/ DS_CMD_PRINT("Data not change, %s flag operation %x is ignored\n", path, flags); ds_unlock(); goto ret; } } bin_area_size = sizeof(BIN_AREA) + CALC_BLOB_SIZE(size); table = ((SEGMENT_DESC *)(blob->segment))->section->table; is_list_table = IS_LIST_TABLE(table); if (is_list_table) { bin_area_size += sizeof(BLOB_DATA); } if (flags & (DS_FLAG_CHECK | DS_FLAG_NOTIFY)) { /* 构造包含该blob的bin area,用于check和notify */ /* bin_area的长度 = bin_area的头部长度 + BLOB的头部长度 + 实际数据长度 */ if (NULL != (bin_area = ds_new_bin_area(bin_area_size))) { memcpy(bin_area->data, blob, sizeof(BLOB_DATA)); memcpy(bin_area->data + sizeof(BLOB_DATA), data_buf, size); /* 如果section属于表格数据,要格式化table blob(不含数据) */ if (is_list_table) { table_blob = (BLOB_DATA *)(bin_area->data + sizeof(BLOB_DATA) + size); ds_format_table(&table_blob, table); } if (flags & DS_FLAG_CHECK) { /******************** 临界区结束 ********************/ ds_unlock(); if (SLP_ENONE != ds_check_module(bin_area)) { size = 0; goto ret; } ds_lock(); /******************** 临界区开始 ********************/ } } } memcpy(blob->data, data_buf, size); /******************** 临界区结束 ********************/ ds_unlock(); if (data_change && (flags & DS_FLAG_SAVE_FLASH) && IS_CONFIG(((SEGMENT_DESC *)(blob->segment))->section->table)) { ds_save_usr_cfg(); } if ((flags & DS_FLAG_NOTIFY) && (NULL != bin_area)) { do_srvcb_async(bin_area); } ret: ds_free_bin_area(bin_area); /* 返回实际写入大小 */ return size; }
最新发布
08-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值