安卓架构设计八股文

一、MVC/MVP/MVVM 架构模式

1. MVC 架构

// Model - 数据层
public class UserModel {
    private String name;
    private int age;
    
    // getter/setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

// View - 视图层 (Activity/Fragment)
public class UserActivity extends Activity {
    private TextView nameText;
    private UserController controller;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
        
        nameText = findViewById(R.id.name_text);
        controller = new UserController(this, new UserModel());
        
        findViewById(R.id.load_btn).setOnClickListener(v -> {
            controller.loadUserData();
        });
    }
    
    // 更新UI的方法
    public void updateUserInfo(String name) {
        nameText.setText(name);
    }
}

// Controller - 控制层
public class UserController {
    private UserActivity view;
    private UserModel model;
    
    public UserController(UserActivity view, UserModel model) {
        this.view = view;
        this.model = model;
    }
    
    public void loadUserData() {
        // 模拟网络请求
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                model.setName("张三");
                
                // 切回主线程更新UI
                view.runOnUiThread(() -> {
                    view.updateUserInfo(model.getName());
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

MVC 问题

  • Activity/Fragment 同时承担 View 和 Controller 职责
  • 视图和业务逻辑耦合严重
  • 单元测试困难

2. MVP 架构

// Contract - 定义接口契约
public interface UserContract {
    interface View {
        void showUserName(String name);
        void showLoading();
        void hideLoading();
    }
    
    interface Presenter {
        void loadUserData();
        void onDestroy();
    }
}

// Model - 数据层
public class UserRepository {
    public String loadUserData() {
        // 模拟数据加载
        return "李四";
    }
}

// View - 视图层
public class UserActivity extends Activity implements UserContract.View {
    private UserContract.Presenter presenter;
    private TextView nameText;
    private ProgressBar progressBar;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user);
        
        nameText = findViewById(R.id.name_text);
        progressBar = findViewById(R.id.progress_bar);
        
        presenter = new UserPresenter(this, new UserRepository());
        
        findViewById(R.id.load_btn).setOnClickListener(v -> {
            presenter.loadUserData();
        });
    }
    
    @Override
    public void showUserName(String name) {
        nameText.setText(name);
    }
    
    @Override
    public void showLoading() {
        progressBar.setVisibility(View.VISIBLE);
    }
    
    @Override
    public void hideLoading() {
        progressBar.setVisibility(View.INVISIBLE);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.onDestroy();
    }
}

// Presenter - 表示层
public class UserPresenter implements UserContract.Presenter {
    private UserContract.View view;
    private UserRepository repository;
    private CompositeDisposable disposables;
    
    public UserPresenter(UserContract.View view, UserRepository repository) {
        this.view = view;
        this.repository = repository;
        this.disposables = new CompositeDisposable();
    }
    
    @Override
    public void loadUserData() {
        view.showLoading();
        
        Disposable disposable = Observable.fromCallable(() -> repository.loadUserData())
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                name -> {
                    view.hideLoading();
                    view.showUserName(name);
                },
                error -> {
                    view.hideLoading();
                    // 处理错误
                }
            );
        
        disposables.add(disposable);
    }
    
    @Override
    public void onDestroy() {
        disposables.dispose();
    }
}

MVP 优势

  • View 和 Presenter 通过接口通信,解耦彻底
  • Presenter 不持有 Android 引用,便于单元测试
  • 职责分离清晰

3. MVVM 架构 (Jetpack ViewModel + LiveData)

// Model - 数据层
public class UserRepository {
    public LiveData<String> loadUserData() {
        MutableLiveData<String> liveData = new MutableLiveData<>();
        
        // 模拟异步加载
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                liveData.postValue("王五");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        
        return liveData;
    }
}

// ViewModel
public class UserViewModel extends ViewModel {
    private UserRepository repository;
    private MutableLiveData<String> userName = new MutableLiveData<>();
    private MutableLiveData<Boolean> loading = new MutableLiveData<>();
    
    public UserViewModel(UserRepository repository) {
        this.repository = repository;
    }
    
    public LiveData<String> getUserName() { return userName; }
    public LiveData<Boolean> getLoading() { return loading; }
    
