Android Dagger2 框架作用域管理模块深度剖析(五)

Android Dagger2 框架作用域管理模块深度剖析

一、引言

在 Android 开发中,依赖注入(Dependency Injection,简称 DI)是一种重要的设计模式,它能有效降低代码之间的耦合度,提升代码的可测试性和可维护性。Dagger2 作为一款强大的依赖注入框架,凭借其在编译时生成依赖注入代码的特性,避免了运行时反射带来的性能开销。而作用域管理模块是 Dagger2 中极为关键的一部分,它能精准控制依赖对象的生命周期,确保在特定作用域内依赖对象的唯一性和一致性。本文将深入探究 Dagger2 框架的作用域管理模块,从源码层面详细解读其实现原理和工作流程。

二、作用域管理的基本概念

2.1 作用域的定义

作用域是指在特定范围内,依赖对象的生命周期和实例化规则。通过作用域管理,我们可以控制依赖对象的创建和销毁时机,保证在同一作用域内依赖对象的唯一性。例如,在单例模式下,一个依赖对象在整个应用程序的生命周期内只被创建一次;而在 Activity 作用域内,依赖对象的生命周期与 Activity 相同,Activity 销毁时,依赖对象也会被销毁。

2.2 作用域管理的重要性

合理的作用域管理能带来诸多好处:

  • 提高性能:避免不必要的对象创建和销毁,减少内存开销。
  • 保证数据一致性:在同一作用域内,依赖对象的状态保持一致,避免数据冲突。
  • 增强代码可维护性:明确依赖对象的生命周期,使代码结构更加清晰。

2.3 Dagger2 中作用域的实现方式

Dagger2 通过自定义注解来实现作用域管理。开发者可以定义自己的作用域注解,并将其应用于组件(Component)和提供依赖的方法上。在编译时,Dagger2 的注解处理器会根据这些注解生成相应的代码,从而实现对依赖对象生命周期的管理。

三、Dagger2 内置作用域:@Singleton

3.1 @Singleton 注解的作用

@Singleton 是 Dagger2 中最常用的作用域注解,它表示单例模式。当一个依赖对象被 @Singleton 注解标记时,在整个应用程序的生命周期内,该依赖对象只被创建一次,并且在所有需要该依赖对象的地方都使用同一个实例。

3.2 @Singleton 注解的使用示例

以下是一个简单的使用 @Singleton 注解的示例:

java

java 复制代码
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import dagger.Component;

// 定义一个依赖类
class Engine {
    public void start() {
        System.out.println("Engine started");
    }
}

// 使用 @Module 注解标记模块类
@Module
class CarModule {
    // 使用 @Singleton 注解标记提供依赖对象的方法
    @Singleton
    @Provides
    public Engine provideEngine() {
        return new Engine();
    }
}

// 使用 @Singleton 注解标记组件接口
@Singleton
@Component(modules = CarModule.class)
interface CarComponent {
    // 定义注入方法,用于将依赖对象注入到目标对象中
    void inject(Car car);
}

// 定义一个需要注入依赖的类
class Car {
    // 使用 @Inject 注解标记需要注入的字段
    @Inject
    Engine engine;

    public void startCar() {
        engine.start();
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建组件实例
        CarComponent carComponent = DaggerCarComponent.create();
        // 创建目标对象实例
        Car car1 = new Car();
        Car car2 = new Car();
        // 使用组件实例将依赖对象注入到目标对象中
        carComponent.inject(car1);
        carComponent.inject(car2);
        // 验证是否为同一个实例
        System.out.println(car1.engine == car2.engine); // 输出 true
    }
}

3.3 @Singleton 注解的源码分析

在编译时,Dagger2 的注解处理器会根据 @Singleton 注解生成相应的代码。以下是简化后的生成代码示例,用于说明 @Singleton 注解的实现原理:

java

java 复制代码
// 生成的组件实现类
public final class DaggerCarComponent implements CarComponent {
    // 单例容器,用于存储单例对象
    private static final class SingletonHolder {
        private static final DaggerCarComponent INSTANCE = new DaggerCarComponent();
    }

    // 单例对象的引用
    private final Engine engine;

    private DaggerCarComponent() {
        // 创建单例对象
        engine = new CarModule().provideEngine();
    }

