Processes and Threads

When an application component starts and the application does not have any other components running, the Android system starts a new Linux process for the application with a single thread of execution. By default, all components of the same application run in the same process and thread (called the "main" thread). If an application component starts and there already exists a process for that application (because another component from the application exists), then the component is started within that process and uses the same thread of execution. However, you can arrange for different components in your application to run in separate processes, and you can create additional threads for any process.

This document discusses how processes and threads work in an Android application.

Processes


By default, all components of the same application run in the same process and most applications should not change this. However, if you find that you need to control which process a certain component belongs to, you can do so in the manifest file.

The manifest entry for each type of component element—<activity><service><receiver>, and<provider>—supports an android:process attribute that can specify a process in which that component should run. You can set this attribute so that each component runs in its own process or so that some components share a process while others do not. You can also set android:process so that components of different applications run in the same process—provided that the applications share the same Linux user ID and are signed with the same certificates.

The <application> element also supports an android:process attribute, to set a default value that applies to all components.

Android might decide to shut down a process at some point, when memory is low and required by other processes that are more immediately serving the user. Application components running in the process that's killed are consequently destroyed. A process is started again for those components when there's again work for them to do.

When deciding which processes to kill, the Android system weighs their relative importance to the user. For example, it more readily shuts down a process hosting activities that are no longer visible on screen, compared to a process hosting visible activities. The decision whether to terminate a process, therefore, depends on the state of the components running in that process. The rules used to decide which processes to terminate is discussed below.

Process lifecycle

The Android system tries to maintain an application process for as long as possible, but eventually needs to remove old processes to reclaim memory for new or more important processes. To determine which processes to keep and which to kill, the system places each process into an "importance hierarchy" based on the components running in the process and the state of those components. Processes with the lowest importance are eliminated first, then those with the next lowest importance, and so on, as necessary to recover system resources.

There are five levels in the importance hierarchy. The following list presents the different types of processes in order of importance (the first process is most important and is killed last):

  1. Foreground process

    A process that is required for what the user is currently doing. A process is considered to be in the foreground if any of the following conditions are true:

    Generally, only a few foreground processes exist at any given time. They are killed only as a last resort—if memory is so low that they cannot all continue to run. Generally, at that point, the device has reached a memory paging state, so killing some foreground processes is required to keep the user interface responsive.

  2. Visible process

    A process that doesn't have any foreground components, but still can affect what the user sees on screen. A process is considered to be visible if either of the following conditions are true:

    • It hosts an Activity that is not in the foreground, but is still visible to the user (its onPause() method has been called). This might occur, for example, if the foreground activity started a dialog, which allows the previous activity to be seen behind it.
    • It hosts a Service that's bound to a visible (or foreground) activity.

    A visible process is considered extremely important and will not be killed unless doing so is required to keep all foreground processes running.

  3. Service process

    A process that is running a service that has been started with the startService() method and does not fall into either of the two higher categories. Although service processes are not directly tied to anything the user sees, they are generally doing things that the user cares about (such as playing music in the background or downloading data on the network), so the system keeps them running unless there's not enough memory to retain them along with all foreground and visible processes.

  4. Background process

    A process holding an activity that's not currently visible to the user (the activity's onStop() method has been called). These processes have no direct impact on the user experience, and the system can kill them at any time to reclaim memory for a foreground, visible, or service process. Usually there are many background processes running, so they are kept in an LRU (least recently used) list to ensure that the process with the activity that was most recently seen by the user is the last to be killed. If an activity implements its lifecycle methods correctly, and saves its current state, killing its process will not have a visible effect on the user experience, because when the user navigates back to the activity, the activity restores all of its visible state. See the Activities document for information about saving and restoring state.

  5. Empty process

    A process that doesn't hold any active application components. The only reason to keep this kind of process alive is for caching purposes, to improve startup time the next time a component needs to run in it. The system often kills these processes in order to balance overall system resources between process caches and the underlying kernel caches.