    public void loadUserData() {
        loading.setValue(true);
        
        repository.loadUserData().observeForever(name -> {
            userName.setValue(name);
            loading.setValue(false);
        });
    }
}

// View - Activity/Fragment
public class UserActivity extends AppCompatActivity {
    private ActivityUserBinding binding;
    private UserViewModel viewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_user);
        
        UserRepository repository = new UserRepository();
        viewModel = new ViewModelProvider(this, 
            new ViewModelProvider.Factory() {
                @Override
                public <T extends ViewModel> T create(Class<T> modelClass) {
                    return (T) new UserViewModel(repository);
                }
            }).get(UserViewModel.class);
        
        setupObservers();
        
        binding.loadBtn.setOnClickListener(v -> {
            viewModel.loadUserData();
        });
    }
    
    private void setupObservers() {
        viewModel.getUserName().observe(this, name -> {
            binding.nameText.setText(name);
        });
        
        viewModel.getLoading().observe(this, isLoading -> {
            binding.progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE);
        });
    }
}

二、Jetpack 架构组件

1. ViewModel 原理

// ViewModel 生命周期感知
public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users = new MutableLiveData<>();
    
    public MyViewModel() {
        loadUsers();
    }
    
    private void loadUsers() {
        // 数据加载,配置变化时不会重新执行
    }
    
    @Override
    protected void onCleared() {
        super.onCleared();
        // 清理资源
    }
}

// ViewModelStore 管理
public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
    public final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    
    public final ViewModel get(String key) {
        return mMap.get(key);
    }
}

2. LiveData 原理

// LiveData 核心机制
public abstract class LiveData<T> {
    private final Object mDataLock = new Object();
    private volatile Object mData = NOT_SET;
    private int mVersion = START_VERSION;
    private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = 
        new SafeIterableMap<>();
    
    // 观察者包装类
    private abstract class ObserverWrapper {
        final Observer<? super T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;
        
        ObserverWrapper(Observer<? super T> observer) {
            mObserver = observer;
        }
    }
    
    // 设置数据
    protected void setValue(T value) {
        assertMainThread("setValue");
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
    
    // 分发数据
    private void dispatchingValue(ObserverWrapper initiator) {
        // 遍历所有观察者并通知
        for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
            considerNotify(iterator.next().getValue());
            if (mDispatchInvalidated) {
                break;
            }
        }
    }
}

// MediatorLiveData - 合并多个 LiveData
public class CombinedLiveData extends MediatorLiveData<CombinedData> {
    private String source1;
    private Integer source2;
    
    public CombinedLiveData(LiveData<String> liveData1, LiveData<Integer> liveData2) {
        addSource(liveData1, value -> {
            source1 = value;
            updateValue();
        });
        
        addSource(liveData2, value -> {
            source2 = value;
            updateValue();
        });
    }
    
    private void updateValue() {
        setValue(new CombinedData(source1, source2));
    }
}

3. DataBinding 原理

<!-- layout.xml -->
<layout>
    <data>
        <variable name="viewModel" type="com.example.UserViewModel" />
    </data>
    
    <LinearLayout>
        <TextView
            android:text="@{viewModel.userName}"
            android:visibility="@{viewModel.loading ? View.GONE : View.VISIBLE}" />
            
        <ProgressBar
            android:visibility="@{viewModel.loading ? View.VISIBLE : View.GONE}" />
    </LinearLayout>
</layout>
// 生成的 Binding 类
public class ActivityUserBinding extends ViewDataBinding {
    // 绑定逻辑
    @Override
    protected void executeBindings() {
        UserViewModel viewModel = mViewModel;
        
        if (viewModel != null) {
            // 更新 UI
            TextViewBindingAdapter.setText(this.nameText, viewModel.getUserName());
        }
    }
}

4. Room 数据库架构

// Entity
@Entity(tableName = "users")
public class User {
    @PrimaryKey
    public int id;
    
    @ColumnInfo(name = "user_name")
    public String name;
    
    public int age;
}

// Dao
@Dao
public interface UserDao {
    @Query("SELECT * FROM users")
    LiveData<List<User>> getAllUsers();
    
    @Insert
    void insertUser(User user);
    
    @Update
    void updateUser(User user);
    
    @Delete
    void deleteUser(User user);
    
    @Query("SELECT * FROM users WHERE id = :userId")
    LiveData<User> getUserById(int userId);
}

