【问题标题】:How do I implement navigation between android library modules focusing reusability and separation of concerns?如何在关注可重用性和关注点分离的 android 库模块之间实现导航?
【发布时间】:2020-04-22 07:42:38
【问题描述】:

我正在尝试实现一个单独的导航模块,以便在关注可扩展性、可重用性和模块独立性的 android 库模块之间导航。我的应用架构类似于这个例子:

我目前的做法

1- 为每个库定义NavigatorInterface

2- 在NavigationModule 中实现每个NavigatorInterface。 (当然导航模块会知道所有其他库模块,但没关系,因为它不会被重用)

以下是我上面提到的架构的示例代码:

:auth

|-- 登录活动

|-- 注册活动

|-- 认证导航器

public class LoginActivity extends AppCompatActivity {
    private NavigatorCoordinator navigator; // how do I achieve this injection, without using Dagger etc.

    .....
    private void signup(){
        navigator.NavigateToSignup(this);
    }

    private void profile(){
        navigator.NavigateToProfile(this);
    }
    .....
}


public class SignupActivity extends AppCompatActivity {
    private NavigatorCoordinator navigator; // how do I achieve this injection, without using Dagger etc.

    .....
    private void login(){
        navigator.NavigateToLogin(this);
    }

    private void profile(){
        navigator.NavigateToProfile(this);
    }
    .....
}



public interface AuthNavigator {
    void NavigateToLogin(Context context);
    void NavigateToRegister(Context context);
}

:个人资料

|-- 个人资料活动

|-- ProfileNavigator

public class ProfileActivity extends AppCompatActivity {
    private NavigatorCoordinator navigator; // how do I achieve this injection, without using Dagger etc.

    .....
    private void about(){
        navigator.NavigateToAbout(this);
    }
    .....
}


public interface ProfileNavigator {
    void NavigateToProfile(Context context);
}

:关于

|-- 关于活动

|-- 关于导航器

public class AboutActivity extends AppCompatActivity {
    private NavigatorCoordinator navigator; // how do I achieve this injection, without using Dagger etc.

    .....
    private void profile(){
        navigator.NavigateToProfile(this);
    }
    .....
}


public interface AboutNavigator {
    void NavigateToAbout(Context context);
}

上述方法是尝试消除同一模块:auth 内以及两个模块:profile:about 之间的循环依赖。下面是:navigation的实现。

:导航

|-- 导航器

public class Navigator implements AuthNavigator, ProfileNavigator, AboutNavigator {
    @Override
    public void NavigateToLogin(Context context) {
        context.startActivity(new Intent(context, LoginActivity.class));
    }

    @Override
    public void NavigateToSingup(Context context) {
        context.startActivity(new Intent(context, SignupActivity.class));
    }

    @Override
    public void NavigateToProfile(Context context) {
        context.startActivity(new Intent(context, ProfileActivity.class));
    }

    @Override
    public void NavigateToAbout(Context context) {
        context.startActivity(new Intent(context, AboutActivity.class));
    }
}

问题:

1- 我相信,我需要在:navigation 模块中实现NavigatorCoordinator,但我该怎么做呢?有这个实现的例子吗?

2- 如何在不使用 Deggar 或任何其他框架的情况下将 NavigatorCoordinator 的依赖项注入驻留在不同 modules 中的每个活动类中?我们可以使用Application类来实现吗,如果可以,请给出一个实现示例?

3- 如何从任何库设置Launcher 活动,例如:LoginActivity

4- 如何从:app 模块调用:navigation 模块以启动特定活动,例如:ProfileActivity

5- 这是实现可重用性、可扩展性和关注点分离的良好模块间导航方法吗?

6- 还有其他类似的好方法吗?任何代码示例?链接到文章?

PS:请不要告诉我使用导航架构组件。

【问题讨论】:

    标签: android module navigation


    【解决方案1】:

    实际上,您可以为此应用一些面向对象的解决方法。首先,您可以考虑在 common 模块中声明一个导航器接口。由于每个模块都是独立的并且彼此不认识,因此您需要遵循更通用的方法。您可以定义类似 Navigator 界面的东西

    interface Navigator {
        fun navigate(activity: Activity, bundle: Bundle)
    }
    

    现在考虑打开活动而不引用它,但使用一个键。您应该有一个工厂来将给定的键映射到活动导航器。你可以认为你想用一个键打开 AboutActivity,因为你没有它的引用。键可以是整数,也可以是类包名,没关系。我会坚持使用包名作为键。

    interface NavigatorFactory {
        fun of(packageName: String): Navigator
    }
    

    在活动中导航时,您需要以某种方式获取这些导航器。一种理想的方法是使用应用程序类,因为您在任何活动中都有它的引用。所以我们定义了一个接口应用到应用类。

    interface NavigatorApplication {
        fun getNavigatorFactory(): NavigatorFactory
    }
    

    所以我们在 common 模块中结束我们的实现。让我们继续应用程序模块。 你需要开始声明这些接口的实现

    class AboutActivityNavigator: Navigator {
        public void navigate(Activity activity, bundle: Bundle) {
            val intent = Intent(activity, AboutActivity::class.java)
            activity.startActivity(intent, bundle)
        }
    }
    

    返回这些 Navigator 实例的工厂实现。

    object NavigatorFactoryImpl: NavigatorFactory {
        fun get(key: String): Navigator {
            when(key) {
                is "com.example.AboutActivity" -> AboutActivityNavigator()
            }
        }
    }
    

    为工厂提供模块已知的接口。这样模块就可以到达接口并接收工厂实例。

    class Application: NavigatorApplication {
        val factoryImpl = NavigatorFactoryImpl()
        fun getNavigatorFactory() = factoryImpl
    }
    

    我们也完成了应用模块。现在,您可以导航到任何模块中所需的每个活动。

    class ProfileActivity: Activity() {
        fun navigateToAboutActivity() {
            (application as? NavigatorApplication)?
                .getNavigatorFactory()
                .of("com.example.AboutActivity")
                .navigate(this, Bundle())
        }
    }
    

    最后,一切都是有代价的。您的模块是完全独立的。但不要忘记,您仍然在传递一些硬编码的密钥来打开活动。此外,您无法保证类型安全。如果您在包中传递诸如 ("page-id",34) 之类的键值对,您可能永远不知道目标活动是否接受相同的键和相同的类型。

    而且如果你以后有基于单activity的架构,也可以查看Jetpack Navigation by Google

    【讨论】:

      猜你喜欢
      • 2016-09-19
      • 2014-09-13
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多