Android ranks a process at the highest level it can, based upon the importance of the components currently active in the process. For example, if a process hosts a service and a visible activity, the process is ranked as a visible process, not a service process.

In addition, a process's ranking might be increased because other processes are dependent on it—a process that is serving another process can never be ranked lower than the process it is serving. For example, if a content provider in process A is serving a client in process B, or if a service in process A is bound to a component in process B, process A is always considered at least as important as process B.

Because a process running a service is ranked higher than a process with background activities, an activity that initiates a long-running operation might do well to start a service for that operation, rather than simply create a worker thread—particularly if the operation will likely outlast the activity. For example, an activity that's uploading a picture to a web site should start a service to perform the upload so that the upload can continue in the background even if the user leaves the activity. Using a service guarantees that the operation will have at least "service process" priority, regardless of what happens to the activity. This is the same reason that broadcast receivers should employ services rather than simply put time-consuming operations in a thread.

Threads


When an application is launched, the system creates a thread of execution for the application, called "main." This thread is very important because it is in charge of dispatching events to the appropriate user interface widgets, including drawing events. It is also the thread in which your application interacts with components from the Android UI toolkit (components from the android.widget and android.view packages). As such, the main thread is also sometimes called the UI thread.

The system does not create a separate thread for each instance of a component. All components that run in the same process are instantiated in the UI thread, and system calls to each component are dispatched from that thread. Consequently, methods that respond to system callbacks (such as onKeyDown() to report user actions or a lifecycle callback method) always run in the UI thread of the process.

For instance, when the user touches a button on the screen, your app's UI thread dispatches the touch event to the widget, which in turn sets its pressed state and posts an invalidate request to the event queue. The UI thread dequeues the request and notifies the widget that it should redraw itself.

When your app performs intensive work in response to user interaction, this single thread model can yield poor performance unless you implement your application properly. Specifically, if everything is happening in the UI thread, performing long operations such as network access or database queries will block the whole UI. When the thread is blocked, no events can be dispatched, including drawing events. From the user's perspective, the application appears to hang. Even worse, if the UI thread is blocked for more than a few seconds (about 5 seconds currently) the user is presented with the infamous "application not responding" (ANR) dialog. The user might then decide to quit your application and uninstall it if they are unhappy.

Additionally, the Andoid UI toolkit is not thread-safe. So, you must not manipulate your UI from a worker thread—you must do all manipulation to your user interface from the UI thread. Thus, there are simply two rules to Android's single thread model:

  1. Do not block the UI thread
  2. Do not access the Android UI toolkit from outside the UI thread

Worker threads

Because of the single thread model described above, it's vital to the responsiveness of your application's UI that you do not block the UI thread. If you have operations to perform that are not instantaneous, you should make sure to do them in separate threads ("background" or "worker" threads).

For example, below is some code for a click listener that downloads an image from a separate thread and displays it in an ImageView:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView.setImageBitmap(b);
        }
    }).start();
}

At first, this seems to work fine, because it creates a new thread to handle the network operation. However, it violates the second rule of the single-threaded model: do not access the Android UI toolkit from outside the UI thread—this sample modifies the ImageView from the worker thread instead of the UI thread. This can result in undefined and unexpected behavior, which can be difficult and time-consuming to track down.

To fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:

For example, you can fix the above code by using the View.post(Runnable) method:

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap =
                    loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

Now this implementation is thread-safe: the network operation is done from a separate thread while theImageView is manipulated from the UI thread.

However, as the complexity of the operation grows, this kind of code can get complicated and difficult to maintain. To handle more complex interactions with a worker thread, you might consider using a Handler in your worker thread, to process messages delivered from the UI thread. Perhaps the best solution, though, is to extend the AsyncTask class, which simplifies the execution of worker thread tasks that need to interact with the UI.

Using AsyncTask

AsyncTask allows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.

