組件化開發(fā):
業(yè)務模塊之間相互獨立,互不依賴
startActivity的方式:
-
顯式:
- 方式1:
intent.setClass(this,DemoActivity.class);
強依賴,也就是需要import class,互不依賴的module則無法拿到class對象 - 方式2:
intent.setClassName(this,"com.example.helloworld.DemoActivity");
- 方式1:
-
隱式:
- 方式3:
manifest: <intent-filter > <action android:name="com.action.DemoActivity"/>
intent.setAction("com.action.DemoActivity");
- 方式3:
針對方式2和3,我們可以在basemodule中通過靜態(tài)配置activity包名加類名或action進行夸module跳轉。
針對方式1,互不依賴的module間如何拿到class對象,
一種思路可以通過反射:
intent.setClass(this, Class.forName("com.example.helloworld.DemoActivity"));
然后也是用靜態(tài)配置方式管理。
另一種思路,就是利用
apt代替反射
在編譯期提前拿到你要反射的元素信息,這個元素就是注解聲明的元素,可以是類,方法,屬性,arouter就是使用這個思路,而其他采用apt的框架同理,包括butterknife,eventbus等
什么是 APT ?
APT 全稱 Annotation Processing Tool,翻譯過來即注解處理器。引用官方一段對 APT 的介紹:APT 是一種處理注釋的工具, 它對源代碼文件進行檢測找出其中的注解,并使用注解進行額外的處理。
如何使用:
http://www.itdecent.cn/p/b616a569c516
簡單流程:
新建Java lib,新建類繼承AbstractProcessor類并給類上面加上@AutoService進行注解處理器的自動注冊,重寫getSupportedAnnotationTypes返回你需要過濾的自定義的注解,重寫process來處理遍歷的所有注解,獲取注解的元素的所有信息并按自己需求創(chuàng)建java文件生成java源碼,對于java代碼的生成存在有多種方式,如字符串拼接,JavaPoet等。
以butterknife舉例:
除了引用源碼之外,還需要使用annotationProcessor 額外引用它的注解處理器
implementation 'com.jakewharton:butterknife:10.1.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
使用時:
@BindView(R.id.view)
TextView view;
然后編譯期通過它的注解處理器生成的新的java源碼如下:
public class TestActivity_ViewBinding {
public void bind(AnnotationTestActivity host) {
host.view= (android.widget.TextView)(((android.app.Activity)host).findViewById( 2131231102));
}
}
而運行期在onCreate中 ButterKnife.bind(this);
bind方法內部實現(xiàn)原理簡單來說就是反射生成TestActivity_ViewBinding對象,并調用它的bind方法,即可將findViewById的結果賦值給被注解的元素上。
再看arouter:
同樣需要引入依賴注解處理器
compile 'com.alibaba:arouter-api:1.4.0'
annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'
使用時:
@Route(path = "/test/activity4") //path應該放在basemodule中統(tǒng)一管理
public class SecondActivity
然后編譯期通過它的注解處理器生成的新的java源碼文件在build/generated/...文件夾路徑下可以找到:

public class ARouterGrouptest implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, SecondActivity .class,
"/test/activity4", "test", null, -1, -2147483648));
}
}
然后在Application 中初始化 ARouter.init(this); 這時開始加載由注解處理器生成的ARouterGrouptest類對象,并訪問loadInto方法進行路由表的收集工作,放入Map<String, Calss<>>中存儲在ARouter中。
navigation
ARouter.getInstance()
.build(path)//路徑
.withString("name ","value") //參數(shù)
.navigation();
此時就是查詢路由表,拿到class對象即可。
Autowired
而注解 @Autowired自動裝載功能,類似ButterKnife。
@Autowired
String name = "value";
在onCreate中調用ARouter.getInstance().inject(this);即可完成參數(shù)的裝載,此功能類似ButterKnife.bind(this);
Provider
ARouter的服務功能(Provider):就是module對外提供接口供其他module訪問的:
base模塊中聲明一個公共接口協(xié)議,任何模塊都可以訪問:
public interface AccountService extends IProvider {
Account getAccount();
}
在account中實現(xiàn)這個接口:
public class AccountServiceImpl implements AccountService {
@Override
public Account getAccount() {
return AccountManager.getInstance().getAccount();
}
}
與activity的路由功能一樣,也是通過注冊和收集對應的class對象,來完成跨module的訪問,只不過路由拿的是activity,而服務拿的是IProvider ,arouter內部拿到的就是AccountServiceImpl實例,但是會返給我們AccountService 也就是他的父類,客戶端并不關心實現(xiàn),所以拿到父類定義的接口協(xié)議就可以按需訪問了。
字節(jié)碼插樁
最后補充arouter運用了字節(jié)碼插樁能力,在哪用呢,回顧一下,arouter通過注解處理器生成的新的java源碼文件,然后在Application 中初始化ARouter.init(this);這時arouter需要查找每個module生成的新的java源碼文件,通過查找dex文件遍歷class查找這些類,找到后在new他們的實例,然后調用loadInto方法進行路由表的收集工作,這個環(huán)節(jié)是在Application中初始化的,故會影響啟動時長;
所以arouter提供了字節(jié)碼插樁功能,在編譯期檢索這些apt生成的java類,并把調用他們的代碼插入到arouter的初始化的方法中,和apt一樣,字節(jié)碼插樁也是在編譯期處理,不一樣的是apt是在java編譯class時,而字節(jié)碼插樁在class轉dex時

關于字節(jié)碼插樁自行搜索:asm+transform
arouter使用
根目錄的buildgradel引入:
classpath "com.alibaba:arouter-register:?"
module中buildgradle使用:
apply plugin: 'com.alibaba.arouter'
arouter插樁后的代碼位置和內容如下:(如果不用插件,則loadRouterMap方法就是空的,那么就在運行期初始化時遍歷dex查找)
private static void loadRouterMap() {
register("com.alibaba.android.arouter.routes.ARouterRootmodulea");
register("com.alibaba.android.arouter.routes.ARouterRootmoduleb");
register("com.alibaba.android.arouter.routes.ARouterProvidersmodulea");
}
這里的register方法實際就是:
Class<?> clazz = Class.forName(className);
Object obj = clazz.getConstructor().newInstance();
而后再調用obj.loadInto(Map);//Map就是路由表
總結:
1.startActivity的三種方式
2.apt代替反射的思路解決問題
3.butterknife原理
4.ARouter初始化做了什么
5.ARouter參數(shù)自動裝載原理
6.ARouter的provider使用和原理
7.asm+transform字節(jié)碼插樁技術
8.ARouter利用字節(jié)碼插樁來優(yōu)化初始化時的性能