Activity启动流程

学习Android,有的东西总是绕不过,比如Acticity的启动流程源码学习,这个之前面试的时候准备看,但是发现太复杂了,没太多时间,现在找到新工作了,接触Replugin的时候涉及到,Activity的启动,就顺道一起学习下。

Activity的启动流程:

不同的Android版本启动流程上代码会有不同,本文基于Android 11,学习activity的启动流程

Activity启动client端调用流程:

当用户在界面手点击屏幕上的应用图标时,应用程序的MainActivity是由Launcher启动的,

    boolean startActivitySafely(View v, Intent intent, Object tag) {
        boolean success = false;
        try {
            success = startActivity(v, intent, tag);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
        }
        return success;
    }

Launcher在启动应用程序的时候,会调用上面的startActivitySafely来启动对应的Activity

这个startActivitySafely我们可以看到对startActivity启动的时候catch了ActivityNotFoundException的异常,这个很重要,因为有时候拉起的activity不存在也不至于你自己的app崩溃了

系统中应用安装的时候,PackageManagerService会对应用的AndroidManifest文件进行扫描,会获取到对应的组件信息,Launcher会通过Action和Category去查询所有对应的组件信息,也包括图标,这样用户点击的时候就会去启动对应的Activity。

    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        ...
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }

startActivity最终会去调用Activity#startActivityForResult,-1表示不需要执行结果

  1. Activity#startActivityForResult
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
         	...
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

上面的代码中mParent代表的是ActivityGroup,ActivityGroup醉在用来在一个界面中引入多个activity,但是在api13中已经废弃了,系统推荐使用Fragment来代替ActivityGroup,因此mParent就会为null,所以我们关注半部分mParent==null的逻辑即可。
2. Instrumentation#execStartActivity
if逻辑中调用到了Instrumentation#execStartActivity函数

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
     	...
        try {
            intent.migrateExtraStreamToClipData(who);
            intent.prepareToLeaveProcess(who);
            int result = ActivityTaskManager.getService().startActivity(whoThread,
                    who.getBasePackageName(), who.getAttributionTag(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                    target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }
  • Instrumentation: 该类负责Activity和Application的创建和生命周期的调用,同时监控系统与应用程序的所有交互

上面的代码可以看到调用了ActivityTaskManager.getService().startActivity()获取ActivityTaskManagerService去启动activity,

Android10及以前,启动Activity这里使用的是AMS,可能不想AMS的代码过于复杂,11就将原本AMS中的
System service for managing activities and their containers (task, stacks, displays,… ).
activity的task,stack,显示相关的功能分离到ActivityTaskManagerService中进行

调用了ActivityTaskManager.getService().startActivity()去启动Activity之后,会返回服务端启动的result,checkStartActivityResult会对服务端启动结果进行校验,然后返回。

ActivityTaskManager.getService()返回的是一个IActivityTaskManager对象,用于跨进城调用ATMS的startActivity方法

/** @hide */
public static IActivityTaskManager getService() {
	return IActivityTaskManagerSingleton.get();
}
  • IActivityTaskManager对象是ATMS在app进程中的代理对象,ActivityTaskManagerService实现了IActivityTaskManager.Stub。
client端调用流程

在这里插入图片描述

服务端调用流程

ATMS#startActivity会调用到下面的函数中ActivityTaskManagerService#startActivityAsUser::

    private int startActivityAsUser(IApplicationThread caller, String callingPackage,
            @Nullable String callingFeatureId, Intent intent, String resolvedType,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
        assertPackageMatchesCallingUid(callingPackage);
        enforceNotIsolatedCaller("startActivityAsUser");

        userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
                Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");

        // TODO: Switch to user app stacks here.
        return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setCallingFeatureId(callingFeatureId)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setUserId(userId)
                .execute();
               

    }

上面的return语句构造了ActivityStarter,这个类携带了相关的参数,用于确定intent和flag应该如何转换为activity,以及堆栈的转换逻辑。

ActivityStarter#execute函数中,

    int execute() {
        try {
            // Refuse possible leaked file descriptors
            if (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) {
                throw new IllegalArgumentException("File descriptors passed in Intent");
            }
			...
            // If the caller hasn't already resolved the activity, we're willing
            // to do so here. If the caller is already holding the WM lock here,
            // and we need to check dynamic Uri permissions, then we're forced
            // to assume those permissions are denied to avoid deadlocking.
            if (mRequest.activityInfo == null) {
                mRequest.resolveActivity(mSupervisor);
            }

            int res;
            synchronized (mService.mGlobalLock) {
              	...
                res = executeRequest(mRequest);
     			...	
        } finally {
            onExecutionComplete();
        }
    }

ActivityStarter#execute主要执行逻辑在ActivityStarter#executeRequest中:

     mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
                request.voiceInteractor, startFlags, true /* doResume */, checkedOptions, inTask,
                restrictedBgActivity, intentGrants);

ActivityStarter#executeRequest方法中会去调用ActivityStarter#startActivityUnchecked启动activity

    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, Task inTask,
                boolean restrictedBgActivity, NeededUriGrants intentGrants) {
      		...
            result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
  			...
        postStartActivityProcessing(r, result, startedActivityStack);

        return result;
    }

调用了ActivityStarter#startActivityInner之后,

   int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, Task inTask,
            boolean restrictedBgActivity, NeededUriGrants intentGrants) {
        setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor, restrictedBgActivity);
            ...
                mRootWindowContainer.resumeFocusedStacksTopActivities(
                        mTargetStack, mStartActivity, mOptions);
            ...
        return START_SUCCESS;
    }

