我可以在Fragment中注册MVP Presenter吗?

12

我一直在遵循谷歌提供的MVP设计模式来重构我的应用程序,参考了位于GitHub上的代码示例。我有一个MainActivity和多个Fragment,为每个Fragment创建一个Activity会显得有些混乱,因此我一直在考虑在Fragment中注册Presenter。我看到的是每个Fragment都注册了自己的Presenter,但我不确定这样做有多少问题... :)

所以这是我的Presenter:

public class FirstPresenter implements FirstContract.Presenter {
    private final FirstContract.View mView;

    public FirstPresenter(FirstContract.View view) {
        mView = view;
    }

    @Override
    public void start() {
        Log.e(TAG, "Start");
    }
}

以下是我的 Fragment:

public class FirstFragment extends Fragment implements FirstContract.View {
    private FirstContract.Presenter mPresenter;

@Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container
            , Bundle savedInstanceState) {
...
// I register firstFragment's presenter here.
mPresenter = new FirstPresenter(this);
...

所以我的问题是,这样做对吗?我可以将Presenter注册到Fragment而不是Activity中吗?如果这不是正确的方式,是否有一些好的示例来处理在一个Activity和多个Fragment中使用MVP模式的情况?

谢谢大家, 敬礼!


1
在Android Blueprint存储库中的示例代码中,您将看到PresentersActivity内部创建,但注册也将在Fragment内完成。例如,请参阅TaskDetailActivity:https://github.com/googlesamples/android-architecture/blob/todo-mvp/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/taskdetail/TaskDetailActivity.java - 我们的应用程序中也使用MVP,并且以相同的方式进行注册。我认为这样做没有任何问题。 - Darwind
2个回答

19

正如您在Google的示例中所看到的(https://github.com/googlesamples/android-architecture),Activities创建Presenters。此外,Views附加到Activity上,Presenters将视图(Fragments)作为参数获取。

Fragment事务提交或Fragment(视图)状态恢复后,Presenters被创建并将Fragments(视图)作为参数获取,然后调用。

view.setPresenter(T presenter); 

视图和Presenters的注册方法。

我认为在Fragment中创建Presenter不是一个好的做法。首先它们是分离的层,这违反了关注点分离原则。其次,如果您在Fragment中创建Presenter,您将Presenter的生命周期绑定到视图的LifeCycle上,当Fragment被销毁并重新创建时,您会创建一个新的Presenter,但它们是不同的层。

模型(Model)是定义要在用户界面中显示或执行其他操作的数据的接口。

Presenter作用于模型和视图。它从存储库(模型)中检索数据,并格式化以在视图中显示。

视图(View)是一种被动的接口,用于显示数据(模型)并将用户命令(事件)路由到Presenter以对该数据进行操作。

因此,Activity可以充当总控制器,创建PresentersViews并将它们连接起来。

enter image description here

如果我们谈论您的问题,是的,您可以在片段中注册Presenter。但是,应避免在用作视图的片段中创建Presenters。

但是,安卓社区有许多不同的MVP模式方法,如下所示。 https://plus.google.com/communities/114285790907815804707

为什么Activity不是ui元素? http://www.techyourchance.com/activities-android/


1
感谢您详细的回答!由于您似乎是一位经验丰富的开发者,您是否有解决一个Activity和多个片段问题的解决方案? - MilanNz
5
正如我在答案中提到的,如果你需要在同一个activity中使用多个fragment,你可以让activity创建新的fragment,提交fragment事务,创建presenter并将它们连接起来。但是我没有在谷歌示例中看到过这样的实现方式,他们实现了一个activity对应一个fragment。 - savepopulation

2
如果您正在使用一个Activity来托管多个Fragment,并且您还使用Dagger 2来注入Presenter,那么您可以直接将每个Presenter注入到每个Fragment中。
我的使用案例故事
我从几个月前开始使用架构进行项目开发,自从我发现了Android jetpack Navigation Component之后,我开始将所有的应用视图迁移到这种模式。
所以,在这个过程中,我遇到了许多需要重构的情况,而我在这种情况下不知道该怎么做。
由于我从一开始就使用Dagger 2将我的Presenter注入到我的Activity中,所以在使用Fragment时也不会有太大的变化。
我查看了同一个存储库,以了解如何在Fragment中遵循体系结构,如果您只有1个Fragment作为子项,则在主Activity中进行Presenter的实例化确实是一个很好的方法。
问题在于,如果我需要在一个主Activity中拥有多个Fragment,我应该创建每个Presenter的实例并通过我的FragmentManager将其传递到每个Fragment中,我认为这不是我想要的,因为它会增加从主Activity中Presenter的多个实例化。
这导致了一个情况,在我的主Activity中有所有Presenter的多个实例,以及一些接口来处理如果需要分离工作/视图的情况。
在使用多个Fragment的情况下,一种简单的方法就是不考虑主Activity,而是直接将Presenter注入到每个Fragment本身中。
由于使用Dagger,这样做可以使注入更加简洁。
看一个简单的例子。
class MainMenuActivity : BaseActivity(){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        inflateMainFragment(savedInstanceState)
    }

      override fun getLayout(): Int {
        return R.layout.activity_main_menu
    }

    fun inflateMainFragment(savedInstanceState: Bundle?){
        if (savedInstanceState == null) {
            val fragment = MainMenuFragment()
            supportFragmentManager
                .beginTransaction()
                .add(R.id.nav_host_fragment, fragment)
                .commit()
        }
    }
}

正如您所看到的,这里我没有任何现有呈现器的实例化需求。相反,我只是在每个片段中注入我需要的每个呈现器。

Original Answer翻译成"最初的回答"

class MapsFragment: BaseMapFragment(), MapContract.MapView {

    private lateinit var mMap: GoogleMap

    @Inject
    lateinit var presenter: MapsPresenter

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_paseo,container,false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        (requireActivity().application as YawpApplication).getAppComponent()?.inject(this)
        presenter.attachView(this)
        setupToolbar()
        setupMap()
    }
}

利用Fragment的生命周期,你可以在onDestroyView()方法中分离所有的Fragment视图,并且在垃圾收集器运行时节省一些内存空间。"最初的回答"
 override fun onDestroyView() {
        super.onDestroyView()
        presenter.detachView()
        presenter.detachJob()
    }

我在Google官方的代码库中找到了一个问题,帮助我更好地理解它。
您可以在此处查看:这里。原始答案为"Original Answer"。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接