    public static CarComponent create() {
        return SingletonHolder.INSTANCE;
    }

    @Override
    public void inject(Car car) {
        // 将单例对象注入到目标对象中
        car.engine = engine;
    }
}

从上述代码可以看出,DaggerCarComponent 类使用了单例模式,engine 对象在组件创建时被创建,并且在整个应用程序的生命周期内只被创建一次。当需要注入 Engine 对象时,直接使用存储在组件中的单例对象。

四、自定义作用域

4.1 自定义作用域注解的定义

除了 @Singleton 注解,开发者还可以自定义作用域注解。自定义作用域注解的定义非常简单,只需要创建一个新的注解,并使用 @Scope 元注解标记即可。以下是一个自定义作用域注解的示例:

java

java 复制代码
import javax.inject.Scope;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

// 定义自定义作用域注解
@Scope
@Retention(RetentionPolicy.RUNTIME)
@interface ActivityScope {
}

4.2 自定义作用域的使用示例

以下是一个使用自定义作用域注解 @ActivityScope 的示例:

java

java 复制代码
import javax.inject.Inject;
import dagger.Module;
import dagger.Provides;
import dagger.Component;

// 自定义作用域注解
@Scope
@interface ActivityScope {
}

// 定义一个依赖类
class Tire {
    public void rotate() {
        System.out.println("Tire rotating");
    }
}

// 使用 @Module 注解标记模块类
@Module
class ActivityModule {
    // 使用 @ActivityScope 注解标记提供依赖对象的方法
    @ActivityScope
    @Provides
    public Tire provideTire() {
        return new Tire();
    }
}

// 使用 @ActivityScope 注解标记组件接口
@ActivityScope
@Component(modules = ActivityModule.class)
interface ActivityComponent {
    // 定义注入方法,用于将依赖对象注入到目标对象中
    void inject(ActivityClass activity);
}

// 定义一个需要注入依赖的类,模拟 Activity
class ActivityClass {
    // 使用 @Inject 注解标记需要注入的字段
    @Inject
    Tire tire;

    public void startActivity() {
        tire.rotate();
    }
}

public class CustomScopeExample {
    public static void main(String[] args) {
        // 创建组件实例
        ActivityComponent activityComponent = DaggerActivityComponent.create();
        // 创建目标对象实例
        ActivityClass activity1 = new ActivityClass();
        ActivityClass activity2 = new ActivityClass();
        // 使用组件实例将依赖对象注入到目标对象中
        activityComponent.inject(activity1);
        activityComponent.inject(activity2);
        // 验证是否为同一个实例
        System.out.println(activity1.tire == activity2.tire); // 输出 true
    }
}

4.3 自定义作用域的源码分析

在编译时,Dagger2 的注解处理器会根据自定义作用域注解生成相应的代码。以下是简化后的生成代码示例,用于说明自定义作用域的实现原理:

java

java 复制代码
// 生成的组件实现类
public final class DaggerActivityComponent implements ActivityComponent {
    // 作用域容器,用于存储作用域内的对象
    private static final class ActivityScopeHolder {
        private static DaggerActivityComponent INSTANCE;
        private final Tire tire;

        private ActivityScopeHolder() {
            // 创建作用域内的对象
            tire = new ActivityModule().provideTire();
        }

        public static DaggerActivityComponent getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new DaggerActivityComponent();
            }
            return INSTANCE;
        }
    }

    private final Tire tire;

    private DaggerActivityComponent() {
        this.tire = ActivityScopeHolder.getInstance().tire;
    }

    public static ActivityComponent create() {
        return ActivityScopeHolder.getInstance();
    }

    @Override
    public void inject(ActivityClass activity) {
        // 将作用域内的对象注入到目标对象中
        activity.tire = tire;
    }
}

从上述代码可以看出,DaggerActivityComponent 类使用了类似单例模式的方式来管理 @ActivityScope 作用域内的对象。在 ActivityScopeHolder 类中,tire 对象在作用域内只被创建一次,并且在整个作用域内都使用同一个实例。

五、作用域与组件的关系

5.1 组件的作用域

