Android 路由框架之阿里ARouter詳解

一、起源

  • 由于業(yè)務(wù)的增長提高了項目的復(fù)雜性,為了更好的適應(yīng)團隊開發(fā),提高開發(fā)效率,實行組件化乃大勢所趨。
  • 組件化可以讓我們程序更容易的擴展、更方便的維護,更快捷的同步開發(fā)與更簡單的單元調(diào)試。
  • 但是由于各個組件在不同的model甚至在不同的項目中,這樣他們之間的通信就成了問題,而 ARouter的出現(xiàn)就是讓組件間、模塊間是實現(xiàn)完全的獨立。
  • 并且后臺還可以通過路由的機制控制Android界面調(diào)用實現(xiàn)遠程控制和高度解耦。

二、是什么?

一個用于幫助 Android App 進行組件化改造的框架 —— 支持模塊間的路由、通信、解耦


三、功能介紹

  1. 支持直接解析標準URL進行跳轉(zhuǎn),并自動注入?yún)?shù)到目標頁面中
  2. 支持多模塊工程使用
  3. 支持添加多個攔截器,自定義攔截順序
  4. 支持依賴注入,可單獨作為依賴注入框架使用
  5. 支持InstantRun
  6. 支持MultiDex(Google方案)
  7. 映射關(guān)系按組分類、多級管理,按需初始化
  8. 支持用戶指定全局降級與局部降級策略
  9. 頁面、攔截器、服務(wù)等組件均自動注冊到框架
  10. 支持多種方式配置轉(zhuǎn)場動畫
  11. 支持獲取Fragment
  12. 完全支持Kotlin以及混編
  13. 支持第三方 App 加固(使用 arouter-register 實現(xiàn)自動注冊)
  14. 支持生成路由文檔
  15. 提供 IDE 插件便捷的關(guān)聯(lián)路徑和目標類

四、原理解析

1、調(diào)用關(guān)系
  • 兩個界面之間的跳轉(zhuǎn),調(diào)用關(guān)系如下:


    界面調(diào)用用例圖
  1. 注冊:B界面先在ARouter里注冊一個唯一標識;
  2. 查詢:A界面調(diào)用時,ARouter會先查詢B界面是否已注冊;
  3. 整合:ARouter查詢到B界面后,會將A界面?zhèn)魅胄畔⑦M行整合;
  4. 調(diào)用:整合好信息后,會調(diào)起B(yǎng)界面并將傳入信息傳遞到B界面。
2、流程解析
  • 界面跳轉(zhuǎn),arouter做了以下工作:
ARouter流程圖

五、原理解析

  1. 從外部URL映射到內(nèi)部頁面,以及參數(shù)傳遞與解析
  2. 跨模塊頁面跳轉(zhuǎn),模塊間解耦
  3. 攔截跳轉(zhuǎn)過程,處理登陸、埋點等邏輯
  4. 跨模塊API調(diào)用,通過控制反轉(zhuǎn)來做組件解耦

六、基礎(chǔ)功能

1.添加依賴和配置
android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
    // 替換成最新版本, 需要注意的是api
    // 要與compiler匹配使用,均使用最新版可以保證兼容
    compile 'com.alibaba:arouter-api:x.x.x'
    annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
}
2.初始化SDK
if (isDebug()) {           // 這兩行必須寫在init之前,否則這些配置在init過程中將無效
    ARouter.openLog();     // 打印日志
    ARouter.openDebug();   // 開啟調(diào)試模式(如果在InstantRun模式下運行,必須開啟調(diào)試模式!線上版本需要關(guān)閉,否則有安全風(fēng)險)
}
ARouter.init(mApplication); // 盡可能早,推薦在Application中初始化
3.簡單跳轉(zhuǎn)
  • 目標界面
// 在支持路由的頁面上添加注解(必選)
// 這里的路徑需要注意的是至少需要有兩級,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
    ...
}
  • 跳轉(zhuǎn)操作
// 1. 應(yīng)用內(nèi)簡單的跳轉(zhuǎn)
ARouter.getInstance().build("/test/activity").navigation();
//等同于startActivityForResult
ARouter.getInstance().build("/test/activity").navigation(getActivity(),REQUEST_CODE);
4.帶參跳轉(zhuǎn)
  • 目標界面
