androidpn启动步骤

Androidpn client的运行步骤

  1. 创建ServiceManger对象,设置提醒时候的图标

  2. 1)         读取raw下的andriodpn.properties信息,apikeyxmppHostxmppPort

  3. 2)         创建client_preferences.xml 并存入信息apikeyxmppHostxmppPortversionCALLBACK_ACTIVITY_CLASS_NAMECALLBACK_ACTIVITY_PACKAGE_NAME 信息

  4. 开启服务

  5. 1)         创建serviceThread线程,

  6. 2)         在线程中通过开始NotificationService服务(推送的后台服务)

  7. NotificationService创建时onCreate()

  8. 1)         创建出电话管理器TelephonyManager 获取手机的deviceId,存入client_preference中去

  9. 2)         如果手机是模拟器则deviceId的值变成0+,并且多加上EMULATOR_DEVICE_ID属性

  10. 3)         创建XmppManager对象

  11. 4)         提交一个线程任务(TaskSubmitter.submit    ß

  12. 5)         任务内容为(NotificationService.start()),注册广播事件NotificationReceiverConnectivityReceiver 、调用xmpp进行连接

  13. a)         NotificationReceiver 进行推送过来的数据封装,传给notifier这时就可以看到推送的消息了,数据有(idapi_keytitlemessageurl

 

  1. ConnectivityReceiver 通过ConnectivityManager判断是否有网络,如果有网络进进行notificationService.connect() 实际上xmpp的连接,如果没网络就销毁连接notificationService.discount()

  2. 6)          

  3. 4.         XmppManagerxmpp的连接

  4. 1)         添加连接任务ConnectTask 到线程池中

  5. 2)         添加注册任务RegisterTask到线程池中

如果在client_preferences中没有找到用户名密码,就随机生成一个用户名密码向服务器注册

发送

<iq id="3XeCX-0" type="set">

    <query xmlns="jabber:iq:register">

        <password>8760a685a5b44132b9f86357cac6fcd5</password>

        <username> d93db873c4fa4858a7a562e08537f6bf </username>

    </query>

</iq>

接受

<iq type="result" id="3XeCX-0" to="127.0.0.1/1868b569" />

  1. 添加登录任务LoginTask到线程池中

先判断是否有权限(此账户是否登录已经登录)

发送

<iq id="DUE1S-0" type="get">

    <query xmlns="jabber:iq:auth">

        <username>d93db873c4fa4858a7a562e08537f6bf</username>

    </query>

</iq>

接受:

<iq type="result" id="DUE1S-0">

    <query xmlns="jabber:iq:auth">

        <username>d93db873c4fa4858a7a562e08537f6bf</username>

        <password />

        <digest />

        <resource />

    </query>

</iq>

如果没有登录发送xml信息:

<iq id="DUE1S-1" type="set">

    <query xmlns="jabber:iq:auth">

        <username>d93db873c4fa4858a7a562e08537f6bf</username>

        <digest>36926a5756f65910b1be819346dbc827883425b4</digest>

        <resource>AndroidpnClient</resource>

    </query>

</iq>

                   接受消息的格式

<iq    type="set"       id="214-0" to="a64e0a7d27a94d3a9760b77d10223ccc@127.0.0.1/AndroidpnClient">

    <notification xmlns="androidpn:iq:notification">

        <id>1eb0f3ed</id>

        <apiKey>1234567890</apiKey>

        <title>title11</title>

        <message>msg22</message>

        <uri>http://www.baidu.com</uri>

    </notification>

</iq>

如果登录时没有注册会抛出XMPPException异常,异常码为401;会清除client_preference中的usernamepassword数据

 

登录之后,会有一个NotificationPacketListener心跳包监听器在后台运行,只要监听到有数据包发送过来就会通过NotificationIQProviderNotificationIQ进行解析,并且发送广播给NotificationReceiver

 

androidpn server启动步骤

androidpn (Android Push Notification)是一个基于XMPP协议的java开源Android push notification实现。

它包含了完整的客户端和服务器端。

本文是在Tomcat下部署 androidpn server的启动日志的分析。

 

# tail -f logs/catalina.out 

 

#启动Tomcat的服务:对应tomcat/conf/server.xml中的    <Service name="Catalina">
May 04, 2014 2:55:59 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Catalina

 

#启动 Tomcat servlet 引擎:对应 tomcat/conf/server.xml中的  <Engine name="Catalina" defaultHost="localhost">
May 04, 2014 2:55:59 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.39

 

#部署ROOT目录

May 04, 2014 2:56:03 PM org.apache.catalina.startup.HostConfig deployDirectory
INFO: Deploying web application directory /app/apache-tomcat-7.0.39/webapps/ROOT

