JessYanCoding / MVPArms

⚔️ A common architecture for Android applications developing based on MVP, integrates many open source projects, to make your developing quicker and easier (一个整合了大量主流开源项目高度可配置化的 Android MVP 快速集成框架).
Apache License 2.0
10.29k stars 2.39k forks source link

复用的Presenter 注入简单示例 #181

Closed Sum41forever closed 6 years ago

Sum41forever commented 6 years ago

复用有2种方式。

  1. 复用通用的 Module,注入用原始的 Component。
  2. 使用依赖 Component 的方式重用 Component

复用通用的 Module

UserActivity 中注入通用 P

通用的 P 命名为 GirlPresenter, 逻辑就是发起一个请求 UserActivity 中注入通用 P UserComponent 中要多添加一个通用的 Module UserComponent 中要多添加一个 Module

GirlModule 提供通用的 P 需要的对象

GirlModule.class

GirlPresenter

GirlPresenter.class

GirlContract

GirlContract.class

Model 的实现基本和 DEMO 中 UserModel 相同这里就不多写了


GirlPresenter 测试情况

通用的 P 发起请求并且成功回调 通用的 P 发起请求 请求回调 请求回调2

使用依赖 Component 的方式重用 Component

@ActivityScope
@Component(modules = {UserModule.class},dependencies = AppComponent.class)
public interface UserComponent {
    TestComponent plus(TestModule testModule);
}
@TestScope //注意 Scope不同!!!不同 Component 的 Scope 不能相同
@Subcomponent(modules = {TestModule.class}) //要用@Subcomponent
public interface TestComponent {
    void inject(UserActivity activity);
}
@Module
public class TestModule {
    private TestContract.View view;
    public TestModule(TestContract.View view) {
        this.view = view;
    }
    @TestScope  //注意所有相关的 Scope 都要用 @TestScope
    @Provides
    TestContract.View provideTestView() {
        return this.view;
    }

}
    @Inject
    TestPresenter mTestPresenter;
    UserComponent userComponent;

    @Override
    public void setupActivityComponent(AppComponent appComponent) {
        userComponent = DaggerUserComponent
                .builder()
                .appComponent(appComponent)
                .userModule(new UserModule(this))
                .build();

        //通用的 TestComponent 的注入,这里 plus 后返回的对象类型是 TestComponent
        userComponent.plus(new TestModule(this)).
                inject(this);

        mTestPresenter.requestUsers(true);
        mPresenter.requestUsers(false);
    }

Module 和 Component 都用通用的

2 个 P 的回调方法都调用了的,这里就不截图了。 因为用了不同的Component,Scope 不能相同!!!带了 Scope 以后,Module,Model 等都需要新的 Scope,这就不是很方便。个人认为维护和阅读都不是很舒服,你想想一个 Acitivity 里面基类的主 Presenter 的 Scope 是一个,通用的 Presenter 的 Scope 是一个,感觉生命周期可能会出问题,这一点没深入研究,只提出自己的想法,欢迎讨论。

JessYanCoding commented 6 years ago

如果想复用依赖关系比较复杂的 Presenter, 可以选择复用该 PresenterModuleComponent, 因为 ModuleComponent, 描述了该 Presenter 的依赖关系图, 指导了 Dagger 如何去完成注入

Sum41forever commented 6 years ago

在实验第二种复用方式 所查阅的比较不错的文章: Dagger2:Scope, Component间依赖和subComponent Dagger 2. Part II. Custom scopes, Component dependencies, Subcomponents

h2uan3g commented 5 years ago

如果acticity和fragment间的复用会不会有问题, @FragmentScope /@ActivityScope

ruanbaojun1105 commented 5 years ago

@h2uan3g make编译都不会通过,我也不知如何复用带@FragmentScope /@ActivityScope注入的类 ,,,

FanDuiWuXiaoJiaBan commented 5 years ago

对于新版本的模板的做法

这里假设新建UserDetailActivityActivity复用UserPresenter UserDetailActivityComponent的代码如下

@ActivityScope
@Component(modules = {UserDetailActivityModule.class,UserModule.class}, dependencies = AppComponent.class)
public interface UserDetailActivityComponent {
    void inject(UserDetailActivityActivity activity);
    @Component.Builder
    interface Builder {
        @BindsInstance
        UserDetailActivityComponent.Builder view(UserDetailActivityContract.View view);
        //名字随便
        @BindsInstance
        UserDetailActivityComponent.Builder view2(UserContract.View view);

        UserDetailActivityComponent.Builder appComponent(AppComponent appComponent);

        UserDetailActivityComponent build();
    }
}

UserDetailActivityActivity的代码如下

public class UserDetailActivityActivity extends BaseActivity implements
        UserDetailActivityContract.View,UserContract.View {//这里实现UserContract.View的接口
    @Inject
    UserPresenter userPresenter;
    @Inject
    RxPermissions mRxPermissions;
    @Override
    public void setupActivityComponent(@NonNull AppComponent appComponent) {
        DaggerUserDetailActivityComponent //如找不到该类,请编译一下项目
                .builder()
                .appComponent(appComponent)
                .view(this)
                .view2(this)//别忘了注入UserContract.View
                .build()
                .inject(this);
    }

    @Override
    public int initView(@Nullable Bundle savedInstanceState) {
        return R.layout.activity_user_detail; //如果你不需要框架帮你设置 setContentView(id) 需要自行设置,请返回 0
    }

    @Override
    public void initData(@Nullable Bundle savedInstanceState) {
        userPresenter.requestUsers(true);
    }

    @Override
    public void showLoading() {

    }

    @Override
    public void hideLoading() {

    }

    @Override
    public void showMessage(@NonNull String message) {
        checkNotNull(message);
        ArmsUtils.snackbarText(message);
    }

    @Override
    public void launchActivity(@NonNull Intent intent) {
        checkNotNull(intent);
        ArmsUtils.startActivity(intent);
    }

    @Override
    public void killMyself() {
        finish();
    }

    @Override
    public void startLoadMore() {

    }

    @Override
    public void endLoadMore() {

    }

    @Override
    public Activity getActivity() {
        return this;
    }

    @Override
    public RxPermissions getRxPermissions() {
        return mRxPermissions;
    }
}
ruanbaojun1105 commented 5 years ago