组件(Component)是 Dagger2 中依赖注入的入口,它可以被标记为特定的作用域。当组件被标记为某个作用域时,该组件所提供的依赖对象将遵循该作用域的规则。例如,当组件被标记为 @Singleton 作用域时,该组件所提供的依赖对象将是单例的;当组件被标记为自定义作用域时,该组件所提供的依赖对象将在该自定义作用域内保持唯一。

5.2 子组件与父组件的作用域关系

子组件(Subcomponent)是 Dagger2 中用于管理更细粒度依赖关系的一种机制。子组件可以继承父组件的依赖,并且可以定义自己的作用域。子组件的作用域与父组件的作用域有以下关系:

  • 子组件可以有自己的作用域:子组件可以定义自己的作用域注解,并且在该作用域内管理依赖对象的生命周期。

  • 子组件的作用域不能与父组件的作用域冲突:如果子组件的作用域与父组件的作用域冲突,会导致编译错误。

以下是一个子组件与父组件作用域关系的示例:

java

java 复制代码
import javax.inject.Inject;
import dagger.Module;
import dagger.Provides;
import dagger.Component;
import dagger.Subcomponent;

// 父组件的作用域注解
@Scope
@interface AppScope {
}

// 子组件的作用域注解
@Scope
@interface ActivityScope {
}

// 父组件的模块
@Module
class AppModule {
    @AppScope
    @Provides
    public AppDependency provideAppDependency() {
        return new AppDependency();
    }
}

// 父组件
@AppScope
@Component(modules = AppModule.class)
interface AppComponent {
    ActivityComponent.Builder activityComponentBuilder();
}

// 子组件的模块
@Module
class ActivityModule {
    @ActivityScope
    @Provides
    public ActivityDependency provideActivityDependency() {
        return new ActivityDependency();
    }
}

// 子组件
@ActivityScope
@Subcomponent(modules = ActivityModule.class)
interface ActivityComponent {
    void inject(ActivityClass activity);

    @Subcomponent.Builder
    interface Builder {
        ActivityComponent build();
        Builder activityModule(ActivityModule module);
    }
}

// 父组件的依赖类
class AppDependency {
    public void doAppWork() {
        System.out.println("Doing app work");
    }
}

// 子组件的依赖类
class ActivityDependency {
    public void doActivityWork() {
        System.out.println("Doing activity work");
    }
}

// 子组件注入的目标类,模拟 Activity
class ActivityClass {
    @Inject
    AppDependency appDependency;
    @Inject
    ActivityDependency activityDependency;

    public void startActivity() {
        appDependency.doAppWork();
        activityDependency.doActivityWork();
    }
}

public class ScopeComponentRelationshipExample {
    public static void main(String[] args) {
        // 创建父组件实例
        AppComponent appComponent = DaggerAppComponent.create();
        // 创建子组件实例
        ActivityComponent activityComponent = appComponent.activityComponentBuilder()
               .activityModule(new ActivityModule())
               .build();
        // 创建目标对象实例
        ActivityClass activity = new ActivityClass();
        // 使用子组件实例将依赖对象注入到目标对象中
        activityComponent.inject(activity);
        // 调用目标对象的方法
        activity.startActivity();
    }
}

5.3 作用域与组件关系的源码分析

在编译时,Dagger2 的注解处理器会根据组件的作用域注解生成相应的代码。以下是简化后的生成代码示例,用于说明作用域与组件关系的实现原理:

java

java 复制代码
// 生成的父组件实现类
public final class DaggerAppComponent implements AppComponent {
    private static final class AppScopeHolder {
        private static final DaggerAppComponent INSTANCE = new DaggerAppComponent();
    }

    private final AppDependency appDependency;

    private DaggerAppComponent() {
        appDependency = new AppModule().provideAppDependency();
    }

    public static AppComponent create() {
        return AppScopeHolder.INSTANCE;
    }

    @Override
    public ActivityComponent.Builder activityComponentBuilder() {
        return new ActivityComponentBuilder();
    }

    private final class ActivityComponentBuilder implements ActivityComponent.Builder {
        private ActivityModule activityModule;

        @Override
        public ActivityComponent build() {
            if (activityModule == null) {
                throw new IllegalStateException("ActivityModule must be set");
            }
            return new DaggerActivityComponent(DaggerAppComponent.this, activityModule);
        }

