如何在跨平台应用程序中处理基于UI的导航?

12 android objective-c ios swift j2objc

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

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

让我们假设我们想要编写一个带有Java部分的iOS应用程序,以后可以使用相同的Android应用程序进行共享.以下是设计的图形表示:

在此输入图像描述

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

在右侧,您有Objective-C部分.在这里,您UIViewController可以实现Java接口,这些接口转换为ObjectiveC协议.

问题:

我正在努力的是如何进行视图之间的导航.假设你在UIViewControllerA上,然后点击一个按钮,它会带你到UIViewControllerB.你会怎么做?

情况1:

在此输入图像描述

您将按钮点击报告给UIViewControllerA的Java ControllerA(1),Java ControllerA调用链接到UIViewControllerB(3)的Java ControllerB(2).然后,您遇到的问题是Java Controller端不知道如何在Objective-C View层次结构中插入UIViewControllerB.您无法从Java端处理此问题,因为您只能访问View接口.

案例2:

在此输入图像描述

您可以转换到UIViewControllerB,无论它是模态还是UINavigationController或其他(1).然后,首先需要正确的UIViewControllerB实例,该实例绑定到Java ControllerB(2).否则,UIViewControllerB无法与Java ControllerB(2,3)进行交互.当您拥有正确的实例时,您需要告诉Java ControllerB已经显示了View(UIViewControllerB).

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

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

zlo*_*iel 5

简短回答:

我们是这样做的:

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

长答案:

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

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

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

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

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

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

这是一个简单的“正常”内容的例子:

(所有例子完全是虚构的)

ActivityAllUsers显示模型对象项的列表。这些项目来自于后台线程中的调用AllUsersInteractor- a UseCase 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;

  }
}
Run Code Online (Sandbox Code Playgroud)

更复杂的调用示例

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

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

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


小智 1

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

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

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

GenericSlot slot1 = new GenericSlot();
GenericSlot slot2 = new GenericSlot();
GenericSlot slot3 = new GenericSlot();
Run Code Online (Sandbox Code Playgroud)

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

setInSlot(Object slot, View v);
Run Code Online (Sandbox Code Playgroud)

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

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