You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发:是否需为每个Activity创建MVP的Presenter、Model及契约?

Android MVP: Contract Per Activity & Scaling for Multiple Activities

Great question—this is something a lot of Android devs grapple with when adopting MVP, especially as apps scale. Let’s break this down clearly:

Do You Need a Contract for Every Activity?

Short answer: Yes, but with flexibility.

The core idea of MVP contracts is to enforce clear boundaries between the View (your Activity/Fragment), Presenter, and Model. Each Activity (or Fragment, since they’re often the primary View layer in MVP) represents a distinct screen or feature set, so a dedicated contract ensures:

  • No cross-feature coupling: Each feature’s responsibilities stay isolated, making it easier to modify one without breaking others.
  • Better testability: You can mock View/Presenter interfaces easily, no need to rely on concrete Activity classes for unit tests.
  • Improved maintainability: New developers can quickly grasp exactly what a screen does just by looking at its contract.

That said, if you have tiny, reusable components (like a simple settings screen with minimal interactions), you might reuse a base contract or simplify it—but as a general rule, one contract per feature screen (Activity/Fragment) is the best practice.

How to Implement MVP for Multiple Activities

Here’s a practical, scalable approach to keep your codebase clean:

1. Use Base Interfaces to Cut Down Boilerplate

Create base interfaces for View, Presenter, and Model to avoid repeating common methods across every contract. For example:

// Base View interface
interface BaseView {
    fun showLoading()
    fun hideLoading()
    fun showError(message: String)
}

// Base Presenter interface
interface BasePresenter<V : BaseView> {
    fun attachView(view: V)
    fun detachView()
}

Then your feature-specific contracts can extend these to add screen-specific methods:

// Contract for a User Profile screen
interface UserProfileContract {
    interface View : BaseView {
        fun displayUserDetails(user: User)
    }

    interface Presenter : BasePresenter<View> {
        fun loadUserProfile(userId: String)
    }

    interface Model {
        suspend fun fetchUser(userId: String): User
    }
}

2. Organize Packages by Feature, Not Layer

Instead of having monolithic views/, presenters/, models/ folders, structure your code by feature. This makes it trivial to locate all code related to a single screen:

app/src/main/java/com/yourapp/
├── userprofile/
│   ├── UserProfileActivity.kt
│   ├── UserProfileContract.kt
│   ├── UserProfilePresenter.kt
│   └── UserProfileModel.kt
├── dashboard/
│   ├── DashboardActivity.kt
│   ├── DashboardContract.kt
│   ├── DashboardPresenter.kt
│   └── DashboardModel.kt
└── base/
    ├── BaseView.kt
    └── BasePresenter.kt

3. Handle Navigation Without Tying Presenters to Android Framework

Your Presenter shouldn’t directly start Activities (that would break testability by coupling it to Android classes). Instead:

  • Define navigation callbacks in the View interface:
    interface UserProfileContract {
        interface View : BaseView {
            // ... other methods
            fun navigateToEditProfileScreen(userId: String)
        }
    }
    
  • Let the Activity (the View implementation) handle the actual navigation with Intents:
    class UserProfileActivity : AppCompatActivity(), UserProfileContract.View {
        // ...
        override fun navigateToEditProfileScreen(userId: String) {
            val intent = Intent(this, EditProfileActivity::class.java)
            intent.putExtra("USER_ID", userId)
            startActivity(intent)
        }
    }
    
  • The Presenter simply calls view?.navigateToEditProfileScreen(userId) when navigation is needed.

4. Reuse Shared Data Logic with Repositories

If multiple Activities need access to the same data (like user session info), create a shared repository class that’s injected into relevant Presenters. This avoids duplicating data-fetching logic across features.

Final Thoughts

Sticking to one contract per feature screen might feel like extra work upfront, but it pays off massively in maintainability and testability as your app grows. Using base interfaces and feature-based packaging will keep your codebase organized and reduce unnecessary repetition.

内容的提问来源于stack exchange,提问作者Ilya Maximencko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:25:53