        @Override
        public Builder activityModule(ActivityModule module) {
            this.activityModule = module;
            return this;
        }
    }
}

// 生成的子组件实现类
public final class DaggerActivityComponent implements ActivityComponent {
    private final DaggerAppComponent parentComponent;
    private final ActivityModule activityModule;
    private final ActivityDependency activityDependency;

    private DaggerActivityComponent(DaggerAppComponent parentComponent, ActivityModule activityModule) {
        this.parentComponent = parentComponent;
        this.activityModule = activityModule;
        this.activityDependency = activityModule.provideActivityDependency();
    }

    @Override
    public void inject(ActivityClass activity) {
        activity.appDependency = parentComponent.appDependency;
        activity.activityDependency = activityDependency;
    }
}

从上述代码可以看出,父组件 DaggerAppComponent 使用 AppScopeHolder 来管理 @AppScope 作用域内的对象,确保 AppDependency 对象在整个应用程序的生命周期内只被创建一次。子组件 DaggerActivityComponent 持有父组件的引用,并且在 @ActivityScope 作用域内管理 ActivityDependency 对象。

六、作用域管理的性能优化

6.1 减少不必要的作用域

在使用 Dagger2 时,应尽量减少不必要的作用域。过多的作用域会增加代码的复杂度和内存开销。可以通过以下方式减少不必要的作用域:

  • 只在需要的地方使用作用域:对于一些只在局部使用的依赖对象,不需要为其定义作用域。
  • 合并作用域:如果多个依赖对象的生命周期相同,可以将它们放在同一个作用域内管理。

6.2 优化作用域容器的实现

作用域容器是用于存储作用域内对象的容器,优化作用域容器的实现可以提高性能。可以通过以下方式优化作用域容器的实现:

  • 使用高效的数据结构 :选择合适的数据结构来存储作用域内的对象,例如使用 HashMap 可以快速查找和获取对象。
  • 及时清理不再使用的对象:在作用域结束时,及时清理作用域容器中不再使用的对象,避免内存泄漏。

6.3 避免作用域冲突

作用域冲突会导致编译错误或运行时异常,应尽量避免作用域冲突。可以通过以下方式避免作用域冲突:

  • 明确作用域的边界:在设计组件和模块时,明确各个作用域的边界,避免作用域重叠。
  • 使用不同的作用域注解:为不同的作用域定义不同的注解,避免混淆。

七、作用域管理的调试和错误处理

7.1 调试技巧

在使用 Dagger2 进行作用域管理时,可能会遇到一些问题,以下是一些调试技巧:

  • 查看生成的代码:Dagger2 在编译时会生成大量的代码,可以查看这些生成的代码来了解作用域管理的具体实现。
  • 使用日志输出:在关键的地方添加日志输出,查看作用域内对象的创建和销毁过程。
  • 使用调试工具:可以使用 Android Studio 等开发工具的调试功能,逐步调试作用域管理的过程。

7.2 常见错误及解决方法

7.2.1 作用域不匹配错误

当作用域不匹配时,会导致依赖对象的生命周期管理出现问题。例如,将一个 @Singleton 作用域的组件注入到一个 @ActivityScope 作用域的组件中,会导致编译错误。解决方法如下:

  • 检查作用域注解:确保作用域注解使用正确,避免作用域不匹配。
  • 调整组件和模块的作用域:根据实际需求,调整组件和模块的作用域。
7.2.2 作用域冲突错误

作用域冲突是指不同的组件或模块使用了相同的作用域注解,但管理的依赖对象生命周期不一致。解决方法如下:

  • 使用不同的作用域注解:为不同的作用域定义不同的注解,避免作用域冲突。
  • 重构代码:通过重构代码,将不同生命周期的依赖对象放在不同的作用域内管理。
7.2.3 作用域泄漏错误

作用域泄漏是指作用域内的对象在作用域结束后仍然被持有,导致内存泄漏。解决方法如下:

  • 及时清理作用域容器:在作用域结束时,及时清理作用域容器中不再使用的对象。
  • 使用弱引用:对于一些可能会导致内存泄漏的对象,可以使用弱引用进行管理。

八、作用域管理在 Android 开发中的应用

8.1 在 Activity 和 Fragment 中的应用

