13

假设您有一个跨平台应用程序。该应用程序在 Android 和 iOS 上运行。您在两个平台上的共享语言是 Java。通常,您会用 Java 编写业务逻辑,而所有 UI 特定部分都用 Java(适用于 Android)和 Objective-C(适用于 iOS)。

通常,当您在跨平台、跨语言应用程序中实现MVP 模式时,您将拥有 Java 中的 Model 和 Presenter,并为您的 Views 提供一个 Java 接口,这是您的 Presenter 已知的。这样,您的共享 Java 演示者可以与您在平台特定部分上使用的任何视图实现进行通信。

假设我们想编写一个带有 Java 部分的 iOS 应用程序,以后可以与同一个 Android 应用程序共享。这是设计的图形表示:

在此处输入图像描述

左侧是 Java 部分。在 Java 中,您编写模型、控制器以及视图接口。您使用依赖注入进行所有接线。然后可以使用J2objc将 Java 代码转换为 Objective-C 。

在右侧,您有 Objective-C 部分。在这里,您UIViewController可以实现翻译成 ObjectiveC 协议的 Java 接口。

问题:

我正在苦苦挣扎的是视图之间的导航是如何发生的。假设您在 UIViewControllerA 上,并且您点击了一个按钮,该按钮应将您带到 UIViewControllerB。你会怎么办?

情况1:

在此处输入图像描述

您将按钮点击报告给 UIViewControllerA 的 Java ControllerA (1),Java ControllerA 调用链接到 UIViewControllerB (3) 的 Java ControllerB (2)。然后你有一个问题,你不知道从 Java 控制器端如何将 UIViewControllerB 插入到 Objective-C 视图层次结构中。您无法从 Java 端处理它,因为您只能访问 View 接口。

案例二:

在此处输入图像描述

无论是模态的还是使用 UINavigationController 或其他 (1),您都可以转换到 UIViewControllerB。然后,首先您需要绑定到 Java ControllerB (2) 的 UIViewControllerB 的正确实例。否则 UIViewControllerB 无法与 Java ControllerB (2,3) 交互。当您拥有正确的实例时,您需要告诉 Java ControllerB 视图 (UIViewControllerB) 已显示。

我仍在努力解决如何处理不同控制器之间导航的问题。

如何对不同控制器之间的导航进行建模并适当地处理跨平台视图更改?

4

3 回答 3

5

简短的回答:

以下是我们的做法:

  • 对于简单的“正常”内容(例如打开设备摄像头的按钮或打开另一个Activity/UIViewController操作背后没有任何逻辑的按钮) -ActivityA直接打开ActivityB. ActivityB如果需要,现在负责与应用共享逻辑层进行通信。
  • 对于任何更复杂或依赖逻辑的东西,我们使用 2 个选项:
    1. ActivityA调用 some 的方法,UseCase该方法返回一个enumorpublic static final int并相应地采取一些行动 -OR-
    2. SaidUseCase可以调用ScreenHandler我们之前注册的方法,该方法知道如何Activities使用一些提供的参数从应用程序中的任何位置打开 common。

长答案:

我是一家公司的首席开发人员,该公司使用 java 库用于应用程序的模型、逻辑和业务规则,这两个移动平台(Android 和 iOS)都使用 j2objc 实现。

我的设计原则直接来自 Uncle Bob 和 SOLID,在设计包含组件间通信的整个应用程序时,我真的不喜欢使用 MVP 或 MVC,因为你开始将每个应用程序Activity与 1 和只有 1链接,Controller这有时是可以的,但大多数时候你最终会得到一个控制器的上帝对象,它往往会像View. 这可能导致严重的代码异味。

我最喜欢的处理方式(也是我认为最干净的方式)是将所有内容分解为UseCases每个处理应用程序中的 1 个“情况”。当然,您可以拥有一个Controller处理其中几个的,UseCases但它所知道的只是如何委派给这些UseCases,仅此而已。

此外,如果此操作是简单的“带我到地图屏幕”或类似的任何操作,我看不到将 an 的每个操作链接ActivityController坐在逻辑层中的原因。的角色Activity应该是处理Views它所持有的,并且作为生活在应用程序生命周期中的唯一“智能”事物,我认为它没有理由不能调用下一个活动本身的开始。

此外,Activity/UIViewController生命周期过于复杂且彼此差异太大,无法由通用 java 库处理。这是我认为是“细节”而不是真正的“业务规则”的东西,每个平台都需要实现和担心,从而使 java lib 中的代码更加稳固,不易更改。