@buyaomiege 你编译能通过,但是在inject的时候必须传两个实现view,不然dagger初始化都不过

ruanbaojun1105 commented 5 years ago

@buyaomiege 在activity里

FeatherZhong commented 5 years ago

@buyaomiege 在activity里

能够具体说明一下上面代码怎么修改吗,谢谢。

JessYanCoding commented 5 years ago

关于 @FragmentScope /@ActivityScope 这里统一回复:

重用同一个 Presenter,最简单的方式就是 Fragment 也使用和 Activity 一样的 Component,如果 Fragment 有自己独立的 Component,并且 Component 的 scope 和要重用的 Presenter 的 scope 不同肯定会报错。

有时候不要纠结 ActivityScope,Scope 的作用只是保证 Module 和 Component 同步。

也就是说,如果 Component 的 Scope 和 Module 的 Scope 是一样,第二次调用,就不会生成新的对象,也就是说,你 Fragment 的 Component 用 ActivityScope 也是可以的,但只要保证 Presenter 也是用的 ActivityScope 就可以了,达到的效果和用 FragmentScope 没有任何区别,所以你的 Presenter 本身用的就是 ActivityScope,你在 Fragment 中想重用这个 Presenter,那给 Fragment 的 Component 和 Module 定义 ActivityScope 也是可以的。

自定义的 Scope 本来也就没什么实际意义,就是为了在阅读源码时好区分业务层的逻辑,但是在 Dagger 编辑器中和官方定义的 @Singleton 都是使用相同的处理逻辑,核心在于 Module 和 Component 是否是同一个 scope,至于这个 scope 是什么 scope,不是太重要。

ruanbaojun1105 commented 5 years ago

hah哈哈,我也之前还一直纠结ActivityScope 和FragmentScope ,你这么一讲,我想到多建的P层类好委屈

guan25 commented 4 years ago

1、以上讨论的复用是指,一个Activity/fragment 对应多个Presenter,如果反过来呢一个Presenter对应多个Activity(虽说,这样设计并不合适)? 2、一般我理解,mvp中Presenter复用,应该是可以切换不同的view层acitivity(Presenter多个实例对应不同v层),但貌似试了几种也不行

guan25 commented 4 years ago

后来试了这样直接复用component,来实现复用Presenter ,在component 多加 inject,每次调用就会生成一个新的presenter实例。这样也ok image

ps: 觉得模板类生成的Presenter类的上面添加 单例scope(@FragmentScope)并没有什么用哈 image

marvin-an commented 4 years ago

你好,我也是关于一些代码复用的问题。我的项目是用的kotlin,androidx(如果需要集成很多第三方库不建议切换到Androidx,很多库还没适配)。 项目中有一些公共的接口我想把它们放在一起,不希望在每个使用的地方再写一遍重复的内容。 我尝试参考上面提到的第一种方式复用代码 在CommonModule.kt

@Module
class CommonModule(private val view: CommonContract.View) {

    @ActivityScope
    @Provides
    fun provideCommonView(): CommonContract.View {
        return view
    }

    @ActivityScope
    @Provides
    fun provideCommonModel(model: CommonContract.Model): CommonContract.Model {
        return model
    }
}

在LoginComponent.kt

@ActivityScope
@Component(
    modules = [LoginModule::class, CommonModule::class],
    dependencies = [AppComponent::class]
)
interface LoginComponent {
    fun inject(activity: LoginActivity)
}

重新make project之后 在DaggerLoginComponent.java中是这样的

/**
   * @deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://dagger.dev/unused-modules.
   */
    @Deprecated
    public Builder commonModule(CommonModule commonModule) {// 为什么这里是会这样??没有赋值
      Preconditions.checkNotNull(commonModule);
      return this;
    }

在LoginActivity.kt中

// 如果这两行去掉项目编译通过  生成DaggerLoginComponent.java 但commonModule()方法里面没有赋值 为何
//如果不去这两行项目直接编译不通过 不会生成DaggerLoginComponent.java 报错信息为 LoginComponent.java:8: 错误: [Dagger/DependencyCycle] Found a dependency cycle: 为何?
//@Inject
//lateinit var mCommonPresenter:CommonPresenter 

    override fun setupActivityComponent(appComponent: AppComponent) {
        DaggerLoginComponent //如找不到该类,请编译一下项目
            .builder()
            .appComponent(appComponent)
            .loginModule(LoginModule(this))
            .commonModule(CommonModule(this))
            .build()
            .inject(this)
    }

🙏望回复 谢谢

llzj1991 commented 4 years ago

关于的Presenter复用,经过几天的折腾终于实现了 image image image

实测编译通过,Presenter调用正常