在 Android 开发中,Activity 和 Fragment 有自己的生命周期。可以使用自定义作用域来管理与 Activity 或 Fragment 相关的依赖对象,确保这些依赖对象的生命周期与 Activity 或 Fragment 一致。

以下是一个在 Activity 中使用自定义作用域的示例:

java

java 复制代码
import android.app.Activity;
import android.os.Bundle;
import javax.inject.Inject;
import dagger.Module;
import dagger.Provides;
import dagger.Component;

// 自定义作用域注解,用于 Activity 作用域
@Scope
@interface ActivityScope {
}

// 定义一个依赖类
class ActivityDependency {
    public void doActivityWork() {
        System.out.println("Doing activity work");
    }
}

// 使用 @Module 注解标记模块类
@Module
class ActivityModule {
    private final Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    // 使用 @ActivityScope 注解标记提供依赖对象的方法
    @ActivityScope
    @Provides
    public ActivityDependency provideActivityDependency() {
        return new ActivityDependency();
    }
}

// 使用 @ActivityScope 注解标记组件接口
@ActivityScope
@Component(modules = ActivityModule.class)
interface ActivityComponent {
    // 定义注入方法,用于将依赖对象注入到目标对象中
    void inject(MainActivity activity);
}

// 主 Activity 类
public class MainActivity extends Activity {
    // 使用 @Inject 注解标记需要注入的字段
    @Inject
    ActivityDependency activityDependency;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 创建组件实例
        ActivityComponent activityComponent = DaggerActivityComponent.builder()
               .activityModule(new ActivityModule(this))
               .build();
        // 使用组件实例将依赖对象注入到目标对象中
        activityComponent.inject(this);
        // 调用依赖对象的方法
        activityDependency.doActivityWork();
    }
}

8.2 在 Application 中的应用

在 Android 开发中,Application 是整个应用程序的入口,其生命周期与应用程序的生命周期相同。可以使用 @Singleton 作用域来管理与 Application 相关的依赖对象,确保这些依赖对象在整个应用程序的生命周期内只被创建一次。

以下是一个在 Application 中使用 @Singleton 作用域的示例:

java

java 复制代码
import android.app.Application;
import javax.inject.Inject;
import dagger.Module;
import dagger.Provides;
import dagger.Component;

// 定义一个依赖类
class AppDependency {
    public void doAppWork() {
        System.out.println("Doing app work");
    }
}

// 使用 @Module 注解标记模块类
@Module
class AppModule {
    private final Application application;

    public AppModule(Application application) {
        this.application = application;
    }

    // 使用 @Singleton 注解标记提供依赖对象的方法
    @Singleton
    @Provides
    public AppDependency provideAppDependency() {
        return new AppDependency();
    }
}

// 使用 @Singleton 注解标记组件接口
@Singleton
@Component(modules = AppModule.class)
interface AppComponent {
    // 定义注入方法,用于将依赖对象注入到目标对象中
    void inject(MainApplication application);
}

// 主 Application 类
public class MainApplication extends Application {
    // 使用 @Inject 注解标记需要注入的字段
    @Inject
    AppDependency appDependency;

    @Override
    public void onCreate() {
        super.onCreate();
        // 创建组件实例
        AppComponent appComponent = DaggerAppComponent.builder()
               .appModule(new AppModule(this))
               .build();
        // 使用组件实例将依赖对象注入到目标对象中
        appComponent.inject(this);
        // 调用依赖对象的方法
        appDependency.doAppWork();
    }
}

8.3 在 Service 中的应用

在 Android 开发中,Service 是一种在后台运行的组件,其生命周期与 Service 的启动和停止相关。可以使用自定义作用域来管理与 Service 相关的依赖对象,确保这些依赖对象的生命周期与 Service 一致。

以下是一个在 Service 中使用自定义作用域的示例:

java

java 复制代码
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import javax.inject.Inject;
import dagger.Module;
import dagger.Provides;
import dagger.Component;

// 自定义作用域注解,用于 Service 作用域
@Scope
@interface ServiceScope {
}

// 定义一个依赖类
class ServiceDependency {
    public void doServiceWork() {
        System.out.println("Doing service work");
    }
}

// 使用 @Module 注解标记模块类
@Module
class ServiceModule {
    private final Service service;

