如何实现 Android 库模块之间的导航,重点关注可重用性和关注点分离?

Muh*_*uja 5 navigation android module

我正在尝试实现一个单独的导航模块来在 android 库模块之间导航,重点关注可扩展性、可重用性和模块独立性。我的应用程序架构与此示例类似:

在此输入图像描述

我目前的做法

NavigatorInterface1-为每个库定义

2- 实现NavigatorInterface中的每一个NavigationModule。(当然导航模块会了解所有其他库模块,但这并不重要,因为它不会被重用)

以下是我上述架构的示例代码:

: 验证

|-- 登录活动

|-- 报名活动

|-- AuthNavigator

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);
}
Run Code Online (Sandbox Code Playgroud)

:轮廓

|-- 个人资料活动

|-- 简介导航

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);
}
Run Code Online (Sandbox Code Playgroud)

:关于

|-- 关于活动

|-- 关于Navigator

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);
}
Run Code Online (Sandbox Code Playgroud)

上述方法试图消除同一模块内: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));
    }
}
Run Code Online (Sandbox Code Playgroud)

问题:

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

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

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

4-如何以启动特定活动的方式:navigation从模块调用模块,例如: ?:appProfileActivity

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

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

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

小智 0

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

interface Navigator {
    fun navigate(activity: Activity, bundle: Bundle)
}
Run Code Online (Sandbox Code Playgroud)

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

interface NavigatorFactory {
    fun of(packageName: String): Navigator
}
Run Code Online (Sandbox Code Playgroud)

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

interface NavigatorApplication {
    fun getNavigatorFactory(): NavigatorFactory
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

class Application: NavigatorApplication {
    val factoryImpl = NavigatorFactoryImpl()
    fun getNavigatorFactory() = factoryImpl
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

如果您将来有基于单活动的架构,您还可以查看Jetpack Navigation by Google