Search code examples
androidandroid-activitymvpj2objc

Difficulties implementing Model-View-Presenter in Android


Model-View-Presenter (MVP) is a well known design pattern for GUI applications. For Android, implementing the business logic in a plain Java module facilitates testing without requiring an Android emulator.

However, I am having difficulties implementing the pattern on Android because of special requirements to the GUI of Android applications:

  • An Activity may be destroyed at any point (incoming call, user presses home button, ...), and when recreated it should be in the exact same state as when it was left. This is unlike most other GUI applications.

  • An Activity can go through many lifecycle states. It may be paused in which case the UI of the Activity should not be modified. If for example some data is being loaded in the background, it cannot be delivered to the View part of MVP (Activity) if it is in a paused state. Again, this is an unusual requirement.

I have read the blog post MVP for Android and looked at the example source code. The end goal I am trying to achieve by using the MVP pattern is to be able to translate all business logic to Objective-C using the transpiler j2objc, such that the business logic can be reused while implementing the same app on iOS.

Is there anyone that have implemented the MVP pattern for Android successfully, and in that case, what am I missing?


Solution

  • I suggest implementing the MVP component without involving Activity, perhaps conceptually thinking about what would be useful on both Android and GWT. Create the component using test-driven-development with a mocked View interface, adding tests until the business logic is fully implemented and verified. TDD helps keep the component's API lean (why write tests for stuff you don't need?), which makes porting the component easier.

    The Activity requirements you describe can be generalized to be platform-independent: the component should be serializable (small 's', not specifically Java serialization), and needs to accept lifecycle state events. Those, too, can be fully tested using mocks for system features. As you go through this step, you'll likely notice that few of the Activity requirements are necessarily Android-specific, and may be useful on other platforms. Avoid creating huge service APIs; to support serialization, for example, all that's needed are store/load methods, not something like the Parcel API. I've found describing such service APIs to another developer on a whiteboard to be a great way to find unnecessary fluff.

    Next, port the component to Android, perhaps by creating an Activity that delegates to the component and provides Android-specific implementation classes for the mocked interfaces. It should all "just work" the first time, but in reality, some requirements may have been missed, so add them to the platform-independent part and repeat.

    When you're ready to port to iOS, reimplement those previously mocked interfaces. If these interfaces are lean, it will probably be easier to create them directly in Objective-C, importing the protocol headers generated by j2objc. For example, j2objc's NSDictionaryMap class implements java.util.Map with an NSDictionary implementation -- no need to write and translate a Java version since its just using iOS APIs.