To use it, you must subclass AsyncTask and implement the doInBackground() callback method, which runs in a pool of background threads. To update your UI, you should implement onPostExecute(), which delivers the result from doInBackground() and runs in the UI thread, so you can safely update your UI. You can then run the task by calling execute() from the UI thread.

For example, you can implement the previous example using AsyncTask this way:

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }

    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}

Now the UI is safe and the code is simpler, because it separates the work into the part that should be done on a worker thread and the part that should be done on the UI thread.

You should read the AsyncTask reference for a full understanding on how to use this class, but here is a quick overview of how it works:

Caution: Another problem you might encounter when using a worker thread is unexpected restarts in your activity due to a runtime configuration change (such as when the user changes the screen orientation), which may destroy your worker thread. To see how you can persist your task during one of these restarts and how to properly cancel the task when the activity is destroyed, see the source code for the Shelves sample application.

Thread-safe methods

In some situations, the methods you implement might be called from more than one thread, and therefore must be written to be thread-safe.

This is primarily true for methods that can be called remotely—such as methods in a bound service. When a call on a method implemented in an IBinder originates in the same process in which the IBinder is running, the method is executed in the caller's thread. However, when the call originates in another process, the method is executed in a thread chosen from a pool of threads that the system maintains in the same process as theIBinder (it's not executed in the UI thread of the process). For example, whereas a service's onBind() method would be called from the UI thread of the service's process, methods implemented in the object that onBind()returns (for example, a subclass that implements RPC methods) would be called from threads in the pool. Because a service can have more than one client, more than one pool thread can engage the same IBindermethod at the same time. IBinder methods must, therefore, be implemented to be thread-safe.

Similarly, a content provider can receive data requests that originate in other processes. Although theContentResolver and ContentProvider classes hide the details of how the interprocess communication is managed, ContentProvider methods that respond to those requests—the methods query()insert(),delete()update(), and getType()—are called from a pool of threads in the content provider's process, not the UI thread for the process. Because these methods might be called from any number of threads at the same time, they too must be implemented to be thread-safe.

Interprocess Communication


Android offers a mechanism for interprocess communication (IPC) using remote procedure calls (RPCs), in which a method is called by an activity or other application component, but executed remotely (in another process), with any result returned back to the caller. This entails decomposing a method call and its data to a level the operating system can understand, transmitting it from the local process and address space to the remote process and address space, then reassembling and reenacting the call there. Return values are then transmitted in the opposite direction. Android provides all the code to perform these IPC transactions, so you can focus on defining and implementing the RPC programming interface.

To perform IPC, your application must bind to a service, using bindService(). For more information, see theServices developer guide.


当应用程序组件启动,应用程序没有任何其他组件上运行,Android系统启动与执行单线程应用一个新的Linux进程。缺省情况下,同一应用程序的所有组件在同一进程和线程(称为“主”线程)运行。如果一个应用程序组件开始,已经存在该应用程序的处理(因为从应用另一种组分存在),则该组件被该过程中开始,并使用执行相同的线程。但是,您可以安排您的应用程序在单独的进程中运行不同的组件,并且您可以为任意程序创建额外的线程。

本文讨论进程和线程如何在Android应用程序。

流程


缺省情况下,同一应用程序的所有组件在同一进程中运行,并且大多数应用程序不应该改变这种情况。但是,如果你发现你需要控制其处理某个部件属于,你可以在清单文件中这样做。

对于每种类型的组件元素-的清单项< 活动><服务><接收器><提供商> -支持的Android版 ​​本:进程是可以指定该组件应该运行的过程属性。使每个组件在自己的进程中运行,或使一些组件共享一个进程,而别人没有,你可以设置该属性。还可以设置 机器人:过程,使得不同的应用程序组件在运行相同过程提供的应用程序共享相同的Linux用户ID和相同的证书签名。

<应用程序>元素还支持安卓过程属性,设置适用于所有组件的默认值。

机器人可能决定在某一时刻关闭的过程中,当存储器是低并要求被更直接服务于用户的其它过程。在这个过程中运行的应用程序丧生组件都因此被毁。一个进程再次启动的时候再有工作给他们做这些组件。

