1 前言
距离首次接触 Dagger2 已经有半年的时间了,从最初的一脸懵逼,到慢慢的熟练使用,这个过程真的感谢 MVPArms,这半年在 MVPArms 真的学到很多东西,由此演变出的 MVVMArms 可以说是这半年学习的结晶。其中在构建 MVVMArms 的过程中,采用了最新的 Dagger2.11,更好的支持了 Android 的依赖注入。好了,废话就说这么多,下面来通过一个例子来对 Dagger.Android 有更进一步的认识。
下载源码一起看会更好!下载源码一起看会更好!下载源码一起看会更好!
DaggerAndroid:https://github.com/xiaobailong24/DaggerAndroid
如果你还没接触过 Dagger2,可以看我之前转载的一篇文章 - Dagger2 学习,里面概念讲得很清晰。
目前大多数文章还是讲解简单使用 Dagger2,但是对于多 Module 下,怎么通过 Dagger 管理他们之间的依赖关系,还没有这样的文章,我会把在 MVVMArms 中探索出的一种 Dagger.Android 多 Module 管理方案分享给大家。
2 Gradle 配置
要在 Android 中使用 Dagger2 , 先添加 Gradle 配置,最新的版本可在 GitHub 找到。这里使用了 Android Studio 3.0 Beta6。
//dagger.android
implementation 'com.google.dagger:dagger:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
implementation 'com.google.dagger:dagger-android:2.11'
implementation 'com.google.dagger:dagger-android-support:2.11'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
3 入门篇
先来看一下几个关键的概念:
- @Component: Component 是一个注入器,就像注射器一样,Component 会把目标类依赖的实例注入到目标类中,来初始化目标类中的依赖。
- @Subcomponent: 从名字可以看出,这也是一个注入器,只不过被 @Subcomponent 注解的是被 @Component 注解的下一层级,这就像 Subcomponent 继承于 Component 一样。
- @Module: 为一些三方类库提供注入的对象,常配合 @Provides 使用。
- AndroidInjectionModule: 主要提供 Dagger.Android 组件包,它应该被包含在注入 Application 的 Component 注入器的 modules 中。
- AndroidInjection: Dagger.Android 注入的核心类,主要封装了一些静态方法用来为四大组件和 Fragment 进行注入。
Dagger.Android 可以有两种注入方式,下面分别通过 Activity 和 Fragment 来看一下。
3.1 Activity 依赖注入(第一种注入方式)
3.1.1 AndroidInjectionModule
在整个 Application 的 Component 中添加 AndroidInjectionModule。
AndroidInjectionModule 主要提供 Dagger.Android 组件包,它应该被包含在注入 Application 的 Component 注入器的 modules 中。
此例中为 AppComponent。
AppComponent
@Singleton
@Component(modules = AndroidInjectionModule.class)
public interface AppComponent {
void inject(MainApp mainApp);
}
这样就可以确保使用最新的 Dagger.Android。
3.1.2 @Subcomponent
为 Activity 编写 Subcomponent,该接口需要继承 public interface AndroidInjector<T>;该接口内有一个被 @Subcomponent.Builder 注解的继承于 AndroidInjector.Builder<T> 的抽象类,其中泛型 T 为要注入的目标 Activity。
MainActivitySubcomponent
@ActivityScope
@Subcomponent(modules = KobeModule.class)//DataModule
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
}
}
一些需要注入的数据类型可以包含在 @Subcomponent(modules = {}) 中。
KobeModule
@Module
public class KobeModule {
@ActivityScope
@Provides
public Person provideKobe() {
return new Person("Kobe", 39);
}
}
Person
public class Person {
private String name;
private int age;
@Inject
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
3.1.3 @Module
接下来,编写 Activity 的 Module,绑定上一步新建的 Subcomponent,这样 MainActivitySubcomponent 就会为 MainActivity 注入 MainActivityModule 中提供的内容。
然后将其添加到全局 Component 中,即上文中的 AppComponent,这样 AppComponent 就和 MainActivitySubcomponent 建立了联系,这是一种继承关系,即 Subcomponent 为 Component 的下一级。
MainActivityModule
@Module(subcomponents = MainActivitySubcomponent.class)
public abstract class MainActivityModule {
/**
* 第一种注入方式。需要 Subcomponent
*/
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
}
AppComponent 改写为:
@Singleton
@Component(modules = {AndroidInjectionModule.class,
MainActivityModule.class)
public interface AppComponent {
void inject(MainApp mainApp);
}
3.1.4 HasActivityInjector
让 MainApp 实现 HasActivityInjector 接口,并注入 DispatchingAndroidInjector<Activity>。
为 Activity 提供 AndroidInjector,这是 AndroidInjection.inject(Activity activity) 所需要的,具体见 3.1.6 的源码解析。
MainApp
public class MainApp extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> mActivityInjector;
private AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
mAppComponent = DaggerAppComponent.builder()
.daggerComponent(getDaggerComponent())
.build();
mAppComponent.inject(this);
}
public AppComponent getAppComponent() {
return mAppComponent;
}
@Override
public AndroidInjector<Activity> activityInjector() {
return mActivityInjector;
}
}
3.1.5 AndroidInjection
最后,在目标 Activity 的 onCreate() 方法中进行注入,需要注意的是应该在 super.onCreate() 调用前注入。
MainActivity
public class MainActivity extends AppCompatActivity {
@Inject
Person mKobe;//依赖注入
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
}
最后不要忘记在 AndroidManifest.xml 中指定 MainApp 。
3.1.6 源码解析
AndroidInjection.inject() 从 MainApp 获得一个 DispatchingAndroidInjector<Activity> 对象,并将 MainActivity 传入到 inject(Activity activity) 方法。 DispatchingAndroidInjector 为 MainActivity 类查找 AndroidInjector.Factory 的实现类,即 MainActivitySubcomponent.Builder;接着会创建一个 AndroidInjector,即 MainActivitySubcomponent,并将 MainActivity 传入到 inject(Activity activity) 方法。
AndroidInjection#inject(Activity activity) 源码如下:
public static void inject(Activity activity) {
checkNotNull(activity, "activity");
Application application = activity.getApplication();
//判断 Application 是否实现了 HasActivityInjector 接口
if (!(application instanceof HasActivityInjector)) {
throw new RuntimeException(
String.format(
"%s does not implement %s",
application.getClass().getCanonicalName(),
HasActivityInjector.class.getCanonicalName()));
}
//从 Application 中获取 AndroidInjector 对象,即 DispatchingAndroidInjector<Activity> mActivityInjector
AndroidInjector<Activity> activityInjector =
((HasActivityInjector) application).activityInjector();
checkNotNull(
activityInjector,
"%s.activityInjector() returned null",
application.getClass().getCanonicalName());
//最后注入到 MainActivity 中,此处是在 Dagger 编译生成的 DaggerAppComponent.MainActivitySubcomponentImpl 类中实现的。
activityInjector.inject(activity);
}
3.2 Fragment 依赖注入(第二种注入方式)
Fragment 使用的是v4兼容包中的 android.support.v4.app.Fragment。
3.2.1 @Subcomponent
由于在 Activity 依赖注入的第一步已经添加 AndroidInjectionModule,所以这里可以直接使用。这种方式其实是第一种方式的简化,如果 MainFragmentSubcomponent 和 MainFragmentSubcomponent.Builder 没有其他的方法或超类型,如下,
MainFragmentSubcomponent
@FragmentScope
@Subcomponent
public interface MainFragmentSubcomponent extends AndroidInjector<MainFragment> {
@Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainFragment> {
}
}
这时可以省略 MainFragmentSubcomponent,也就是说,可以直接不用定义 MainFragmentSubcomponent。
3.2.2 @Module
当 Subcomponent 和 它的 Builder 没有其它方法或超类型时,可以不再需要 Subcomponent。
其实Subcomponent 的作用就是生成 AndroidInjector<T>,而 @ContributesAndroidInjector 注解也可以为我们做这个事情。
MainFragmentModule
@Module
public abstract class MainFragmentModule {
/**
* 第二种注入方式。当 Subcomponent 和 它的 Builder 没有其它方法或超类型时,可以不再需要 Subcomponent
*/
@FragmentScope
@ContributesAndroidInjector(modules = JordonModule.class)//DataModule
abstract MainFragment contributeMainFragment();
}
一些需要注入的数据类型可以包含在 @ContributesAndroidInjector(modules = {}) 中。
将MainFragmentModule 装载到 AppComponent 中:
@Singleton
@Component(modules = {AndroidInjectionModule.class,
MainActivityModule.class,
MainFragmentModule.class})
public interface AppComponent {
void inject(MainApp mainApp);
}
3.2.3 HasSupportFragmentInjector
让要依赖注入的目标 Fragment(即 MainFragment) 的宿主 Activity(即 MainActivity) 实现 HasSupportFragmentInjector 接口。
为 Fragment 提供 AndroidInjector,这是 AndroidInjection.inject(Fragment fragment) 所需要的,具体见 3.2.5 的源码解析。
MainActivity
public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {
@Inject
DispatchingAndroidInjector<Fragment> mFragmentInjector;
//...
public void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return this.mFragmentInjector;
}
}
如果使用 android.app.Fragment,Activity 应该实现 HasFragmentInjector 接口,并注入 DispatchingAndroidInjector<Fragment>。
这一步也可以在 Application 实现 HasSupportFragmentInjector 接口,类似 3.1.4 所述。
3.2.4 AndroidInjection
最后,在目标 Fragment 的 onAttach() 方法中进行注入。
MainFragment
public class MainFragment extends Fragment {
@Inject
Person mJordon;//依赖注入
@Override
public void onAttach(Context context) {
super.onAttach(context);
AndroidSupportInjection.inject(this);
}
//...
}
3.2.5 源码解析
Fragment 注入的原理与 Activity 的类似,这里再强调一遍,其实第二种方式是第一种方式的简化,使用 @ContributesAndroidInjector 注解来自动生成 Subcomponent。
AndroidSupportInjection#inject(Fragment fragment) 源码如下:
public static void inject(Fragment fragment) {
checkNotNull(fragment, "fragment");
//获取 HasSupportFragmentInjector
HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector(fragment);
Log.d(
TAG,
String.format(
"An injector for %s was found in %s",
fragment.getClass().getCanonicalName(),
hasSupportFragmentInjector.getClass().getCanonicalName()));
//从 Activity 中获取 HasSupportFragmentInjector 对象,即 DispatchingAndroidInjector<Fragment> mFragmentInjector
AndroidInjector<Fragment> fragmentInjector =
hasSupportFragmentInjector.supportFragmentInjector();
checkNotNull(
fragmentInjector,
"%s.supportFragmentInjector() returned null",
hasSupportFragmentInjector.getClass().getCanonicalName());
//最后注入到 MainFragment 中,此处是在 Dagger 编译生成的 DaggerAppComponent.MainFragmentSubcomponentImpl 类中实现的。
fragmentInjector.inject(fragment);
}
AndroidSupportInjection#findHasFragmentInjector(Fragment fragment)
private static HasSupportFragmentInjector findHasFragmentInjector(Fragment fragment) {
Fragment parentFragment = fragment;
while ((parentFragment = parentFragment.getParentFragment()) != null) {
if (parentFragment instanceof HasSupportFragmentInjector) {
return (HasSupportFragmentInjector) parentFragment;
}
}
Activity activity = fragment.getActivity();
if (activity instanceof HasSupportFragmentInjector) {
return (HasSupportFragmentInjector) activity;
}
if (activity.getApplication() instanceof HasSupportFragmentInjector) {
return (HasSupportFragmentInjector) activity.getApplication();
}
throw new IllegalArgumentException(
String.format("No injector was found for %s", fragment.getClass().getCanonicalName()));
}
由源码可知,如果需要在 Fragment 中进行依赖注入,可以有两种实现方式:一种是宿主 Activity 实现 HasSupportFragmentInjector,另一种是 Application 实现 HasSupportFragmentInjector。
3.3 其他组件
Service、BroadcastReceiver 和 ContentProvider 的注入方式与此类似。
为了方便,Dagger.Android 为我们提供了一些封装好的组件类,下面引用官方文档的一段话,如果有需要,可以直接使用这些组件类。
Because DispatchingAndroidInjector looks up the appropriate AndroidInjector.Factory by the class at runtime, a base class can implement HasActivityInjector/HasFragmentInjector/etc as well as call AndroidInjection.inject(). All each subclass needs to do is bind a corresponding @Subcomponent. Dagger provides a few base types that do this, such as DaggerActivity and DaggerFragment, if you don’t have a complicated class hierarchy. Dagger also provides a DaggerApplication for the same purpose — all you need to do is to extend it and override the applicationInjector() method to return the component that should inject the Application.
The following types are also included:
Note: DaggerBroadcastReceiver should only be used when the BroadcastReceiver is registered in the AndroidManifest.xml. When the BroadcastReceiver is created in your own code, prefer constructor injection instead.
4 多 Module 实战
上面只是简单的介绍了Dagger.Android 的使用,下面重点来了,还是将通过一个例子,详解怎么利用 Dagger 构建多 Module 依赖关系,从而实现组件化。
这里,将上述对 Activity 和 Fragment 的依赖注入分离到一个 Library Module 中,利用 Application.ActivityLifecycleCallbacks 和 FragmentManager.FragmentLifecycleCallbacks 监听,构建全局的依赖注入。
这里提前提一下本方案下,Dagger 的依赖关系:
DaggerComponent -> AppComponent -> MainActivitySubcomponent/MainFragmentSubcomponent
其中:DaggerComponent 为 library 的注入器,它的作用域是 @Singleton;
AppComponent 为主 Module 的全局注入器,它的作用域是 @AppScope,并且 AppComponent 是依赖于 DaggerComponent,也就是说,DaggerComponent 顶级注入器,AppComponent 是主 Module 的注入器;
MainActivitySubcomponent/MainFragmentSubcomponent 是 Activity/Fragment 的注入器,作用域为 @ActivityScope/@FragmentScope,这里是 @Subcomponent,通过继承的方式实现层级依赖,为 AppComponent 的下一级。
AppScope
@Scope
@Retention(RUNTIME)
public @interface AppScope {
}
4.1 Library Module
4.1.1 DaggerFragmentLifecycleCallbacks - 全局 Fragment 依赖注入
这里使用 FragmentLifecycleCallbacks 全局监听 Fragment 的生命周期,对 Dagger.Android 进行统一注入管理。
DaggerFragmentLifecycleCallbacks
public class DaggerFragmentLifecycleCallbacks extends FragmentManager.FragmentLifecycleCallbacks {
@Inject
public DaggerFragmentLifecycleCallbacks() {
}
@Override
public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {
super.onFragmentAttached(fm, f, context);
Timber.i(f.toString() + " ---> onFragmentAttached");
AndroidSupportInjection.inject(f);//Dagger.Android Inject for Fragment
}
@Override
public void onFragmentActivityCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
super.onFragmentActivityCreated(fm, f, savedInstanceState);
Timber.i(f.toString() + " ---> onFragmentActivityCreated");
}
@Override
public void onFragmentDetached(FragmentManager fm, Fragment f) {
super.onFragmentDetached(fm, f);
Timber.i(f.toString() + " ---> onFragmentDetached");
}
}
可以看到,DaggerFragmentLifecycleCallbacks 也是通过 Dagger 进行管理的,在 onFragmentAttached() 方法中进行 Fragment 的依赖注入;并且使用 Timber 打印了几个关键生命周期回调Log。
4.1.2 DaggerActivityLifecycleCallbacks - 全局 Activity 依赖注入
这里使用 ActivityLifecycleCallbacks 全局监听 Activity 的生命周期,对 Dagger.Android 进行统一注入管理。
DaggerActivityLifecycleCallbacks
public class DaggerActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
@Inject
DaggerFragmentLifecycleCallbacks mFragmentLifecycleCallbacks;
@Inject
public DaggerActivityLifecycleCallbacks() {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Timber.w(activity + " ---> onActivityCreated");
AndroidInjection.inject(activity);//Dagger.Android Inject for Activity
if ((activity instanceof HasSupportFragmentInjector || activity.getApplication() instanceof HasSupportFragmentInjector)
&& activity instanceof FragmentActivity) {
((FragmentActivity) activity).getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(mFragmentLifecycleCallbacks, true);
}
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
Timber.w(activity + " ---> onActivityDestroyed");
}
}
4.1.3 DaggerComponent - 顶级注入器
DaggerComponent
@Singleton
@Component(modules = {AndroidInjectionModule.class,
DaggerModule.class})
public interface DaggerComponent {
Application application();
void inject(DaggerDelegate daggerDelegate);
}
其中 DaggerModule 主要提供一些全局依赖,这里只有一个 provideApplication() 方法,可以自行添加需要的东西。
DaggerModule
@Module
public class DaggerModule {
private final Application mApplication;
public DaggerModule(Application application) {
mApplication = application;
}
@Singleton
@Provides
public Application provideApplication() {
return this.mApplication;
}
}
4.1.4 DaggerDelegate - 开始注入
DaggerDelegate
public class DaggerDelegate {
@Inject
DaggerActivityLifecycleCallbacks mActivityLifecycleCallbacks;
private DaggerComponent mComponent;
private final Application mApplication;
public DaggerDelegate(Application application) {
mApplication = application;
}
public void onCreate() {
Timber.plant(new Timber.DebugTree());
mComponent = DaggerDaggerComponent.builder()
.daggerModule(new DaggerModule(mApplication))
.build();
mComponent.inject(this);
mApplication.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
}
public DaggerComponent getComponent() {
return mComponent;
}
}
这里的 DaggerDelegate 是一个代理类,为了克服 Application 继承的问题,通过封装一个代理类来对 library 的 Dagger 注入进行管理,然后在需要的 Module 里使用。
至此,Library Module 的依赖注入结构搭建完成。
4.2 App Module
由上一大节可知,Library Module 中是没有 Application 的,它是一个 library,如果想使用它,需要在 主 Module 中进行依赖。再说一遍,DaggerComponent 是顶级注入器,AppComponent 主 Module 的全局注入器,它仅限定于 @AppScope。
首先在 app/build.gradle 中添加上节中的 Library Module 依赖:
dependencies {
//library
implementation project(':library')
//...
}
4.2.1 AppComponent - 主 Module 的全局注入器
AppComponent
@AppScope
@Component(dependencies = DaggerComponent.class,
modules = {AppModule.class,
MainActivityModule.class,
MainFragmentModule.class})
public interface AppComponent {
void inject(MainApp mainApp);
}
可以看到,dependencies = DaggerComponent.class,这就是上文所说的:AppComponent 是依赖于 DaggerComponent,也就是说,DaggerComponent 顶级注入器,AppComponent 是主 Module 的注入器。
其中,AppModule 主要提供主 Module 的一些全局依赖,可自行扩展。
AppModule
@Module
public class AppModule {
private Application mApplication;
public AppModule(Application application) {
mApplication = application;
}
}
4.2.2 MainApp - 真正注入的地方
MainApp
public class MainApp extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> mActivityInjector;
private DaggerDelegate mDaggerDelegate;
private AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
//Library 的依赖注入(顶级)
mDaggerDelegate = new DaggerDelegate(this);
mDaggerDelegate.onCreate();
//注入主 Module 中(该 Module 全局)
mAppComponent = DaggerAppComponent.builder()
.daggerComponent(getDaggerComponent())
.build();
mAppComponent.inject(this);
}
public DaggerComponent getDaggerComponent() {
return mDaggerDelegate.getComponent();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
@Override
public AndroidInjector<Activity> activityInjector() {
return mActivityInjector;
}
}
MainApp 是真正进行依赖注入的地方,首先使用上一节 Library Module 中的 DaggerDelegate 进行顶级依赖注入,然后进行 主Module 的依赖注入。需要注意的是,MainApp 必须实现 HasActivityInjector 接口,才能进行 Dagger.Android 注入。
最后不要忘记在 AndroidManifest.xml 中指定 MainApp 。
4.2.3 MainActivityModule/MainFragmentModule
由第一节的例子可知:当 Subcomponent 和 它的 Builder 没有其它方法或超类型时,可以不再需要手写 Subcomponent,而是通过 @ContributesAndroidInjector 注解来自动生成。
所以,这里的 MainActivitySubcomponent/MainFragmentSubcomponent 可以省略;这样,MainActivityModule/MainFragmentModule 如下:
MainActivityModule
@Module
public abstract class MainActivityModule {
@ActivityScope
@ContributesAndroidInjector(modules = KobeModule.class)//DataModule
abstract MainActivity contributeMainActivity();
}
MainFragmentModule
@Module
public abstract class MainFragmentModule {
@FragmentScope
@ContributesAndroidInjector(modules = JordonModule.class)//DataModule
abstract MainFragment contributeMainFragment();
}
KobeModule 和 JordonModule 依旧是第一大节中的,就不再重复贴代码了。至此就可以在 Activity/Fragment 中进行依赖注入了。Dagger.Adnroid 注入是在 Library Module 中的 DaggerActivityLifecycleCallbacks/DaggerFragmentLifecycleCallbacks 完成的,这是一个全局监听器,使用 DaggerDelegate 在 MainApp 中进行注册的。
4.2.4 总结
再来强调一下,本方案的 Dagger 层级依赖关系:
DaggerComponent -> AppComponent -> MainActivitySubcomponent/MainFragmentSubcomponent
其中:DaggerComponent 为 library 的注入器,它的作用域是 @Singleton;
AppComponent 为主 Module 的全局注入器,它的作用域是 @AppScope,并且 AppComponent 是依赖于 DaggerComponent,也就是说,DaggerComponent 顶级注入器,AppComponent 是主 Module 的注入器;
MainActivitySubcomponent/MainFragmentSubcomponent 是 Activity/Fragment 的注入器,作用域为 @ActivityScope/@FragmentScope,这里是 @Subcomponent,通过继承的方式实现层级依赖,为 AppComponent 的下一级。
5 总结
上面两个例子,第一个介绍了 Dagger.Android 的简单使用,这是 Dagger2.11 的新姿势;这样不需要每个 Activity/Fragment 中再重复手写一串的依赖注入代码;而是通过实现 HasActivityInjector/HasSupportFragmentInjector 接口,通过生命周期的监听,使用 AndroidInjection.inject() 自动注入。
以上是 MVVMArms 框架的基本的 Dagger 层级依赖关系,更详细的使用可以查看 MVVMArms。
如果各位有任何疑问,欢迎交流。如果文中有不正之处,也请不吝赐教。
知识分享才会有快乐,后面我会继续分解 MVVMArms 的关键模块,如果各位对 MVVMArms 有任何问题或建议,欢迎一起交流。
6 Github
以上方案的源码都可以在 Github 查看。
7 相关资源
联系
我是 xiaobailong24,您可以通过以下平台找到我:
网友评论