Android MVP架构中Activity含多Fragment的Presenter与View设计疑问
Hey there! Let's break this down based on how your two Fragments relate to each other and their business logic—this is a super common scenario in MVP, so you're not alone here. The core idea in Google's MVP is separation of concerns and single responsibility, so your choice will hinge on how much overlap exists between the two tabs.
First, remember that MVP pairs a View (in this case, your Fragment) with a Presenter that handles its specific business logic. Avoid forcing shared Presenters unless the logic is truly reusable—over-sharing leads to messy, unmaintainable code.
Case 1: Fragments Have Independent Business Logic
If your two tabs do completely different things (e.g., one shows a chat list, the other displays user settings), give each Fragment its own Presenter and corresponding View interface. This keeps each component focused, easy to test, and decoupled from the other.
Why?
- Each Presenter only cares about its Fragment's needs—no extra code for handling unrelated logic.
- If you need to modify one tab's behavior later, you won't risk breaking the other.
- Unit testing becomes simpler, since each Presenter has a clear, narrow scope.
Case 2: Fragments Share Core Business Logic
If the tabs are variations on the same theme (e.g., two product category lists with identical load/refresh/click logic), you can reuse base logic while keeping separate Presenters and View interfaces for each Fragment. This balances code reuse with maintainability.
Why?
- You avoid duplicating code for common tasks (like fetching data from a repository).
- Each Fragment still has its own contract, so you can add tab-specific features later without disrupting the shared logic.
Let's look at concrete examples for both cases.
Example 1: Independent Fragments
First, define separate contracts for each tab:
// ChatContract.java public interface ChatContract { interface View extends BaseView { void showChatList(List<Chat> chats); void showLoadError(); } interface Presenter extends BasePresenter { void loadChats(); void refreshChats(); } } // SettingsContract.java public interface SettingsContract { interface View extends BaseView { void showUserSettings(UserSettings settings); void showSaveSuccess(); } interface Presenter extends BasePresenter { void loadSettings(); void saveSettings(UserSettings newSettings); } }
Then implement each Fragment with its own Presenter:
public class ChatFragment extends Fragment implements ChatContract.View { private ChatContract.Presenter mPresenter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPresenter = new ChatPresenter(this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_chat, container, false); // Setup RecyclerView, refresh button, etc. return view; } @Override public void onStart() { super.onStart(); mPresenter.loadChats(); } // Implement ChatContract.View methods @Override public void showChatList(List<Chat> chats) { // Update your RecyclerView adapter } @Override public void showLoadError() { Toast.makeText(getContext(), "Failed to load chats", Toast.LENGTH_SHORT).show(); } }
Example 2: Shared Core Logic
First, create a base contract and Presenter for shared behavior:
// BaseProductContract.java public interface BaseProductContract { interface View extends BaseView { void showProductList(List<Product> products); void showLoadError(String message); } interface Presenter extends BasePresenter { void loadProducts(String category); void refreshProducts(String category); } } // BaseProductPresenter.java public class BaseProductPresenter<T extends BaseProductContract.View> implements BaseProductContract.Presenter { protected T mView; protected ProductRepository mRepository; public BaseProductPresenter(T view) { mView = view; mRepository = new ProductRepository(); } @Override public void loadProducts(String category) { // Shared logic to fetch products mRepository.getProducts(category, new Callback<List<Product>>() { @Override public void onSuccess(List<Product> products) { mView.showProductList(products); } @Override public void onError(String error) { mView.showLoadError(error); } }); } @Override public void refreshProducts(String category) { // Shared refresh logic mRepository.refreshProducts(category, this::loadProducts); } }
Then create tab-specific contracts and Presenters that extend the base:
// ElectronicsProductContract.java public interface ElectronicsProductContract extends BaseProductContract { interface View extends BaseProductContract.View { // Tab-specific view method void showElectronicsExclusiveOffer(String offer); } interface Presenter extends BaseProductContract.Presenter { void loadElectronicsProducts(); void checkExclusiveOffers(); } } // ElectronicsProductPresenter.java public class ElectronicsProductPresenter extends BaseProductPresenter<ElectronicsProductContract.View> implements ElectronicsProductContract.Presenter { public ElectronicsProductPresenter(ElectronicsProductContract.View view) { super(view); } @Override public void loadElectronicsProducts() { // Use base logic with specific category super.loadProducts("electronics"); } @Override public void checkExclusiveOffers() { // Tab-specific business logic mRepository.getElectronicsOffers(offer -> { mView.showElectronicsExclusiveOffer(offer); }); } }
- Keep your Activity as a container only: Don't put business logic in the Activity. Its job is to manage Fragment transactions and pass any shared dependencies (like a repository instance) to the Fragments/Presenters if needed.
- Avoid shared View interfaces unless necessary: Even if logic is shared, each Fragment's View should have its own interface—you can extend a base View to reuse common methods.
- Testability first: A good MVP design makes unit testing easy. If a Presenter is handling two unrelated Views, testing becomes a headache.
内容的提问来源于stack exchange,提问作者Ankit Saini