在决定哪些进程杀掉,Android系统的重量为他们的用户相对重要性。例如,它更容易关闭托管不再显示在屏幕上,相比于托管可见活动的处理活动的方法。是否要终止一个进程的决定,因此,依赖于在该进程中运行的组件的状态。用于决定哪些进程终止规则将在下面讨论。

流程生命周期

Android系统试图维持一个应用程序,只要有可能,但最终需要删除旧的流程,以回收内存为新的或更重要的进程。以确定哪些进程保持和杀,系统中的每个过程放置到基于在过程和这些组件的运行状态的部件的“重要性的层次结构”。具有最低重要性过程首先消除,那么那些具有下一个最低重要性等,在必要时恢复系统资源。

中有重要的层次结构五个等级。下面列出了不同类型的进程按重要性顺序(第一个过程是最重要的,并 杀死最后一个):

  1. 前台进程

    所需什么用户目前正在做的方法。进程被认为是如果任一下列条件,则前景:

    通常,只有几前台进程在任何给定时间存在。他们被杀害只能作为最后的手段,如果内存是如此之低,他们不可能全部继续运行。通常,在这一点上,该装置已经达到了一个内存分页的状态,所以杀死某些前台进程需要保持用户界面的响应。

  2. 可见过程

    不具有任何前台组件,但仍的方法,可能会影响用户看到在屏幕上的内容。进程被认为是如果以下任一条件,则可见:

    • 它承载的活动不是在前台,但仍是可见的用户(其的onPause()方法被调用)。这可能会发生,例如,如果前景活动开始对话,这使得它的后面被视为以前的活性。
    • 它承载了服务,绑定到一个可见的(或前景)的活动。

    一个可视进程被认为是极其重要的,除非这样做是需要保持所有前台进程运行,就不会被撞死。

  3. 服务流程

    运行已经开始与一个服务的过程startService()方法,并且不会陷入要么两个较高类别。虽然服务过程不直接依赖于任何用户看到的,他们一般做的事情,用户关心的(如播放的背景音乐,或在网络上下载数据),这样系统保持运行它们,除非有没有足够的内存留住他们连同所有前台和可视进程。

  4. 后台进程

    一个过程拿着一个活动,这不是当前对用户可见(活动的 的onStop()方法被调用)。这些方法具有对用户体验没有直接的影响,并且该系 ​​统可以在任何时候杀死它们回收内存为前景,可见光或服务的过程。通常有许多运行后台进程,因此它们被保存在一个LRU(最近最少使用)列表,以确保与最近一次被用户看到该活动的过程是最后被杀害。如果一个activity正确的实现它的生命周期方法,并保存其当前状态,杀死它的进程不会对用户体验明显的效果,因为当用户返回到活动,该活动将恢复其所有可见的状态。请参阅活动 有关保存和恢复状态信息的文件。

  5. 空进程

    不持有任何活动应用组件的过程。保持这种进程活着的唯一原因是为了缓存目的,下一次组件需要在运行它以提高启动时间。该系统往往为了处理高速缓存和底层的内核缓存之间平衡整个系统的资源,杀死这些进程。

机器人排在它可以最高水平的方法,基于目前的过程中活性组分的重要性。例如,如果一个进程承载服务和可见光活性,该过程被列为一个可见的过程,而不是一个服务进程。

另外,一个过程的排名将会增加,因为其它工艺依赖于它-一个正服务另一个进程处理永远不能被排名比它所服务的进程低。例如,如果在处理A的内容提供者的服务中处理B的客户机,或者如果在过程A中的服务绑定到在过程B中的成分,处理A始终被认为至少进程B一样重要

由于运行的服务的进程比后台活动的过程中,启动一个长期运行的操作可能会做的很好开始的活动排名靠前服务,该操作,而不是简单地创建一个工作线程,特别是如果操作将可能经久活动。例如,这是上载的图像到一个网站的活性应该启动一个服务来执行上载,以便上载可以在后台继续即使用户离开活性。使用服务保证了操作具有至少“服务进程”优先,不管发生什么情况的活性。这是广播接收器应该使用服务,而不是简单地把费时的操作在一个线程一样的道理。