@Route(path = "/test/1")
public class YourActivity extend Activity {
    //獲取數(shù)據(jù)三種方式
    //1.通過Autowired注解表明key   &  需要在onCreate中調(diào)用ARouter.getInstance().inject(this);配合使用
     // 通過name來映射URL中的不同參數(shù)
    @Autowired(name = "key1")
    public long data;
    
    //2.通過Autowired注解 & 將key1作為屬性的名稱   &  需要在onCreate中調(diào)用ARouter.getInstance().inject(this);配合使用
    @Autowired()
    public long key2;
    
    //3.通過Bundle獲取
    getIntent().getExtras().getLong("key3")
}
  • 跳轉(zhuǎn)操作
// 2. 跳轉(zhuǎn)并攜帶參數(shù)
ARouter.getInstance().build("/test/1")
            .withLong("key1", 666L)
            .withString("key2", "888")
            .withObject("key3", new Test("Jack", "Rose"))
            .navigation();

七、進階用法

1.通過URL跳轉(zhuǎn)

方法一

  • AndroidManifest.xml
<activity android:name=".activity.SchameFilterActivity">
    <!-- Schame -->
    <intent-filter>
        <data
        android:host="m.aliyun.com"
        android:scheme="arouter"/>

        <action android:name="android.intent.action.VIEW"/>

        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
    </intent-filter>
</activity>
  • 跳轉(zhuǎn)操作
// 新建一個Activity用于監(jiān)聽Schame事件,之后直接把url傳遞給ARouter即可
public class SchameFilterActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    Uri uri = getIntent().getData();
    ARouter.getInstance().build(uri).navigation();
    finish();
    }
}

方法二

  • 跳轉(zhuǎn)操作
 Uri testUriMix = Uri.parse("arouter://m.aliyun.com/test/activity2");
 ARouter.getInstance().build(testUriMix)
                .withString("key1", "value1")
                .navigation();
  • 目標界面
@Route(path = "/test/activity2")
public class Test2Activity extends AppCompatActivity {
}
2.解析參數(shù)(傳遞Object)
  • 定義解析Obeject的SerializationService
// 如果需要傳遞自定義對象,新建一個類(并非自定義對象類),然后實現(xiàn) SerializationService,并使用@Route注解標注(方便用戶自行選擇序列化方式),例如:
@Route(path = "/yourservicegroupname/json")
public class JsonServiceImpl implements SerializationService {
    @Override
    public void init(Context context) {

    }

    @Override
    public <T> T json2Object(String text, Class<T> clazz) {
        return JSON.parseObject(text, clazz);
    }

    @Override
    public String object2Json(Object instance) {
        return JSON.toJSONString(instance);
    }
}
  • 跳轉(zhuǎn)操作
ARouter.getInstance().build("/test/1")
            .withObejct("obj", new TestObj ("Jack", "Rose"))
            .navigation();
  • 目標界面
// 為每一個參數(shù)聲明一個字段,并使用 @Autowired 標注
// URL中不能傳遞Parcelable類型數(shù)據(jù),通過ARouter api可以傳遞Parcelable對象
@Route(path = "/test/activity")
public class Test1Activity extends Activity {

     // 支持解析自定義對象,URL中使用json傳遞
    @Autowired
    TestObj obj;      
    
    // 使用 withObject 傳遞 List 和 Map 的實現(xiàn)了
    // Serializable 接口的實現(xiàn)類(ArrayList/HashMap)
    // 的時候,接收該對象的地方不能標注具體的實現(xiàn)類類型
    // 應(yīng)僅標注為 List 或 Map,否則會影響序列化中類型
    // 的判斷, 其他類似情況需要同樣處理        
    @Autowired
    List<TestObj> list;
    @Autowired
    Map<String, List<TestObj>> map;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ARouter.getInstance().inject(this);
     SerializationService serializationService = ARouter.getInstance().navigation(SerializationService.class);
    serializationService.init(this);
    obj = serializationService.parseObject(getIntent().getStringExtra("key4"), User.class);
     }
}