// Database
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
    
    // 单例模式
    private static volatile AppDatabase INSTANCE;
    
    public static AppDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(
                        context.getApplicationContext(),
                        AppDatabase.class,
                        "app_database"
                    ).build();
                }
            }
        }
        return INSTANCE;
    }
}

三、组件化架构

1. 组件化架构设计

// 基础模块 - base module
public class BaseApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化基础库
    }
}

// 业务模块 - feature modules
// user module, order module, product module...

// 路由框架 - ARouter 原理
@Route(path = "/user/detail")
public class UserDetailActivity extends Activity {
    @Autowired
    public String userId;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ARouter.getInstance().inject(this); // 依赖注入
    }
}

// 使用路由跳转
ARouter.getInstance()
    .build("/user/detail")
    .withString("userId", "123")
    .navigation();

2. 模块间通信

// 服务发现机制
public interface IUserService extends IProvider {
    UserInfo getUserInfo(String userId);
    void login(String username, String password);
}

// 实现服务
@Route(path = "/service/user")
public class UserServiceImpl implements IUserService {
    @Override
    public UserInfo getUserInfo(String userId) {
        return UserRepository.getUserById(userId);
    }
    
    @Override
    public void init(Context context) {}
}

// 使用服务
IUserService userService = ARouter.getInstance()
    .navigation(IUserService.class);
UserInfo user = userService.getUserInfo("123");

3. 组件化配置

// base module build.gradle
android {
    defaultConfig {
        // 基础配置
    }
}

// feature module build.gradle
android {
    // 动态配置
    if (isModule.toBoolean()) {
        // 作为独立应用运行
        apply plugin: 'com.android.application'
    } else {
        // 作为库模块
        apply plugin: 'com.android.library'
    }
}

四、响应式编程 (RxJava)

1. RxJava 核心概念

public class RxJavaArchitecture {
    
    // 创建 Observable
    Observable<String> observable = Observable.create(emitter -> {
        try {
            emitter.onNext("Data 1");
            emitter.onNext("Data 2");
            emitter.onComplete();
        } catch (Exception e) {
            emitter.onError(e);
        }
    });
    
    // 操作符链式调用
    public void loadUserData() {
        Observable.fromIterable(getUserIds())
            .subscribeOn(Schedulers.io())
            .filter(userId -> userId != null)
            .map(userId -> userRepository.getUser(userId))
            .flatMap(user -> Observable.fromIterable(user.getFriends()))
            .take(10)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(
                friend -> updateUI(friend),
                error -> showError(error),
                () -> hideLoading()
            );
    }
    
    // 背压处理
    public void handleBackpressure() {
        Flowable.range(1, 1000000)
            .onBackpressureBuffer(1000) // 缓冲策略
            .observeOn(Schedulers.computation())
            .subscribe(
                item -> processItem(item),
                error -> handleError(error)
            );
    }
}

// RxJava + Retrofit
public interface ApiService {
    @GET("users/{id}")
    Observable<User> getUser(@Path("id") String userId);
    
    @GET("users/{id}/friends")
    Observable<List<User>> getFriends(@Path("id") String userId);
}

public class UserRepository {
    public Observable<User> getUserWithFriends(String userId) {
        return apiService.getUser(userId)
            .flatMap(user -> 
                apiService.getFriends(userId)
                    .map(friends -> {
                        user.setFriends(friends);
                        return user;
                    })
            );
    }
}

2. 生命周期管理

public class RxLifecycle {
    
    // 使用 CompositeDisposable
    public class UserPresenter {
        private CompositeDisposable disposables = new CompositeDisposable();
        
        public void loadData() {
            Disposable disposable = apiService.getData()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(data -> {
                    // 处理数据
                });
            
            disposables.add(disposable);
        }
        
        public void onDestroy() {
            disposables.dispose(); // 防止内存泄漏
        }
    }
    
    // 使用 AutoDispose (RxLifecycle)
    public void autoDisposeExample() {
        apiService.getData()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this)))
            .subscribe(data -> {
                // 自动在生命周期结束时取消订阅
            });
    }
}

五、依赖注入 (Dagger/Hilt)

1. Dagger2 原理

// 模块定义
@Module
public class AppModule {
    @Provides
    @Singleton
    public OkHttpClient provideOkHttpClient() {
        return new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .build();
    }
    
