如您所知,将Android应用程序设计为模块是当今Android开发界的流行做法之一。但是这种趋势带来了一些挑战。其中之一是Circular Dependency.
例如,我有一个导航模块,该模块从Home Feature模块中打开HomeActivity
。另外,我必须从产品模块中打开另一个活动,例如ProductListActivity。
如果我在以下活动之间进行导航,则首页功能必须包含导航模块,而导航模块应包含HomeFeature:
val intent = Intent(activity, HomeActivity::class.java)
这将导致circular dependency
问题。
要找出这个问题的最快解决方案,就是创建如下所示的意图,并以此方法构建导航系统。
Intent(Intent.ACTION_VIEW).setClassName(PACKAGE_NAME, className)
所以我的问题是,使用这种导航方法还会面临哪些其他问题?有没有其他方法可以处理模块化Android应用程序中的导航?
答案 0 :(得分:5)
这是我刺激的解决方案。这样可以使用显式意图。您也可以对navigation component进行一些修改,然后将此方法应用于单一活动应用程序。
这是模块B的导航对象
object ModuleBNavigator {
internal lateinit var navigationImpl: ModuleBContract
fun setNavigationImpl(navigationImpl: ModuleBContract) {
this.navigationImpl = navigationImpl
}
interface ModuleBContract {
fun navigateModuleA(self: Activity, bundle: Bundle?)
}
}
这是模块B活动
class ModuleBActivity : Activity() {
companion object {
private const val BUNDLE = "BUNDLE"
fun newIntent(context: Context, bundle: Bundle?) = Intent(context, ModuleBActivity::class.java).apply {
putExtra(BUNDLE, bundle)
}
}
}
这是向导航模块A插入导航元素的应用程序模块类
class ApplicationModuleApp : Application() {
// Can also inject with a DI library
override fun onCreate() {
super.onCreate()
ModuleBNavigator.setNavigationImpl(object : ModuleBNavigator.ModuleBContract {
override fun navigateModuleA(self: Activity, bundle: Bundle?) {
self.startActivity(ModuleBActivity.newIntent(self, bundle))
}
})
}
}
最后,您可以使用提供的实现从模块A->模块B导航
class ModuleAActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ... Some code
ModuleBNavigator.navigationImpl.navigateModuleA(this, Bundle())
// .. Some code
}
}
此方法避免了对圈子的依赖,并且您不再需要使用隐式意图。 希望这会有所帮助。
答案 1 :(得分:1)
对于另一种方法-实际上与我在问题中提到的类似-哪个实现属于sanogueralorenzo
创建一个loader
来填充模块类
const val PACKAGE_NAME = "com.example.android"
private val classMap = mutableMapOf<String, Class<*>>()
private inline fun <reified T : Any> Any.castOrReturnNull() = this as? T
internal fun <T> String.loadClassOrReturnNull(): Class<out T>? =
classMap.getOrPut(this) {
try {
Class.forName(this)
} catch (e: ClassNotFoundException) {
return null
}
}.castOrReturnNull()
创建一个String extension function
以动态加载Intents
。
private fun intentTo(className: String): Intent =
Intent(Intent.ACTION_VIEW).setClassName(BuildConfig.PACKAGE_NAME, className)
internal fun String.loadIntentOrReturnNull(): Intent? =
try {
Class.forName(this).run { intentTo(this@loadIntentOrReturnNull) }
} catch (e: ClassNotFoundException) {
null
}
创建另一个String extension function
以动态加载Fragments
internal fun String.loadFragmentOrReturnNull(): Fragment? =
try {
this.loadClassOrReturnNull<Fragment>()?.newInstance()
} catch (e: ClassNotFoundException) {
null
}
为您的功能实现创建一个Feature
界面
interface Feature<T> {
val dynamicStart: T?
}
我假设您具有Messages
功能。实施动态功能界面
object Messages : Feature<Fragment> {
private const val MESSAGES = "$PACKAGE_NAME.messages.presentation.MessagesFragment"
override val dynamicStart: Fragment?
get() = MESSAGES.loadFragmentOrReturnNull()
}
最后在另一个模块中使用它无依赖
Messages.dynamicStart?.let {
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.fl_main, it)
.commit()
}
}