主题


当一个应用程序启动时,系统会创建执行应用程序的线程,称为“主”。此线是非常重要的,因为它是负责调度事件到相应的用户界面部件,包括绘图事件。也正是在您的应用程序从Android的UI工具包(从组件组件交互线程android.widgetandroid.view包)。这样,主线程有时也被称为UI线程。

系统并不能为一个组件的每个实例创建一个单独的线程。在同一进程中运行的所有组件被实例化在UI线程,和系统调用每个部件都从该线程调度。因此,该系统响应回调方法(如的onkeydown()报告用户操作或一个生命周期回调方法)的过程中的UI线程始终运行。

例如,当用户触摸屏幕上的按钮,应用的UI线程调度触摸事件的部件,这反过来将它的按压状态和支柱的无效请求到事件队列中。在UI线程取消请求排队,并通知应重绘自己的小部件。

当你的应用程序中执行响应用户交互密集的工作,这个单一线程模型可以产生除非你正确地执行你的应用性能不佳。特别是,如果一切都在UI线程中发生的事情,执行长时间的操作,如网络访问或数据库查询将阻止整个UI。当线程被阻塞,任何事件可以被调度,包括绘图事件。从用户的角度看,应用程序似乎挂起。更糟的是,如果UI线程被阻塞超过几秒钟(目前大约5秒)的用户呈现臭名昭著的“ 应用程序没有响应 ”(ANR)对话。然后,用户可以决定退出应用程序,并卸载它,如果他们不高兴。

此外,Andoid UI工具包是不是线程安全的。所以,你不能操纵从一个工人的UI线程,你必须做的所有操作,以从UI线程的用户界面。因此,仅仅是两个规则Android的单线程模型:

  1. 不要阻塞UI线程
  2. 不要从UI线程外部访问Android的UI工具包

工作线程

由于上述的单线程模型,这是你的应用程序的UI,你不阻塞UI线程响应是至关重要的。如果你有操作执行无法瞬间,你应该确保做他们在单独的线程(“背景”或“工人”线程)。

例如,下面是对从一个单独的线程下载的图像,并显示在一个一个点击监听一些代码ImageView的

public  void onClick ( View v )  { 
    new  Thread ( new  Runnable ()  { 
        public  void run ()  { 
            Bitmap b = loadImageFromNetwork ( "http://example.com/image.png" ); 
            mImageView . setImageBitmap ( b ); 
        } 
    }). start (); 
}

起初,这似乎做工精细,因为它创造了一个新的线程来处理网络操作。但是,它违反了单线程模型的第二条规则:不要从外部UI线程访问Android的UI工具包 -这样修改的ImageView从工作线程,而不是UI线程。这可能导致未定义和意外的行为,这可能是困难和耗时的追查。

要解决这个问题,Android提供了几种方法可以从其他线程访问UI线程。这里有方法可以帮助一个列表:

例如,您可以通过使用修复上面的代码View.post(Runnable接口)方法:

public  void onClick ( View v )  { 
    new  Thread ( new  Runnable ()  { 
        public  void run ()  { 
            final  Bitmap bitmap = 
                    loadImageFromNetwork ( "http://example.com/image.png" ); 
            mImageView . post ( new  Runnable ()  { 
                public  void run ()  { 
                    mImageView . setImageBitmap ( bitmap ); 
                } 
            }); 
        } 
    }). start (); 
}

现在这个实现是线程安全的:网络运行是由一个单独的线程完成,而ImageView的是从UI线程操作。

然而,由于操作的复杂性的增长,这种代码可以得到复杂和难以维护。为了处理一个工作线程更复杂的交互,你可以考虑使用一个处理程序在你的工作线程,以处理来自UI线程传递的消息。也许是最好的解决方案,不过,是延长的AsyncTask类,它简化了需要与UI交互工作线程任务的执行。