    @Provides
    @Singleton
    public Retrofit provideRetrofit(OkHttpClient client) {
        return new Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .client(client)
            .addConverterFactory(GsonConverterFactory.create())
            .build();
    }
}

// 组件定义
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
    void inject(MainActivity activity);
    OkHttpClient getOkHttpClient();
    Retrofit getRetrofit();
}

// 使用
public class MainActivity extends Activity {
    @Inject
    OkHttpClient okHttpClient;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        AppComponent component = DaggerAppComponent.create();
        component.inject(this);
    }
}

2. Hilt 简化依赖注入

// Application 配置
@HiltAndroidApp
public class MyApplication extends Application {
}

// Activity 注入
@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
    @Inject
    UserRepository userRepository;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // userRepository 已自动注入
    }
}

// 模块定义
@Module
@InstallIn(ActivityComponent.class)
public class UserModule {
    @Provides
    public UserRepository provideUserRepository(OkHttpClient client) {
        return new UserRepository(client);
    }
}

六、Clean Architecture

1. 分层架构设计

// Domain Layer (业务逻辑层)
public class GetUserUseCase {
    private final UserRepository userRepository;
    
    public GetUserUseCase(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    public LiveData<User> execute(String userId) {
        return userRepository.getUser(userId);
    }
}

// Data Layer (数据层)
public class UserRepositoryImpl implements UserRepository {
    private final UserLocalDataSource localDataSource;
    private final UserRemoteDataSource remoteDataSource;
    
    public UserRepositoryImpl(UserLocalDataSource local, UserRemoteDataSource remote) {
        this.localDataSource = local;
        this.remoteDataSource = remote;
    }
    
    @Override
    public LiveData<User> getUser(String userId) {
        // 业务逻辑:先查缓存,再查网络
        LiveData<User> cachedUser = localDataSource.getUser(userId);
        if (cachedUser.getValue() != null) {
            return cachedUser;
        }
        return remoteDataSource.getUser(userId);
    }
}

// Presentation Layer (表现层)
public class UserViewModel extends ViewModel {
    private final GetUserUseCase getUserUseCase;
    private final MutableLiveData<User> user = new MutableLiveData<>();
    
    public UserViewModel(GetUserUseCase getUserUseCase) {
        this.getUserUseCase = getUserUseCase;
    }
    
    public void loadUser(String userId) {
        getUserUseCase.execute(userId).observeForever(user::setValue);
    }
}

常见面试问题

1. MVC、MVP、MVVM 的区别和优缺点?

  • MVC: View 和 Controller 耦合,适合简单页面
  • MVP: 通过接口解耦,便于测试,但接口过多
  • MVVM: 数据驱动,代码简洁,学习成本较高

2. LiveData 和 RxJava 的区别?

  • LiveData: 生命周期感知,简单易用,功能有限
  • RxJava: 功能强大,操作符丰富,需要手动管理生命周期

3. ViewModel 如何保证数据在配置变化时不丢失?

通过 ViewModelStore 在 Activity 重建时保持 ViewModel 实例

4. 组件化如何解决模块间依赖和通信?

  • 使用路由框架(ARouter)进行页面跳转
  • 使用服务发现进行模块间通信
  • 基础模块提供公共能力

5. Dagger 和 Hilt 的区别?

