android router不起作用,给 Arouter 优化的一些小建议
Arouter 應(yīng)該算是 Android 國(guó)民級(jí)框架了,在自己做組件化框架的時(shí)候,也是參考了不少 Arouter 的設(shè)計(jì),在閱讀源碼中,覺得有的點(diǎn)是可以優(yōu)化的,所以就有了今天的文章。
1、混淆優(yōu)化
在 README 中可以看到,如果是開啟混淆的話,需要添加如上的規(guī)則。主要原因是 gradle-plugin 在收集 apt 生成類的時(shí)候,注入到 LogisticsCenter 的是 className,然后運(yùn)行時(shí)通過(guò)反射的方式來(lái)初始化類。
Arouter 代碼
在 Arouter-gradle-plugin 模塊的 RouteMethodVisitor 類中會(huì)對(duì)所有收集到的類,注入到 LogisticsCenter 的 loadRouterMap 方法中:
@Override
void visitInsn(int opcode) {
//generate code before return
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
extension.classList.each { name ->
name = name.replaceAll("/", ".")
mv.visitLdcInsn(name)//類名
// generate invoke register method into LogisticsCenter.loadRouterMap()
mv.visitMethodInsn(Opcodes.INVOKESTATIC
, ScanSetting.GENERATE_TO_CLASS_NAME
, ScanSetting.REGISTER_METHOD_NAME
, "(Ljava/lang/String;)V"
, false)
}
}
super.visitInsn(opcode)
}
復(fù)制代碼
注入結(jié)果如下:
private static void loadRouterMap() {
registerByPlugin = false;
// 就大致舉一個(gè)例子
register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");
...
}
復(fù)制代碼
最后會(huì)通過(guò) register 方法來(lái)完成反射初始化:
private static void register(String className) {
if (!TextUtils.isEmpty(className)) {
try {
Class> clazz = Class.forName(className);
Object obj = clazz.getConstructor().newInstance();
if (obj instanceof IRouteRoot) {
registerRouteRoot((IRouteRoot) obj);
}
...
}
}
復(fù)制代碼
優(yōu)化建議
將 String 類型的 className 替換成 Class
我們可以從 gradle-plugin 的 RouteMethodVisitor 入手,來(lái)更改一下 RouteMethodVisitor:
@Override
void visitInsn(int opcode) {
//generate code before return
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
extension.classList.each { name ->
// 獲取 Class
mv.visitLdcInsn(Type.getType("L" + name + ";"))
// generate invoke register method into LogisticsCenter.loadRouterMap()
mv.visitMethodInsn(Opcodes.INVOKESTATIC
, ScanSetting.GENERATE_TO_CLASS_NAME
, ScanSetting.REGISTER_METHOD_NAME
, "(Ljava/lang/Class;)V"
, false)
}
}
super.visitInsn(opcode)
}
復(fù)制代碼
注入的結(jié)果如下:
private static void loadRouterMap() {
registerByPlugin = false;
// 就大致舉一個(gè)例子
register(com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava.class);
...
}
復(fù)制代碼
還要再更改一下 register 方法,更改如下:
private static void register(Class clazz){
if (clazz!=null) {
try {
Object obj = clazz.getConstructor().newInstance();
if (obj instanceof IRouteRoot) {
registerRouteRoot((IRouteRoot) obj);
...
復(fù)制代碼
2、反射優(yōu)化
雖然混淆的問(wèn)題可以通過(guò) Class 來(lái)解決,但仍然無(wú)法解決反射帶來(lái)的性能問(wèn)題。我們?cè)?ASM 插樁的時(shí)候是可以拿到類名的,那我們能不能通過(guò) new 類()? 的方式來(lái)初始化類呢?我們可以繼續(xù)按照上面的思路來(lái)解決掉反射的問(wèn)題。
繼續(xù)來(lái)看 RouteMethodVisitor ,來(lái)更改一下 RouteMethodVisitor:
@Override
void visitInsn(int opcode) {
//generate code before return
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
extension.classList.each { name ->
mv.visitVarInsn(Opcodes.ALOAD, 0)
//用無(wú)參構(gòu)造方法創(chuàng)建實(shí)例
mv.visitTypeInsn(Opcodes.NEW, name)
mv.visitInsn(Opcodes.DUP)
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, name, "", "()V", false)
String interfaceName = ""
String insertMethod = ""
// ①、
if (name.contains("ARouter\$\$Root\$\$")) {
interfaceName = "com/alibaba/android/arouter/facade/template/IRouteRoot"
insertMethod = "registerRouteRoot"
} else if (name.contains("ARouter\$\$Interceptors\$\$")) {
interfaceName = "com/alibaba/android/arouter/facade/template/IInterceptorGroup"
insertMethod = "registerInterceptor"
} else if (name.contains("ARouter\$\$Providers\$\$")) {
interfaceName = "com/alibaba/android/arouter/facade/template/IProviderGroup"
insertMethod = "registerProvider"
}
if (!TextUtils.isEmpty(interfaceName) && !TextUtils.isEmpty(insertMethod)) {
mv.visitMethodInsn(Opcodes.INVOKESTATIC
, ScanSetting.GENERATE_TO_CLASS_NAME
, insertMethod
, "(L${interfaceName};)V"
, false)
}
}
}
super.visitInsn(opcode)
}
復(fù)制代碼
①:根據(jù)類名來(lái)判斷當(dāng)前是 Root、Interceptors 還是 Providers,因?yàn)?Arouter 在 apt 生成類的時(shí)候會(huì)對(duì)有一個(gè)類命名規(guī)則,我們只要根據(jù)這個(gè)規(guī)則,即可找到該類實(shí)現(xiàn)的是哪個(gè)接口。這個(gè)地方還需要有一個(gè) insertMethod,因?yàn)槲覀兪?new 類()?的方式,不能像 register 方法那樣通過(guò) Class 來(lái)做統(tǒng)一處理,我們需要明確的類型來(lái)注入,所以,這個(gè)地方用的是接口。
注入效果如下:
private static void loadRouterMap() {
registerByPlugin = false;
registerRouteRoot(new com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava());
registerInterceptor(new com.alibaba.android.arouter.routes.ARouter$$Interceptors$$app());
registerProvider(new com.alibaba.android.arouter.routes.ARouter$$Providers$$app());
...
}
復(fù)制代碼
3、一些有趣的 issue
1、issue 776 : kt 中注入 Autowired 無(wú)效
Arouter Autowired注入的時(shí)候在存在 kotlin-java 兼容性問(wèn)題
針對(duì)基本數(shù)據(jù)類型的傳遞
var showBadge: Boolean? = null 注入失敗
var showBadge: Boolean? = false 注入成功
原因:
在 arouter 生成代碼中,獲取注入變量通過(guò) substitute.showBadge = substitute.getIntent().getBooleanExtra("showBadge", substitute.showBadge)?,對(duì)于基本數(shù)據(jù)類型的獲取接口都帶有默認(rèn)參數(shù),如 getBooleanExtra(String name, boolean defaultValue),?問(wèn)題出在默認(rèn)參數(shù) 對(duì)應(yīng) java boolean 類型沒有 null 的概念 ,報(bào)錯(cuò) can't unbox a null value
2、issue 818 : androidx 項(xiàng)目中使用 Arouter 的困惑
這個(gè)是我自己提的 🤣 ,主要是在看 arouter-compiler 源碼的時(shí)候,Arouter 寫死的是 support.fragment 的路徑,為什么 types.isSubtype 的判斷對(duì)于 androidx.fragment 卻可以通過(guò)的問(wèn)題,Jetifier 其實(shí)是不會(huì)將常量的 support 替換成 androidx 的,因?yàn)槲覍懥艘粋€(gè) support 轉(zhuǎn) androidx 的 demo 測(cè)了一下
... 待補(bǔ)充吧
關(guān)于找一找教程網(wǎng)
本站文章僅代表作者觀點(diǎn),不代表本站立場(chǎng),所有文章非營(yíng)利性免費(fèi)分享。
本站提供了軟件編程、網(wǎng)站開發(fā)技術(shù)、服務(wù)器運(yùn)維、人工智能等等IT技術(shù)文章,希望廣大程序員努力學(xué)習(xí),讓我們用科技改變世界。
[給 Arouter 優(yōu)化的一些小建議]http://www.zyiz.net/tech/detail-143213.html
總結(jié)
以上是生活随笔為你收集整理的android router不起作用,给 Arouter 优化的一些小建议的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: android 内嵌地图,Android
- 下一篇: android c 电话联系人,Andr