3.聲明攔截器(攔截跳轉(zhuǎn)過程,面向切面編程)
// 比較經(jīng)典的應(yīng)用就是在跳轉(zhuǎn)過程中處理登陸事件,這樣就不需要在目標頁重復(fù)做登陸檢查
// 攔截器會在跳轉(zhuǎn)之間執(zhí)行,多個攔截器會按優(yōu)先級順序依次執(zhí)行
@Interceptor(priority = 8, name = "測試用攔截器")
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
    ...
    callback.onContinue(postcard);  // 處理完成,交還控制權(quán)
    // callback.onInterrupt(new RuntimeException("我覺得有點異常"));      // 覺得有問題,中斷路由流程

    // 以上兩種至少需要調(diào)用其中一種,否則不會繼續(xù)路由
    }

    @Override
    public void init(Context context) {
    // 攔截器的初始化,會在sdk初始化的時候調(diào)用該方法,僅會調(diào)用一次
    }
}
4.處理跳轉(zhuǎn)結(jié)果
// 使用兩個參數(shù)的navigation方法,可以獲取單次跳轉(zhuǎn)的結(jié)果
ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() {
    @Override
    public void onFound(Postcard postcard) {
    ...
    }

    @Override
    public void onLost(Postcard postcard) {
    ...
    }
});.

ARouter.getInstance().build("/test/activity2")
                .navigation(this, new NavCallback() {
                    @Override
                    public void onArrival(Postcard postcard) {
                    }
                    @Override
                    public void onInterrupt(Postcard postcard) {
                        Log.d("ARouter", "被攔截了");
                    }
                });
5.自定義全局降級策略
// 實現(xiàn)DegradeService接口,并加上一個Path內(nèi)容任意的注解即可
        @Route(path = "/xxx/xxx")
        public class DegradeServiceImpl implements DegradeService {
            @Override
            public void onLost(Context context, Postcard postcard) {
                // do something.
            }

            @Override
            public void init(Context context) {

            }
        }
6.為目標頁面聲明更多信息
// 我們經(jīng)常需要在目標頁面中配置一些屬性,比方說"是否需要登陸"之類的
// 可以通過 Route 注解中的 extras 屬性進行擴展,這個屬性是一個 int值,換句話說,單個int有4字節(jié),也就是32位,可以配置32個開關(guān)
// 剩下的可以自行發(fā)揮,通過字節(jié)操作可以標識32個開關(guān),通過開關(guān)標記目標頁面的一些屬性,在攔截器中可以拿到這個標記進行業(yè)務(wù)邏輯判斷
@Route(path = "/test/activity", extras = Consts.XXXX)
7.通過依賴注入解耦:服務(wù)管理
  • 暴露服務(wù)
// 聲明接口,其他組件通過接口來調(diào)用服務(wù)
public interface HelloService extends IProvider {
    String sayHello(String name);
}

// 實現(xiàn)接口
@Route(path = "/yourservicegroupname/hello", name = "測試服務(wù)")
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
    return "hello, " + name;
    }

    @Override
    public void init(Context context) {

    }
}
  • 發(fā)現(xiàn)服務(wù)
public class Test {
    @Autowired
    HelloService helloService;

    @Autowired(name = "/yourservicegroupname/hello")
    HelloService helloService2;

    HelloService helloService3;

    HelloService helloService4;

    public Test() {
    ARouter.getInstance().inject(this);
    }

    public void testService() {
    // 1. (推薦)使用依賴注入的方式發(fā)現(xiàn)服務(wù),通過注解標注字段,即可使用,無需主動獲取
    // Autowired注解中標注name之后,將會使用byName的方式注入對應(yīng)的字段,不設(shè)置name屬性,會默認使用byType的方式發(fā)現(xiàn)服務(wù)(當(dāng)同一接口有多個實現(xiàn)的時候,必須使用byName的方式發(fā)現(xiàn)服務(wù))
    helloService.sayHello("Vergil");
    helloService2.sayHello("Vergil");

    // 2. 使用依賴查找的方式發(fā)現(xiàn)服務(wù),主動去發(fā)現(xiàn)服務(wù)并使用,下面兩種方式分別是byName和byType
    helloService3 = ARouter.getInstance().navigation(HelloService.class);
    helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
    helloService3.sayHello("Vergil");
    helloService4.sayHello("Vergil");
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容