在組件化項目中使用Navigation
Navigation組件目前并不能完美的支持組件化項目,主要問題是在module中聲明的graph中destination不能直接被App引用到切黔,在運(yùn)行時會找不到對應(yīng)的destination. 主要navigation資源的原因砸脊,目前設(shè)計中Navigation graph是獨(dú)立的具篇,graph中聲明的destination是無法共享的纬霞。
雖然Navigation graph中可以include其他的navigation graph, 但是include之后的graph還是無法共享聲明的destination。
官方近期更新了Navigation組件驱显,支持了Dynamic module诗芜,新增了一個<include-dynamic/>實(shí)現(xiàn)瞳抓,不支持deepLink. 由于國內(nèi)使用不了該功能,就不再描述了伏恐。
其他實(shí)現(xiàn)方式
- 只有一個navigation graph
將所有模塊中的fragment都聲明在App的一個Navigation graph內(nèi)孩哑,這樣是可行的,但是這種方式解耦違反組件化的邏輯翠桦,而且只有一個navigation graph横蜒,所有destination的改動都需要修改這個graph,維護(hù)成本高销凑。
- 通過include graph實(shí)現(xiàn)跳轉(zhuǎn)
App中的Nav graph include其他模塊的Nav graph丛晌,然后通過action跳轉(zhuǎn)到下個Nav graph, 如:
<action
android:id="@+id/action_to_next_fragment"
app:destination="@navigation/nav_graph2" />
調(diào)用這個action之后是可以跳轉(zhuǎn)到下個Nav graph中聲明的app:startDestination對應(yīng)的fragment. 如果下個nav_graph沒有聲明startDestination是不行的。
這個方案也有一個很大的缺陷斗幼,就是不能直接Navigation到include的graph中的非startDestination fragment. 只能在graph內(nèi)部進(jìn)行跳轉(zhuǎn)澎蛛。而且聲明的全局action都跳轉(zhuǎn)不過去。
deepLink
使用deepLink可以解決這個問題蜕窿,跟方案2前面的步驟一樣谋逻。然后需要在模塊中對外暴露的fragment加
<deepLink />, 這樣就可以直接navigate過去。 這個方案目前是最可行的方案桐经,但是感覺deepLink被濫用毁兆,因?yàn)閐eepLink的真正作用是通過外部跳轉(zhuǎn)進(jìn)入。對于比較復(fù)雜的項目阴挣,可能好多fragment都需要添加deepLink荧恍。而且deepLink的傳參數(shù)格式需要完全匹配,容易出錯屯吊。自動合并Nav graph
可以像官方處理AndroidManifest.xml一樣送巡,在編譯時將所有module中的nav_graph文件中聲明的destination都合并到app中的nav_graph文件中,理論上可以解決該問題盒卸。但是這個方案有也有很多要考慮的問題
存在的問題:
- AndroidManifest是自動生成的文件骗爆,不是用戶創(chuàng)建的,而且在其他用戶生成的layout文件中有依賴蔽介,如果在編譯前直接修改的話就不太優(yōu)雅摘投。
- 合并只能合并到app的nav graph,每次編譯都需要檢查是否合并虹蓄,合并之后的文件處理不太優(yōu)雅犀呼。
若是直接合并到app的nav graph中,則需要一個備份文件薇组,在編譯完成之后是需要還原回去的外臂,這個操作本身就不太合理。正常的操作將所有nav graph文件生成為一個新的xml文件律胀,生成到build/generated/文件下宋光,然后修改布局中app:navGraph中的對應(yīng)的依賴貌矿,由于這些布局文件不是生成的,都是開發(fā)者自己創(chuàng)建的罪佳,所以不太優(yōu)雅逛漫。而且需要尋找聲明app:navGraph的地方,編譯前修改赘艳,編譯完成之后需要還原酌毡。
還有一種方案就是修改編譯之后的resouce.arsc文件,這個操作比較復(fù)雜蕾管,需要解ResourceTable阔馋,找到對應(yīng)的資源文件,修改生成新的resouce.arsc替換就行了娇掏。這個方案比較復(fù)雜呕寝,而且兼容性問題比較多,需要考慮Android系統(tǒng)和gradle婴梧,Android編譯API的兼容性問題下梢,開發(fā)和維護(hù)成本都比較高。
這個方案應(yīng)該是最合理的方案塞蹭,官方后面可能會解決這個問題孽江。
Navigation組件與Router
在組件化項目中一般都會使用Router來導(dǎo)航,由于之前的Router方案都是針對Activity的番电,之前的fragment添加岗屏,替換,移除等操作嚴(yán)重依賴Activity, 所以使用Router直接跳轉(zhuǎn)到對應(yīng)的fragment是比較麻煩的漱办。使用navigation組件之后就可以比較簡單的實(shí)現(xiàn)了这刷。
以前Router綁定的是URI和activity的class, 一般都是通過注解自動綁定。現(xiàn)在需要定URI, fragment或action或deepLink. 如果deepLink格式統(tǒng)一都不需要綁定娩井,直接使用即可暇屋。注解需要做調(diào)整。
例如:綁定URI和Fragment, 之前的綁定的注解是這樣的:
@Route(path = "/test/list")
class TestActivity : Activity {
...
}
因?yàn)閚avigation跳轉(zhuǎn)到Fragment并不需要Fragment的class洞辣,需要的是在nav graph聲明的id. 所以需要注解添加參數(shù)來綁定咐刨,可以改為:
@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針對的是動作扬霜,所以不應(yīng)該將包含action的注解聲明在Fragment上.可以聲明在方法上定鸟,如:
object NaviControllerHelper{
@RouteAction(path = "/test/list")
fun navigateToDetail(navController: NavController){
navController.navigate(R.id.action_to_detail)
}
}
然后通過注解獲取到方法名稱,反射調(diào)用這個方法即可著瓶。