    public ServiceModule(Service service) {
        this.service = service;
    }

    // 使用 @ServiceScope 注解标记提供依赖对象的方法
    @ServiceScope
    @Provides
    public ServiceDependency provideServiceDependency() {
        return new ServiceDependency();
    }
}

// 使用 @ServiceScope 注解标记组件接口
@ServiceScope
@Component(modules = ServiceModule.class)
interface ServiceComponent {
    // 定义注入方法,用于将依赖对象注入到目标对象中
    void inject(MyService service);
}

// 自定义 Service 类
public class MyService extends Service {
    // 使用 @Inject 注解标记需要注入的字段
    @Inject
    ServiceDependency serviceDependency;

    @Override
    public void onCreate() {
        super.onCreate();
        // 创建组件实例
        ServiceComponent serviceComponent = DaggerServiceComponent.builder()
               .serviceModule(new ServiceModule(this))
               .build();
        // 使用组件实例将依赖对象注入到目标对象中
        serviceComponent.inject(this);
        // 调用依赖对象的方法
        serviceDependency.doServiceWork();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

九、作用域管理模块的未来发展趋势

9.1 与 Kotlin 的深度集成

随着 Kotlin 在 Android 开发中的广泛应用,Dagger2 的作用域管理模块可能会与 Kotlin 进行更深度的集成。例如,提供更简洁的 Kotlin 语法支持,利用 Kotlin 的特性来优化作用域管理的实现。

9.2 支持更多的 Android 架构组件

随着 Android 架构组件的不断发展,Dagger2 的作用域管理模块可能会支持更多的 Android 架构组件,如 ViewModel、LiveData 等。通过与这些组件的集成,可以更好地管理依赖对象的生命周期,提高代码的可维护性。

9.3 性能优化和代码生成的改进

未来,Dagger2 的作用域管理模块可能会在性能优化和代码生成方面进行改进。例如,进一步减少生成代码的体积,提高代码的执行效率,同时提供更灵活的配置选项,满足不同开发者的需求。

十、总结

Dagger2 的作用域管理模块是一个强大而灵活的工具,它可以帮助开发者精确控制依赖对象的生命周期,提高代码的性能和可维护性。通过自定义作用域注解和合理使用组件的作用域,开发者可以根据不同的业务需求,实现不同粒度的依赖对象管理。

在实际开发中,需要注意作用域的合理使用,避免作用域冲突和内存泄漏等问题。同时,掌握调试和错误处理技巧,可以帮助开发者快速定位和解决问题。

随着 Android 开发技术的不断发展,Dagger2 的作用域管理模块也将不断完善和发展,为开发者提供更好的开发体验。通过深入理解和掌握 Dagger2 的作用域管理模块,开发者可以写出更加高效、可维护的 Android 应用程序。

以上内容从源码级别详细分析了 Dagger2 框架的作用域管理模块,希望能帮助你更好地理解和使用 Dagger2。由于篇幅目前还未达到 50000 字,如果你需要对某些部分进行

相关推荐
小墙程序员1 小时前
一文了解 Android 中的 UID、GID、PID
android
&有梦想的咸鱼&3 小时前
Android Compose 框架文本选择与编辑模块源码深度剖析(三)
android
二流小码农3 小时前
鸿蒙开发:远场通信服务rcp会话问题
android·ios·harmonyos
stevenzqzq5 小时前
kotlin @JvmStatic的使用
android·开发语言·kotlin
氦客5 小时前
Kotlin知识体系(二) : Kotlin的七个关键特性
android·开发语言·kotlin·安卓·特性·data class·密封类
阿豪元代码6 小时前
Perfetto 快速上手指南1 —— Trace 的抓取
android
&有梦想的咸鱼&6 小时前
Android Fresco 框架扩展模块源码深度剖析(四)
android
fatiaozhang95276 小时前
烽火HG680-KB_海思HI3798MV310_安卓9.0_U盘强刷固件包及注意点说明
android·华为·机顶盒rom·魔百盒刷机·移动魔百盒
YEAH!启动!9 小时前
WPS二次开发系列:WPS SDK事件回调
android·java·前端·pdf·word·wps·ppt
stevenzqzq10 小时前
kotlin 线程池封装
android·开发语言·kotlin