使用AsyncTask的

AsyncTask的允许您在用户界面上执行异步工作。它在辅助线程执行阻塞操作,然后发布在UI线程上的结果,而不需要你来处理线程和/或处理自己。

要使用它,你必须继承的AsyncTask并实现doInBackground()回调方法,它运行在后台线程池。要更新你的UI,你应该实现onPostExecute() ,它从提供的结果doInBackground()并运行在UI线程,所以你可以安全地更新UI。然后,您可以运行通过调用任务的execute() 从UI线程。

例如,你可以使用实现前面的例子AsyncTask的是这样的:

public  void onClick ( View v )  { 
    new  DownloadImageTask (). execute ( "http://example.com/image.png" ); 
} 

private  class  DownloadImageTask  extends  AsyncTask < String ,  Void ,  Bitmap >  { 
    /**系统调用这在辅助线程执行工作和
      *提供IT)给予AsyncTask.execute(参数* / 
    保护的 位图doInBackground 字符串... 网址 { 
        返回loadImageFromNetwork 网址[ 0 ]); 
    } 

    / **该系统调用它来 ​​在UI线程中执行工作,交付
      *从doInBackground()* /结果
    保护的 无效onPostExecute 位图的结果 { 
        mImageView setImageBitmap 结果); 
    } 
}

现在UI是安全的代码更简单,因为它工作纳入应在一个工作线程,并应在UI线程上执行的一部分来完成的部分分离出来。

你应该阅读AsyncTask的关于如何使用这个类有充分的认识参考,但这里是它如何工作的简要概述:

注意:还有一个问题使用工作线程时,在你的活动因意外重启您可能会遇到运行时配置的变化 (当用户更改屏幕方向,如),这可能会破坏你的工作线程。就看你怎么可以在这些重启之一,如何正确地取消当活动被销毁任务。在坚持你的任务,请参阅源代码货架示例应用程序。

线程安全的方法

在某些情况下,你实现的方法可以从多个线程调用,因此必须被写入到是线程安全的。

这主要是如此,可以调用远程-如在一个方法的方法结合的服务。当在一个实施方法的呼叫的IBinder在将相同的处理始发 的IBinder运行时,该方法在呼叫方的线程中执行。然而,当呼叫在另一个进程发起,该方法在从线程池,该系统中的相同的进程中保持选择的线程中执行的IBinder(它不是在该过程的UI线程执行)。例如,而一个服务的 onBind()方法将被从该服务的过程的UI线程调用时,在该对象实现的方法onBind()返回(例如,一个实现RPC方法的子类)将被从线程调用池。因为服务可以有多个客户端,多个池中的线程可以从事相同的IBinder在同一时间的方法。 的IBinder方法,因此必须实施是线程安全的。

类似地,内容提供者可以接收源自其他过程数据的请求。虽然ContentResolver的ContentProvider的 类隐藏的进程间通信的管理方式,其细节ContentProvider的那那些应对方法请求-方法查询() 插入() 删除() 更新() ,和的getType() -are称为从线程在内容提供者的处理的池,而不是为过程UI线程。因为这些方法可能会从任意数量的同时线程被调用,它们也必须被实现为线程安全的。

间通信


Android提供了用于使用远程过程调用(RPC),其中一个方法是通过一个活动或其它应用程序组件调用进程间通信(IPC)的机构,但在远程执行(在另一个过程),与任何结果返回给调用者。这需要分解的方法调用和它的数据到一个级别的操作系统能理解,从本地进程和地址空间发送它到远程进程和地址空间,然后重新组装和重演呼叫那里。返回值然后在相反的方向传送。Android提供所有的代码来执行这些IPC交易,这样你就可以专注于定义和实施的RPC编程接口。

要执行工控机,应用程序必须绑定到一个服务,使用()bindService。欲了解更多信息,请参阅服务开发者指南。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值