Android Navigation组件(三)

2020-10-13  本文已影响0人  SimpleFunc

在组件化项目中使用Navigation

Navigation组件目前并不能完美的支持组件化项目,主要问题是在module中声明的graph中destination不能直接被App引用到,在运行时会找不到对应的destination. 主要navigation资源的原因,目前设计中Navigation graph是独立的,graph中声明的destination是无法共享的。

虽然Navigation graph中可以include其他的navigation graph, 但是include之后的graph还是无法共享声明的destination。

官方近期更新了Navigation组件,支持了Dynamic module,新增了一个<include-dynamic/>实现,不支持deepLink. 由于国内使用不了该功能,就不再描述了。

其他实现方式

  1. 只有一个navigation graph

将所有模块中的fragment都声明在App的一个Navigation graph内,这样是可行的,但是这种方式解耦违反组件化的逻辑,而且只有一个navigation graph,所有destination的改动都需要修改这个graph,维护成本高。

  1. 通过include graph实现跳转

App中的Nav graph include其他模块的Nav graph,然后通过action跳转到下个Nav graph, 如:

<action
      android:id="@+id/action_to_next_fragment"
      app:destination="@navigation/nav_graph2" />

调用这个action之后是可以跳转到下个Nav graph中声明的app:startDestination对应的fragment. 如果下个nav_graph没有声明startDestination是不行的。

这个方案也有一个很大的缺陷,就是不能直接Navigation到include的graph中的非startDestination fragment. 只能在graph内部进行跳转。而且声明的全局action都跳转不过去。

  1. deepLink
    使用deepLink可以解决这个问题,跟方案2前面的步骤一样。然后需要在模块中对外暴露的fragment加
    <deepLink />, 这样就可以直接navigate过去。 这个方案目前是最可行的方案,但是感觉deepLink被滥用,因为deepLink的真正作用是通过外部跳转进入。对于比较复杂的项目,可能好多fragment都需要添加deepLink。而且deepLink的传参数格式需要完全匹配,容易出错。

  2. 自动合并Nav graph
    可以像官方处理AndroidManifest.xml一样,在编译时将所有module中的nav_graph文件中声明的destination都合并到app中的nav_graph文件中,理论上可以解决该问题。但是这个方案有也有很多要考虑的问题

存在的问题:

若是直接合并到app的nav graph中,则需要一个备份文件,在编译完成之后是需要还原回去的,这个操作本身就不太合理。正常的操作将所有nav graph文件生成为一个新的xml文件,生成到build/generated/文件下,然后修改布局中app:navGraph中的对应的依赖,由于这些布局文件不是生成的,都是开发者自己创建的,所以不太优雅。而且需要寻找声明app:navGraph的地方,编译前修改,编译完成之后需要还原。

还有一种方案就是修改编译之后的resouce.arsc文件,这个操作比较复杂,需要解ResourceTable,找到对应的资源文件,修改生成新的饿resouce.arsc替换就行了。这个方案比较复杂,而且兼容性问题比较多,需要考虑Android系统和gradle,Android编译API的兼容性问题,开发和维护成本都比较高。

这个方案应该是最合理的方案,官方后面可能会解决这个问题。

Navigation组件与Router

在组件化项目中一般都会使用Router来导航,由于之前的Router方案都是针对Activity的,之前的fragment添加,替换,移除等操作严重依赖Activity, 所以使用Router直接跳转到对应的fragment是比较麻烦的。使用navigation组件之后就可以比较简单的实现了。

以前Router绑定的是URI和activity的class, 一般都是通过注解自动绑定。现在需要定URI, fragment或action或deepLink. 如果deepLink格式统一都不需要绑定,直接使用即可。注解需要做调整。
例如:绑定URI和Fragment, 之前的绑定的注解是这样的:

@Route(path = "/test/list")
class TestActivity : Activity {
      ...
}

因为navigation跳转到Fragment并不需要Fragment的class,需要的是在nav graph声明的id. 所以需要注解添加参数来绑定,可以改为:

@Route(path = "/test/list", resId = R.id.testfragment)
class TestFragment : Fragment {
      ...
}

或者是deepLink, 如果path与deepLink的URI一直都不用绑定

@Route(path = "/test/list", deepLink = "app://test/list")
class TestFragment : Fragment {
      ...
}

action不太一样,action针对的是动作,所以不应该将包含action的注解声明在Fragment上.可以声明在方法上,如:

object NaviControllerHelper{

    @RouteAction(path = "/test/list")
    fun navigateToDetail(navController: NavController){
        navController.navigate(R.id.action_to_detail)
    }

}

然后通过注解获取到方法名称,反射调用这个方法即可。

上一篇下一篇

猜你喜欢

热点阅读