Arouter原理及源碼分析

組件化開發(fā):

業(yè)務模塊之間相互獨立,互不依賴

startActivity的方式:

  1. 顯式
    • 方式1:
      intent.setClass(this,DemoActivity.class);
      強依賴,也就是需要import class,互不依賴的module則無法拿到class對象
    • 方式2:
      intent.setClassName(this,"com.example.helloworld.DemoActivity");
  2. 隱式
    • 方式3:
      manifest: <intent-filter > <action android:name="com.action.DemoActivity"/>
      intent.setAction("com.action.DemoActivity");

針對方式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/...文件夾路徑下可以找到:

image.png

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

image.png

關于字節(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)化初始化時的性能

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 組件化被越來越多的Android項目采用,而作為組件化的基礎——路由也是重中之重。本篇文章將詳細的分析阿里巴巴開源...
    胡奚冰閱讀 15,040評論 8 32
  • 一、ARouter 路由框架在大型項目中比較常見,特別是在項目中擁有多個 moudle 的時候。為了實現(xiàn)組件化,多...
    bug音音閱讀 628評論 1 1
  • 本文由玉剛說寫作平臺提供寫作贊助,版權歸玉剛說微信公眾號所有原作者:Xiasem版權聲明:未經(jīng)玉剛說許可,不得以任...
    xiasem閱讀 36,721評論 21 146
  • 越來越多的項目引入ARouter庫來配合組件化開發(fā),引入ARouter基本上成了項目標配,那么熟悉ARouter源...
    奔跑吧哈哈閱讀 1,537評論 0 10
  • 組件化開發(fā)的目的是為了解耦提高業(yè)務的復用,各業(yè)務之間相互獨立,如何跳轉頁面和數(shù)據(jù)傳輸就成為了首要解決的問題,阿里的...
    RNGyyds閱讀 817評論 0 2

友情鏈接更多精彩內容