  • Dagger: 功能强大,配置复杂
  • Hilt: 基于 Dagger,简化配置,与 Android 组件深度集成

这些架构设计八股文涵盖了现代 Android 开发的核心架构模式和最佳实践,理解这些概念对于构建可维护、可测试的应用程序至关重要。

<think>好的,我现在需要处理用户关于安卓开发面试常见问题、知识点和八股文的查询。首先,我得回忆一下用户提供的系统级指令,确保回答符合格式要求,比如行内数学表达式用$...$,独立公式用$$...$$,并且正确使用引用。不过用户的问题是关于安卓开发的,可能不太涉及数学公式,但还是要留意有没有可能需要的地方。 接下来,用户的需求是查找安卓开发面试的相关内容。根据用户提供的引用,特别是引用[1]提到要针对性准备,刷题要结合岗位需求和项目,引用[4]提到了大厂面经、学习笔记等资料。所以回答中需要覆盖常见的知识点,并结合这些引用给出建议。 首先,我需要列出安卓面试的常见问题。通常包括Java/Kotlin基础、Android四大组件、性能优化、多线程、网络、设计模式、架构组件等。例如,Activity生命周期、Handler机制、内存泄漏、ANR问题、MVVM架构等。 然后,要确保结构清晰,分点回答。用户希望知识点和八股文,可能需要分大类,每个大类下列出关键点,并适当解释。例如: 1. Java/Kotlin基础 - 多线程:线程池、同步机制 - 集合框架:HashMap原理 - JVM内存模型:引用类型差异 2. Android核心组件 - Activity生命周期 - Service启动方式 - BroadcastReceiver类型 - ContentProvider使用 3. 性能优化 - 内存泄漏检测 - ANR分析与解决 - 布局优化 4. 网络与安全 - HTTPS握手过程 - 数据加密方式 5. 架构与设计模式 - MVP/MVVM区别 - 常用设计模式如单例、观察者 在回答时,需要结合引用内容,比如引用[1]强调针对性准备和刷题,引用[3]提到学习要有顺序,先基础再深入。因此,在建议部分可以提到按照基础知识到高级优化的顺序学习,结合项目经验来加深理解。 另外,用户可能还希望知道如何准备,比如刷题、看源码、实战项目等,引用[4]提到有大厂面经和实战项目资料,可以建议用户参考这些资源。 需要检查是否有数学公式需要处理,但安卓开发问题可能较少涉及,如果有提到算法复杂度,比如快速排序的时间复杂度$O(n \log n)$,则需要用行内公式。但可能这部分内容不多,所以重点还是文字描述。 最后,生成相关问题,参考用户之前的例子,可以问关于Handler机制、内存优化方法、MVVM优势等。 确保回答中的引用标识正确,例如在提到针对性准备时加上[^1],在建议学习顺序时引用[^3],提到资料时引用[^4]。 现在整理这些思路,按照用户要求的结构组织内容,确保每个部分清晰,引用自然,并且符合格式要求。</think>### 安卓开发面试常见知识点与八股文整理 #### 1. **Java/Kotlin基础** - **多线程与同步机制**:理解线程池(如`ThreadPoolExecutor`)、锁机制(`synchronized`、`ReentrantLock`)及协程(Kotlin)的使用场景。 - **集合框架**:掌握`HashMap`实现原理(哈希冲突解决、红黑树优化)、`ArrayList`与`LinkedList`区别。 - **JVM内存模型**:了解堆栈结构、四种引用类型(强/软/弱/虚)的区别与应用场景。 #### 2. **Android核心组件** - **Activity生命周期**:需熟记`onCreate()`到`onDestroy()`的调用顺序,并解释`onSaveInstanceState()`的作用。 - **Service与IntentService**:两种启动方式(`startService`与`bindService`)的区别及生命周期。 - **BroadcastReceiver**:动态注册与静态注册的适用场景,有序广播与普通广播的区别。 - **ContentProvider**:跨进程数据共享的实现原理及`UriMatcher`的使用。 #### 3. **性能优化** - **内存泄漏**:常见场景(如Handler持有Activity引用)、检测工具(LeakCanary)及解决方案[^4]。 - **ANR分析**:主线程阻塞原因(如耗时I/O)、Trace文件分析及异步任务优化(`AsyncTask`/`Coroutine`)。 - **布局优化**:减少层级(使用`ConstraintLayout`)、`ViewStub`懒加载、`merge`标签的使用。 #### 4. **网络与安全** - **HTTPS握手过程**:简述TLS四次握手流程及对称/非对称加密的作用。 - **数据加密**:了解AES、RSA算法及Android密钥库(KeyStore)的使用。 #### 5. **架构与设计模式** - **MVVM与MVP对比**:数据绑定的实现差异(如`LiveData`与`Presenter`回调)。 - **设计模式**:单例模式(双重校验锁)、观察者模式(`LiveData`原理)、工厂模式的应用场景。 --- ### 面试准备建议 1. **针对性刷题**:结合岗位需求,优先掌握高频考点如Handler机制、Binder原理等[^1]。 2. **项目经验关联**:在回答优化类问题时,可关联实际项目中的性能调优案例(如减少APK体积)[^2]。 3. **源码阅读**:按顺序学习基础组件源码(如`ActivityThread`、`Looper`),避免跳跃式学习。 4. **实战资源**:参考大厂面经合集与实战项目文档(如“Android性能优化权威指南”)[^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值