startActivityInner启动了一个活动,并去判断这个活动是否应该添加到现有活动的顶部,最后关键处调用RootWindowContainer#resumeFocusedStacksTopActivities

    boolean resumeFocusedStacksTopActivities(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {

        if (!mStackSupervisor.readyToResume()) {
            return false;
        }

        boolean result = false;
        if (targetStack != null && (targetStack.isTopStackInDisplayArea()
                || getTopDisplayFocusedStack() == targetStack)) {
            result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
        ...
            if (!resumedOnDisplay) {
                // In cases when there are no valid activities (e.g. device just booted or launcher
                // crashed) it's possible that nothing was resumed on a display. Requesting resume
                // of top activity in focused stack explicitly will make sure that at least home
                // activity is started and resumed, and no recursion occurs.
                final ActivityStack focusedStack = display.getFocusedStack();
                if (focusedStack != null) {
                    result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
                } else if (targetStack == null) {
                    result |= resumeHomeActivity(null /* prev */, "no-focusable-task",
                            display.getDefaultTaskDisplayArea());
                }
            }
        }

        return result;
    }

接着又会去调用:ActivityStack#resumeTopActivityUncheckedLocked

   boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mInResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion.
            mInResumeTopActivity = true;
            result = resumeTopActivityInnerLocked(prev, options);
			
            // When resuming the top activity, it may be necessary to pause the top activity (for
            // example, returning to the lock screen. We suppress the normal pause logic in
            // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
            // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here
            // to ensure any necessary pause logic occurs. In the case where the Activity will be
            // shown regardless of the lock screen, the call to
            // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
            final ActivityRecord next = topRunningActivity(true /* focusableOnly */);
            if (next == null || !next.canTurnScreenOn()) {
                checkReadyForSleep();
            }
        } finally {
            mInResumeTopActivity = false;
        }

        return result;
    }

  • 这里的函数名称带了个Locked,这个不太清楚啥意思,看网上有一些人解释这个含义,但是还是感觉不太对,大家有谁知道的话希望能帮忙解答下疑惑,感谢,🙏:链接:https://zhidao.baidu.com/question/917939701567497939.html
    ActivityStack#resumeTopActivityInnerLocked
    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        ...
        boolean pausing = taskDisplayArea.pauseBackStacks(userLeaving, next);
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false /* uiSleeping */, next);
        }
               ...
                next.completeResumeLocked();
            } catch (Exception e) {
                // If any exception gets thrown, toss away this
                // activity and try the next one.
                Slog.w(TAG, "Exception thrown during resume of " + next, e);
                next.finishIfPossible("resume-exception", true /* oomAdj */);
                return true;
            }
        }	...
            mStackSupervisor.startSpecificActivity(next, true, true);
        }
        return true;
    }

resumeTopActivityInnerLocked函数会进行activity的pause和下一个activityResume的一些操作,然后启动对应的Activity mStackSupervisor.startSpecificActivity, mStackSupervisor.是ActivityStackSupervisor的实例。

  • ActivityStackSupervisor:负责所有ActivityStack的管理。

ActivityStackSupervisor#startSpecificActivity会调用到

  boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
            boolean andResume, boolean checkConfig) throws RemoteException {

                   ...
                  //1 
                // Create activity launch transaction.
                final ClientTransaction clientTransaction = ClientTransaction.obtain(
                        proc.getThread(), r.appToken);

                final DisplayContent dc = r.getDisplay().mDisplayContent;
               //2
                 clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        // TODO: Have this take the merged configuration instead of separate global
                        // and override configs.
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                        r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
                        dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                        r.assistToken, r.createFixedRotationAdjustmentsIfNeeded()));

                // Set desired final state.
                final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Schedule transaction.
                //3
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);

              ...

        return true;
    }

上面的代码中首先创建了一个ClientTransition对象,然后为clientTransition添加了一个callback对象,并将这个对象提交到ClientLifecycleManager中。

  • ClientTransaction:保存一系列消息的容器,这些消息可能被发送到客户端,可以用这个对象和客户端交互。

ClientLifecycleManager#scheduleTransaction

    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        ...
    }

ClientTransaction#schedule的代码如下,

    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

其实mClient其实就是
IApplicationThread这个AIDL接口,具体的实现类其实是ActivityThread的内部类ApplicationThread, mClient.scheduleTransaction(this),会去调用app中的ApplicationThread中的scheduleTransaction方法,

  • IApplicationThread:是系统进程持有的app进程中ApplicationThread的Binder代理对象。系统进程通过ProcessRecord.IApplicationThread调用app进程相关方法。
  • ApplicationThread:ActivityThread的内部类,继承了IApplicationThread.Stub。AMS通过binder代理调用到ApplicationThread中的方法后,通过主线程(ActivityThread中的main方法)中开启的handler消息轮询来通知主线程调用相关方法。主线程的相关生命周期方法的具体实现会委托给Instrumentation类实现,在Instrumentation类中,会调用具体组件的相关生命周期方法。