同样,我的目标是让应用程序的每个组件都尽可能地符合 SRP(单一职责原则),这意味着将尽可能少的东西链接在一起。

所以一个简单的“正常”东西的例子:

(所有例子都是虚构的)

ActivityAllUsers显示模型对象项的列表。这些项目来自调用AllUsersInteractor- aUseCase controller在后台线程中(这反过来也由 java lib 处理,并在请求完成时调度到主线程)。用户单击此列表中的一项。在这个例子中,ActivityAllUsers已经有了模型对象,所以打开ActivityUserDetail是一个直接调用这个数据模型对象的包(或其他机制)。如果需要进一步的操作,新活动ActivityUserDetail负责创建和使用正确的。UseCases

复杂逻辑调用示例:

ActivityUserDetail有一个标题为“添加为朋友”的按钮,单击该按钮会调用以下回调onAddFriendClicked方法ActivityUserDetail

public void onAddFriendClicked() {  
  AddUserFriendInteractor addUserFriend = new AddUserFriendInteractor();
  int result = addUserFriend.add(this.user);
  switch(result){
    case AddUserFriendInteractor.ADDED:
      start some animation or whatever
      break;
    case AddUserFriendInteractor.REMOVED:
      start some animation2 or whatever
      break;
    case AddUserFriendInteractor.ERROR:
      show a toast to the user
      break;
    case AddUserFriendInteractor.LOGIN_REQUIRED:
      start the log in screen with callback to here again
      break;

  }
}

更复杂的调用示例

ABroadcastReceiver在 Android 或AppDelegateiOS 上会收到推送通知。这被发送到NotificationHandlerjava lib 逻辑层中的哪个。在NotificationHandler构造一次的构造函数中,App.onCreate()它需要一个ScreenHandler interface您在两个平台上实现的构造函数。解析此推送通知并在 中调用正确的方法ScreenHandler以打开正确的Activity.

底线是:尽可能保持View愚蠢,保持Activity足够聪明以处理自己的生命周期和处理自己的观点,并与自己的沟通controllers(复数!),其他一切都应该写(希望测试-首先 ;) ) 在 java 库中。

使用这些方法,我们的应用程序目前在 java lib 中运行了大约 60-70% 的代码,下一次更新有望将其提高到 70-80%。

于 2015-05-19T18:28:23.903 回答
1

在共享我所谓的“核心”(用 Java 编写的应用程序的)的跨平台开发中,我倾向于将接下来要显示的视图的所有权授予UI 。这使您的应用程序更加灵活,可以根据需要适应环境(在 iOS 上使用 UINavigationController,在 Android 上使用 Fragments 以及在 Web 界面上使用动态内容的单个页面)。

controllers不应该被绑定到一个视图,而是履行一个特定的角色(一个用于登录/注销的 accountController,一个用于显示和编辑食谱的 recipeController,等等)。

您将拥有interfacesfor yourcontrollers而不是您的views. 然后您可以使用工厂设计模式controllers端(您的 Java 代码)和UI端实例化您views的。引用了您的domain 并使用它为请求的视图提供了一些实现特定接口的控制器。view factorycontroller factory

在此处输入图像描述

示例: 点击“登录”按钮后,ahomeViewControllerViewControllerFactory请求一个loginViewController. 该工厂反过来向ControllerFactory请求实现accountHandling接口的控制器。然后实例化一个新loginViewControllerhomeViewController. 然后homeViewController将新的视图控制器呈现给用户。

由于您的“核心”与环境无关,并且仅包含您的和业务逻辑,因此它应该保持稳定并且不易被编辑。

你可以看看我制作的这个简化的演示项目,它说明了这个设置(减去接口)。

于 2015-05-19T21:54:52.963 回答
1

我建议您使用某种插槽机制。与其他 MVP 框架使用的类似。

定义:插槽是视图的一部分,可以插入其他视图。

在您的演示者中,您可以定义任意数量的插槽:

GenericSlot slot1 = new GenericSlot();
GenericSlot slot2 = new GenericSlot();
GenericSlot slot3 = new GenericSlot();

这些插槽必须在演示者的视图中具有引用。你可以实现一个

setInSlot(Object slot, View v);

方法。如果您setInSlot在视图中实现,则视图可以决定应如何包含它。

看看这里的插槽是如何实现

于 2015-05-23T11:50:30.323 回答