#验证jar包
May 04, 2014 2:56:03 PM org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(/app/apache-tomcat-7.0.39/webapps/ROOT/WEB-INF/lib/el-api-6.0.29.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/el/Expression.class


May 04, 2014 2:56:03 PM org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(/app/apache-tomcat-7.0.39/webapps/ROOT/WEB-INF/lib/jsp-api-2.1.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/el/Expression.class


May 04, 2014 2:56:03 PM org.apache.catalina.loader.WebappClassLoader validateJarFile
INFO: validateJarFile(/app/apache-tomcat-7.0.39/webapps/ROOT/WEB-INF/lib/servlet-api-2.5.jar) - jar not loaded. See Servlet Spec 2.3, section 9.7.2. Offending class: javax/servlet/Servlet.class

#Spring ContextLoaderListener初始化开始
{INFO } [2014-05-04 14:56:04,977] <org.springframework.web.context.ContextLoader> : Root WebApplicationContext: initialization started

#使用 XmlWebApplicationContext ,XmlWebApplicationContext实现了WebApplicationContext接口,当前的servlet用它来创建上下文。

#如果之后还有其他继承的上下文的时候,这里创建的context作为内容上下文的根,称为 ROOT

#实际名称为  org.springframework.web.context.WebApplicationContext.ROOT

{INFO } [2014-05-04 14:56:05,001] <org.springframework.web.context.support.XmlWebApplicationContext> : Refreshing Root WebApplicationContext: startup date [Sun May 04 14:56:05 CST 2014]; root of context hierarchy

 

#开始加载web.xml 中 <context-param>标签 contextConfigLocation 配置的文件 
{INFO } [2014-05-04 14:56:05,035] <org.springframework.beans.factory.xml.XmlBeanDefinitionReader> : Loading XML bean definitions from class path resource [conf/application-context.xml]

 

#开始加载 application-context.xml 中import引用的配置文件
{INFO } [2014-05-04 14:56:05,219] <org.springframework.beans.factory.xml.XmlBeanDefinitionReader> : Loading XML bean definitions from class path resource [conf/rest/rest-component-config.xml]

#IoC容器的实现类,列出所有的spring中配置的bean id,这些bean在 ROOT context上下文中
{INFO } [2014-05-04 14:56:05,326] <org.springframework.beans.factory.support.DefaultListableBeanFactory> : Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@14562294: defining beans [component,application,router,get,post]; root of factory hierarchy

#Spring ContextLoaderListener初始化完成
{INFO } [2014-05-04 14:56:05,408] <org.springframework.web.context.ContextLoader> : Root WebApplicationContext: initialization completed in 430 ms

#开始初始化 dispatcher
{INFO } [2014-05-04 14:56:05,446] <org.springframework.web.servlet.DispatcherServlet> : FrameworkServlet 'dispatcher': initialization started

#使用 XmlWebApplicationContext ,XmlWebApplicationContext实现了WebApplicationContext接口,当前的servlet用它来创建上下文。

#上级context上下文是 Root WebApplicationContext
{INFO } [2014-05-04 14:56:05,448] <org.springframework.web.context.support.XmlWebApplicationContext> : Refreshing WebApplicationContext for namespace 'dispatcher-servlet': startup date [Sun May 04 14:56:05 CST 2014]; parent: Root WebApplicationContext

#加载 dispatcher-servlet.xml
{INFO } [2014-05-04 14:56:05,448] <org.springframework.beans.factory.xml.XmlBeanDefinitionReader> : Loading XML bean definitions from ServletContext resource [/WEB-INF/dispatcher-servlet.xml]

 

#IoC容器的实现类,列出所有的dispatcher-servlet中配置的bean id,上级的Ioc容器是在Spring中创建的。

{INFO } [2014-05-04 14:56:05,500] <org.springframework.beans.factory.support.DefaultListableBeanFactory> : Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@2604a07d: defining beans [urlMapping,filenameController,userController,sessionController,notificationController,paramResolver,viewResolver,messageSource]; parent: org.springframework.beans.factory.support.DefaultListableBeanFactory@14562294

#发现NotificationController中的方法
{DEBUG} [2014-05-04 14:56:05,526] <org.androidpn.server.console.controller.NotificationController> : Found action method [public org.springframework.web.servlet.ModelAndView org.androidpn.server.console.controller.NotificationController.send(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.lang.Exception]
{DEBUG} [2014-05-04 14:56:05,526] <org.androidpn.server.console.controller.NotificationController> : Found action method [public org.springframework.web.servlet.ModelAndView org.androidpn.server.console.controller.NotificationController.list(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.lang.Exception]


#此处实例化NotificationController ,调用 NotificationController 的无参数构造方法。

#NotificationController无参构造方法实例化 NotificationManager

#NotificationManager无参构造方法实例化  SessionManager

#SessionManager无参数构造方法实例化 XmppServer

#XmppServer 无参构造方法实例化并调用start()方法,初始化服务的路径读取配置文件.

#通过 Config 代理并实例化 ConfigManager 加载 config.xml 。

{DEBUG} [2014-05-04 14:56:05,580] <org.apache.commons.configuration.ConfigurationUtils> : ConfigurationUtils.locate(): base is /app/apache-tomcat-7.0.39, name is config.xml
{DEBUG} [2014-05-04 14:56:05,596] <org.apache.commons.configuration.ConfigurationUtils> : Loading configuration from the context classpath (config.xml)

 

#加载  config.xml中的配置文件 config.properties
{DEBUG} [2014-05-04 14:56:05,693] <org.apache.commons.configuration.ConfigurationUtils> : ConfigurationUtils.locate(): base is /app/apache-tomcat-7.0.39, name is config.properties
{DEBUG} [2014-05-04 14:56:05,693] <org.apache.commons.configuration.ConfigurationUtils> : Loading configuration from the context classpath (config.properties)

 

#config.xml加载完毕

{INFO } [2014-05-04 14:56:05,702] <org.androidpn.server.util.ConfigManager> : Configuration loaded: config.xml

 

#用 ClassPathXmlApplicationContext 加载 spring-config.xml配置文件
{INFO } [2014-05-04 14:56:05,712] <org.springframework.context.support.ClassPathXmlApplicationContext> : Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@359d9606: startup date [Sun May 04 14:56:05 CST 2014]; root of context hierarchy
{INFO } [2014-05-04 14:56:05,713] <org.springframework.beans.factory.xml.XmlBeanDefinitionReader> : Loading XML bean definitions from class path resource [spring-config.xml]

 

#加载 spring-config.xml 文件中的 jdbc.properties 配置文件。
{INFO } [2014-05-04 14:56:05,813] <org.springframework.beans.factory.config.PropertyPlaceholderConfigurer> : Loading properties file from class path resource [jdbc.properties]

 

# MINA Socket 配置
{WARN } [2014-05-04 14:56:05,830] <org.springframework.beans.factory.config.CustomEditorConfigurer> : Passing PropertyEditor instances into CustomEditorConfigurer is deprecated: use PropertyEditorRegistrars or PropertyEditor class names instead. Offending key [java.net.SocketAddress; offending editor instance: org.apache.mina.integration.beans.InetSocketAddressEditor@67b7d0fb

 

# Ioc 列表Spring beans id

{INFO } [2014-05-04 14:56:05,838] <org.springframework.beans.factory.support.DefaultListableBeanFactory> : Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@243e2c21: defining beans [propertyConfigurer,dataSource,sessionFactory,transactionManager,userDao,userService,org.springframework.beans.factory.config.CustomEditorConfigurer#0,xmppHandler,filterChainBuilder,ioAcceptor]; root of factory hierarchy

(这里的Spring applicationContext 与前面的applicationContext没有任何关系,是独立的。两个上下文中的bean不可见,修改的方法见 http://blog.csdn.net/teamlet/article/details/26476671 )

#实例化beans
{INFO } [2014-05-04 14:56:05,928] <org.hibernate.cfg.annotations.Version> : Hibernate Annotations 3.4.0.GA
{INFO } [2014-05-04 14:56:05,936] <org.hibernate.cfg.Environment> : Hibernate 3.3.1.GA
{INFO } [2014-05-04 14:56:05,939] <org.hibernate.cfg.Environment> : hibernate.properties not found
{INFO } [2014-05-04 14:56:05,942] <org.hibernate.cfg.Environment> : Bytecode provider name : javassist
{INFO } [2014-05-04 14:56:05,945] <org.hibernate.cfg.Environment> : using JDK 1.4 java.sql.Timestamp handling
{INFO } [2014-05-04 14:56:06,020] <org.hibernate.annotations.common.Version> : Hibernate Commons Annotations 3.1.0.GA
{INFO } [2014-05-04 14:56:06,031] <org.hibernate.cfg.Configuration> : configuring from url: file:/data/apache-tomcat-7.0.39/webapps/ROOT/WEB-INF/classes/hibernate.cfg.xml
{INFO } [2014-05-04 14:56:06,122] <org.hibernate.cfg.Configuration> : Configured SessionFactory: null
{INFO } [2014-05-04 14:56:06,156] <org.hibernate.cfg.AnnotationBinder> : Binding entity from annotated class: org.androidpn.server.model.User
{INFO } [2014-05-04 14:56:06,184] <org.hibernate.cfg.annotations.EntityBinder> : Bind entity org.androidpn.server.model.User on table apn_user
{INFO } [2014-05-04 14:56:06,221] <org.hibernate.cfg.AnnotationConfiguration> : Hibernate Validator not found: ignoring
{INFO } [2014-05-04 14:56:06,222] <org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean> : Building new Hibernate SessionFactory
{INFO } [2014-05-04 14:56:06,224] <org.hibernate.cfg.search.HibernateSearchEventListenerRegister> : Unable to find org.hibernate.search.event.FullTextIndexEventListener on the classpath. Hibernate Search is not enabled.
{INFO } [2014-05-04 14:56:06,263] <org.hibernate.connection.ConnectionProviderFactory> : Initializing connection provider: org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider
{INFO } [2014-05-04 14:56:06,439] <org.hibernate.cfg.SettingsFactory> : RDBMS: MySQL, version: 5.5.33-log
{INFO } [2014-05-04 14:56:06,439] <org.hibernate.cfg.SettingsFactory> : JDBC driver: MySQL-AB JDBC Driver, version: mysql-connector-java-5.1.6 ( Revision: ${svn.Revision} )
{INFO } [2014-05-04 14:56:06,449] <org.hibernate.dialect.Dialect> : Using dialect: org.hibernate.dialect.MySQLDialect
{INFO } [2014-05-04 14:56:06,452] <org.hibernate.transaction.TransactionFactoryFactory> : Transaction strategy: org.springframework.orm.hibernate3.SpringTransactionFactory
{INFO } [2014-05-04 14:56:06,453] <org.hibernate.transaction.TransactionManagerLookupFactory> : No TransactionManagerLookup configured (in JTA environment, use of read-write or transactional second-level cache is not recommended)
{INFO } [2014-05-04 14:56:06,453] <org.hibernate.cfg.SettingsFactory> : Automatic flush during beforeCompletion(): disabled
{INFO } [2014-05-04 14:56:06,453] <org.hibernate.cfg.SettingsFactory> : Automatic session close at end of transaction: disabled
{INFO } [2014-05-04 14:56:06,453] <org.hibernate.cfg.SettingsFactory> : JDBC batch size: 15
{INFO } [2014-05-04 14:56:06,453] <org.hibernate.cfg.SettingsFactory> : JDBC batch updates for versioned data: disabled
{INFO } [2014-05-04 14:56:06,453] <org.hibernate.cfg.SettingsFactory> : Scrollable result sets: enabled
{INFO } [2014-05-04 14:56:06,454] <org.hibernate.cfg.SettingsFactory> : JDBC3 getGeneratedKeys(): enabled
{INFO } [2014-05-04 14:56:06,454] <org.hibernate.cfg.SettingsFactory> : Connection release mode: auto
{INFO } [2014-05-04 14:56:06,454] <org.hibernate.cfg.SettingsFactory> : Maximum outer join fetch depth: 2
{INFO } [2014-05-04 14:56:06,454] <org.hibernate.cfg.SettingsFactory> : Default batch fetch size: 1
{INFO } [2014-05-04 14:56:06,454] <org.hibernate.cfg.SettingsFactory> : Generate SQL with comments: enabled
{INFO } [2014-05-04 14:56:06,455] <org.hibernate.cfg.SettingsFactory> : Order SQL updates by primary key: disabled
{INFO } [2014-05-04 14:56:06,455] <org.hibernate.cfg.SettingsFactory> : Order SQL inserts for batching: disabled
{INFO } [2014-05-04 14:56:06,455] <org.hibernate.cfg.SettingsFactory> : Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
{INFO } [2014-05-04 14:56:06,456] <org.hibernate.hql.ast.ASTQueryTranslatorFactory> : Using ASTQueryTranslatorFactory
{INFO } [2014-05-04 14:56:06,456] <org.hibernate.cfg.SettingsFactory> : Query language substitutions: {}
{INFO } [2014-05-04 14:56:06,456] <org.hibernate.cfg.SettingsFactory> : JPA-QL strict compliance: disabled
{INFO } [2014-05-04 14:56:06,456] <org.hibernate.cfg.SettingsFactory> : Second-level cache: enabled
{INFO } [2014-05-04 14:56:06,457] <org.hibernate.cfg.SettingsFactory> : Query cache: disabled
{INFO } [2014-05-04 14:56:06,460] <org.hibernate.cfg.SettingsFactory> : Cache region factory : org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge
{INFO } [2014-05-04 14:56:06,460] <org.hibernate.cache.impl.bridge.RegionFactoryCacheProviderBridge> : Cache provider: net.sf.ehcache.hibernate.EhCacheProvider
{INFO } [2014-05-04 14:56:06,462] <org.hibernate.cfg.SettingsFactory> : Optimize cache for minimal puts: disabled
{INFO } [2014-05-04 14:56:06,462] <org.hibernate.cfg.SettingsFactory> : Structured second-level cache entries: disabled
{INFO } [2014-05-04 14:56:06,465] <org.hibernate.cfg.SettingsFactory> : Echoing all SQL to stdout
{INFO } [2014-05-04 14:56:06,466] <org.hibernate.cfg.SettingsFactory> : Statistics: disabled
{INFO } [2014-05-04 14:56:06,466] <org.hibernate.cfg.SettingsFactory> : Deleted entity synthetic identifier rollback: disabled
{INFO } [2014-05-04 14:56:06,466] <org.hibernate.cfg.SettingsFactory> : Default entity-mode: pojo
{INFO } [2014-05-04 14:56:06,466] <org.hibernate.cfg.SettingsFactory> : Named query checking : enabled
{INFO } [2014-05-04 14:56:06,488] <org.hibernate.impl.SessionFactoryImpl> : building session factory
{INFO } [2014-05-04 14:56:06,659] <org.hibernate.impl.SessionFactoryObjectFactory> : Not binding factory to JNDI, no JNDI name configured
{INFO } [2014-05-04 14:56:06,664] <org.hibernate.tool.hbm2ddl.SchemaUpdate> : Running hbm2ddl schema update
{INFO } [2014-05-04 14:56:06,664] <org.hibernate.tool.hbm2ddl.SchemaUpdate> : fetching database metadata
{INFO } [2014-05-04 14:56:06,665] <org.hibernate.tool.hbm2ddl.SchemaUpdate> : updating schema
{INFO } [2014-05-04 14:56:06,678] <org.hibernate.tool.hbm2ddl.TableMetadata> : table found: apn.apn_user
{INFO } [2014-05-04 14:56:06,678] <org.hibernate.tool.hbm2ddl.TableMetadata> : columns: [created_date, id, username, updated_date, email, name, password]
{INFO } [2014-05-04 14:56:06,678] <org.hibernate.tool.hbm2ddl.TableMetadata> : foreign keys: []
{INFO } [2014-05-04 14:56:06,678] <org.hibernate.tool.hbm2ddl.TableMetadata> : indexes: [username, primary]
{INFO } [2014-05-04 14:56:06,679] <org.hibernate.tool.hbm2ddl.SchemaUpdate> : schema update complete
{INFO } [2014-05-04 14:56:06,703] <org.springframework.orm.hibernate3.HibernateTransactionManager> : Using DataSource [org.apache.commons.dbcp.BasicDataSource@588d16eb] of Hibernate SessionFactory for HibernateTransactionManager
{DEBUG} [2014-05-04 14:56:06,802] <org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder> : LinkedHashMap is an ordered map.
{INFO } [2014-05-04 14:56:06,836] <org.androidpn.server.xmpp.XmppServer> : Spring Configuration loaded.
{INFO } [2014-05-04 14:56:06,836] <org.androidpn.server.xmpp.XmppServer> : XmppServer started: 127.0.0.1
{INFO } [2014-05-04 14:56:06,836] <org.androidpn.server.xmpp.XmppServer> : Androidpn Server v0.5.0
{DEBUG} [2014-05-04 14:56:06,892] <org.androidpn.server.console.controller.UserController> : Found action method [public org.springframework.web.servlet.ModelAndView org.androidpn.server.console.controller.UserController.list(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.lang.Exception]
{DEBUG} [2014-05-04 14:56:06,906] <org.androidpn.server.console.controller.SessionController> : Found action method [public org.springframework.web.servlet.ModelAndView org.androidpn.server.console.controller.SessionController.list(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.lang.Exception]

# dispatcher 初始化完毕。
{INFO } [2014-05-04 14:56:06,937] <org.springframework.web.servlet.DispatcherServlet> : FrameworkServlet 'dispatcher': initialization completed in 1490 ms

# 对应 tomcat/conf/server.xml中的 Connector 8080端口
May 04, 2014 2:56:09 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]

# 对应 tomcat/conf/server.xml中的 Connector 8009端口
May 04, 2014 2:56:09 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]

# 对应 tomcat/conf/server.xml中的 Server
May 04, 2014 2:56:09 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 9459 ms

<启动完成>


转载于:https://my.oschina.net/qiuhoude/blog/368416

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值