在这里插入图片描述
mClient.scheduleTransaction会回掉客户端的ApplicationThread,

客户端处理

ApplicationThread#scheduleTransaction的代码如下:

@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
    ActivityThread.this.scheduleTransaction(transaction);
}
  • ActivityThread: 运行在UI线程(主线程)。它管理应用程序进程中主线程的执行、调度和执行activities、broadcasts以及activity manager请求的其他操作。

上面代码中调用了ActivityThread#scheduleTransaction,但是ActivityThread中并没有scheduleTransaction方法的实现,不过ActivityThread是ClientTransactionHandler的子类,所以调用了ClientTransactionHandler#scheduleTransaction方法。

 void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }
  • ClientTransactionHandler:定义客户端事务,即ClientTransaction可以在客户端执行的事务的操作。是ActivityThread的父类。

ActivityThread.H会去调用H类中定义的处理消息的handlerMessage方法

ActivityThread#H#handleMessage

                case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    mTransactionExecutor.execute(transaction);
                    if (isSystem()) {
                        // Client transactions inside system process are recycled on the client side
                        // instead of ClientLifecycleManager to avoid being cleared before this
                        // message is handled.
                        transaction.recycle();
                    }
                    // TODO(lifecycler): Recycle locally scheduled transactions.
                    break;

TransactionExecutor事务执行器的execute()方法并把ClientTransaction 作为参数,execute方法代码如下:

TransactionExecutor#execute

    public void execute(ClientTransaction transaction) {
        if (DEBUG_RESOLVER) Slog.d(TAG, tId(transaction) + "Start resolving transaction");
        ...
        //1
        executeCallbacks(transaction);
        //2
        executeLifecycleState(transaction);
        ...
    }

这个函数中主要有两个主要的逻辑,executeCallbacks是执行traction中添加的callback逻辑,executeLifecycleState是执行activity自己的生命周期逻辑,

    public void executeCallbacks(ClientTransaction transaction) {
        final List<ClientTransactionItem> callbacks = transaction.getCallbacks();
         ...
        for (int i = 0; i < size; ++i) {
            final ClientTransactionItem item = callbacks.get(i);
             ...
            if (closestPreExecutionState != UNDEFINED) {
                cycleToPath(r, closestPreExecutionState, transaction);
            }

             //1
            item.execute(mTransactionHandler, token, mPendingActions);
            item.postExecute(mTransactionHandler, token, mPendingActions);
           ...
        }
    }

ClientTransactionItem的item变量方法是在上面realStartActivityLocked中构造的LaunchActivityItem对象,
LaunchActivityItem#execute

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

execute函数会去调用client的handleLaunchActivity函数,这里的client调用的时候传入的是mTransactionHandler,查看代码可以看到,这个就是ActivithThread对象的引用,因为handler继承了ClientTransactionHandler

private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);

ActivityThread#handleLaunchActivity函数

    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
            ...
            final Activity a = performLaunchActivity(r, customIntent);
            ...

ActivityThread#performLaunchActivity会加载需要的activity,同时创建所需要的activity实例对象,

    /**  Core implementation of activity launch. */
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ...
        
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
        	//加载对应的activity类
            java.lang.ClassLoader cl = appContext.getClassLoader();
            //创建实例activity对象
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                    "Unable to instantiate activity " + component
                    + ": " + e.toString(), e);
            }
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
            if (localLOGV) Slog.v(
                    TAG, r + ": app=" + app
                    + ", appName=" + app.getPackageName()
                    + ", pkg=" + r.packageInfo.getPackageName()
                    + ", comp=" + r.intent.getComponent().toShortString()
                    + ", dir=" + r.packageInfo.getAppDir());

            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }

                // Activity resources must be initialized with the same loaders as the
                // application context.
                appContext.getResources().addLoaders(
                        app.getResources().getLoaders().toArray(new ResourcesLoader[0]));

                appContext.setOuterContext(activity);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback,
                        r.assistToken);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                ...

        return activity;
    }

上面的代码会创建activity的实例,并且调用对应的activityonCreate方法,
Instrumentation#callActivityOnCreate

    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }

Activity.performCreate会调用到Activity#onCreate函数,

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
       	...
        if (persistentState != null) {
            onCreate(icicle, persistentState);
        } else {
            onCreate(icicle);
        }
    }

客户端处理流程图:
在这里插入图片描述

到这里activity就走到了自己的onCreate生命周期。android源码中的东西相当复杂,我们在看代码的时候要对主要的流程有一个好的把控,不然最后会迷失在代码的细节中,掌握了流程,后面需要看细节或者修改代码我们也知道从哪里入手。

参考:
https://blog.csdn.net/guojingbu/article/details/117259786
https://blog.csdn.net/yu749942362/article/details/107978083
https://blog.csdn.net/to_perfect/article/details/117754718

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值