ARouter 路由 組件 跳轉 MD


Markdown版本筆記 我的GitHub首頁 我的博客 我的微信 我的郵箱
MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

ARouter 路由 組件 跳轉 MD


目錄

簡介

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

ARouter官網
Demo下載

模塊 arouter-api arouter-compiler arouter-register arouter-idea-plugin
2018-12-15 最新版本 Download Download Download as plugin

支持的功能

  • 支持直接解析標准URL進行跳轉,並自動注入參數到目標頁面中
  • 支持多模塊工程使用
  • 支持添加多個攔截器,自定義攔截順序
  • 支持依賴注入,可單獨作為依賴注入框架使用
  • 支持InstantRun
  • 支持MultiDex
  • 映射關系按組分類、多級管理,按需初始化
  • 支持用戶指定全局降級與局部降級策略
  • 頁面、攔截器、服務等組件均自動注冊到框架
  • 支持多種方式配置轉場動畫
  • 支持獲取Fragment
  • 完全支持Kotlin以及混編
  • 支持第三方 App 加固(使用 arouter-register 實現自動注冊)
  • 支持生成路由文檔
  • 提供IDE 插件便捷的關聯路徑和目標類

典型應用

  • 外部URL映射到內部頁面,以及參數傳遞與解析
  • 跨模塊頁面跳轉,模塊間解耦
  • 攔截跳轉過程,處理登陸、埋點等邏輯
  • 跨模塊API調用,通過控制反轉來做組件解耦

簡單使用

1、添加依賴和配置

android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
    api 'com.alibaba:arouter-api:x.x.x' // 需要注意的是api
    annotationProcessor 'com.alibaba:arouter-compiler:x.x.x' // 要與compiler匹配使用
}

2、添加注解

// 在支持路由的頁面上添加注解(必選),這里的路徑需要注意的是至少需要有兩級,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity { ... }

3、初始化SDK

if (isDebug()) {
    ARouter.openLog();     // 打印日志,這兩行必須寫在init之前,否則這些配置在init過程中將無效
    ARouter.openDebug();   // 開啟調試模式(如果在InstantRun模式下運行,必須開啟調試模式!線上版本需要關閉,否則有安全風險)
}
ARouter.init(mApplication);

4、發起路由操作

ARouter.getInstance().build("/test/activity").navigation(); // 應用內簡單的跳轉
ARouter.getInstance().build("/test/1")
            .withLong("key1", 666L) // 跳轉並攜帶參數
            .withString("key3", "888")
            .withObject("key4", new Test("Jack", "Rose"))
            .navigation();

5、添加混淆規則

-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}

# 如果使用了 byType 的方式獲取 Service,需添加下面規則,保護接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider

# 如果使用了 單類注入,即不定義接口實現 IProvider,需添加下面規則,保護實現
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider

6、使用 Gradle 插件實現路由表的自動加載(可選)

apply plugin: 'com.alibaba.arouter'

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath "com.alibaba:arouter-register:?"
    }
}

可選使用,通過 ARouter 提供的注冊插件進行路由表的自動加載(power by AutoRegister),默認通過掃描 dex 的方式進行加載,通過 gradle 插件進行自動注冊可以縮短初始化時間,解決應用加固導致無法直接訪問 dex 文件,初始化失敗的問題。需要注意的是,該插件必須搭配 api 1.3.0 以上版本使用!

7、使用 IDE 插件導航到目標類
在 Android Studio 插件市場中搜索 ARouter Helper, 或者直接下載文檔上方 最新版本 中列出的 arouter-idea-plugin zip 安裝包手動安裝,安裝后,插件無任何設置,可以在跳轉代碼的行首找到一個圖標,點擊該圖標,即可跳轉到標識了代碼中路徑的目標類。

進階使用

1、通過URL跳轉
新建一個Activity用於監聽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();
    }
}

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>

2、解析URL中的參數
URL中不能傳遞Parcelable類型數據,通過ARouter api可以傳遞Parcelable對象

@Route(path = "/test/activity")
public class Test1Activity extends Activity {
    @Autowired public String name; // 為每一個參數聲明一個字段,並使用 @Autowired 標注
    @Autowired int age;
    @Autowired(name = "girl") boolean boy; // 通過name來映射URL中的不同參數
    @Autowired TestObj obj; // 支持解析自定義對象,URL中使用json傳遞

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ARouter.getInstance().inject(this); //注入
        Log.d("param", name + age + boy); // ARouter會自動對字段進行賦值,無需主動獲取
    }
}

如果需要傳遞自定義對象,新建一個類(並非自定義對象類),然后實現 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); //自行選擇反序列化方式,比如Gson、fastjson
    }

    @Override
    public String object2Json(Object instance) {
        return JSON.toJSONString(instance); //自行選擇序列化方式,比如Gson、fastjson
    }
}

3、聲明攔截器
攔截跳轉過程,面向切面編程
比較經典的應用就是在跳轉過程中處理登陸事件,這樣就不需要在目標頁重復做登陸檢查
攔截器會在跳轉之間執行,多個攔截器會按優先級順序依次執行。

@Interceptor(priority = 8, name = "測試用攔截器")
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        //...條件判斷
        callback.onContinue(postcard);  // 處理完成,交還控制權
        // callback.onInterrupt(new RuntimeException("我覺得有點異常")); // 覺得有問題,中斷路由流程
        // 以上兩種至少需要調用其中一種,否則不會繼續路由
    }

    @Override
    public void init(Context context) {
        // 攔截器的初始化,會在sdk初始化的時候調用該方法,僅會調用一次
    }
}

4、處理跳轉結果
使用兩個參數的navigation方法,可以獲取單次跳轉的結果。

ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() {
    @Override
    public void onFound(Postcard postcard) {
        //...
    }

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

5、自定義全局降級策略
實現DegradeService接口,並加上一個Path內容任意的注解即可

@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、為目標頁面聲明更多信息
我們經常需要在目標頁面中配置一些屬性,比方說"是否需要登陸"之類的,可以通過 Route 注解中的 extras 屬性進行擴展。
這個屬性是一個 int 值,也就是32位,可以配置32個開關。
剩下的可以自行發揮,通過字節操作可以標識32個開關,通過開關標記目標頁面的一些屬性,在攔截器中可以拿到這個標記進行業務邏輯判斷

@Route(path = "/test/activity", extras = Consts.XXXX)

7、通過依賴注入解耦
服務管理(一) 暴露服務

public interface HelloService extends IProvider {
    String sayHello(String name); // 聲明接口,其他組件通過接口來調用服務
}

實現接口

@Route(path = "/yourservicegroupname/hello", name = "測試服務")
public class HelloServiceImpl implements HelloService {

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

    @Override
    public void init(Context context) {

    }
}

服務管理(二) 發現服務

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、(推薦)使用依賴注入的方式發現服務,通過注解標注字段即可使用,無需主動獲取
        // Autowired注解中標注name之后,將會使用byName的方式注入對應的字段,不設置name屬性,會默認使用byType的方式發現服務(當同一接口有多個實現的時候,必須使用byName的方式發現服務)
        helloService.sayHello("Vergil");
        helloService2.sayHello("Vergil");

        // 2、使用依賴查找的方式發現服務,主動去發現服務並使用,下面兩種方式分別是byName和byType
        helloService3 = ARouter.getInstance().navigation(HelloService.class);
        helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
        helloService3.sayHello("Vergil");
        helloService4.sayHello("Vergil");
    }
}

更多功能

1、初始化中的其他設置

ARouter.openLog(); // 開啟日志
ARouter.openDebug(); // 使用InstantRun的時候,需要打開該開關,上線之后關閉,否則有安全風險
ARouter.printStackTrace(); // 打印日志的時候打印線程堆棧

2、詳細的API說明

ARouter.getInstance().build("/home/main").navigation(); // 構建標准的路由請求
ARouter.getInstance().build("/home/main", "ap").navigation(); // 構建標准的路由請求,並指定分組
ARouter.getInstance().build(uri).navigation(); // 構建標准的路由請求,通過Uri直接解析
ARouter.getInstance().build("/home/main", "ap").navigation(this, 5); // startActivityForResult 形式跳轉
ARouter.getInstance().build("/home/main").with(params).navigation(); // 直接傳遞Bundle
ARouter.getInstance().build("/home/main").withFlags().navigation(); // 指定Flag
ARouter.getInstance().withObject("key", new TestObj("Jack", "Rose")).navigation(); // 對象傳遞
ARouter.getInstance().build("/home/main").getExtra(); // 覺得接口不夠多,可以直接拿出Bundle賦值
ARouter.getInstance().build("/home/main").greenChannel().navigation();// 使用綠色通道(跳過所有的攔截器)
ARouter.setLogger();// 使用自己的日志工具打印日志
ARouter.setExecutor();// 使用自己提供的線程池

Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation(); //獲取Fragment

ARouter.getInstance().build("/test/activity2")
    .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom).navigation(this); //轉場動畫
ARouter.getInstance().build("/test/activity2")
    .withOptionsCompat(compat).navigation(this); // 轉場動畫(API16+)

3、獲取原始的URI

String uriStr = getIntent().getStringExtra(ARouter.RAW_URI);

4、重寫跳轉URL
實現 PathReplaceService 接口,並加上一個 Path 內容任意的注解即可

@Route(path = "/xxx/xxx") // 必須標明注解
public class PathReplaceServiceImpl implements PathReplaceService {
    /**
    * For normal path.
    *
    * @param path raw path
    */
    String forString(String path) {
        return path;    // 按照一定的規則處理之后返回處理后的結果
    }

    /**
    * For uri type.
    *
    * @param uri raw uri
    */
    Uri forUri(Uri uri) {
        return url; // 按照一定的規則處理之后返回處理后的結果
    }
}

5、生成路由文檔
生成的文檔路徑:build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
            }
        }
    }
}

其他

1、路由中的分組概念

  • SDK中針對所有的路徑進行分組,只有在分組中的某一個路徑第一次被訪問的時候,該分組才會被初始化
  • 可以通過 @Route 注解主動指定分組,否則使用路徑中第一段字符串/*/作為分組
  • 注意:一旦主動指定分組之后,應用內路由需要使用 ARouter.getInstance().build(path, group) 進行跳轉,手動指定分組,否則無法找到
@Route(path = "/test/1", group = "app")

2、攔截器和服務的異同

  • 攔截器和服務所需要實現的接口不同,但是結構類似,都存在 init(Context) 方法,但是兩者的調用時機不同
  • 攔截器因為其特殊性,會被任何一次路由所觸發,攔截器會在ARouter初始化的時候異步初始化,如果第一次路由的時候攔截器還沒有初始化結束,路由會等待,直到初始化完成。
  • 服務沒有該限制,某一服務可能在App整個生命周期中都不會用到,所以服務只有被調用的時候才會觸發初始化操作

3、Kotlin項目中的配置方式
可以參考 module-kotlin 模塊中的寫法

apply plugin: 'kotlin-kapt'

kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
}

dependencies {
    compile 'com.alibaba:arouter-api:x.x.x'
    kapt 'com.alibaba:arouter-compiler:x.x.x'
}

Q&A

1、"W/ARouter::: ARouter::No postcard![ ]"

2、"W/ARouter::: ARouter::There is no route match the path [/xxx/xxx], in group [xxx][ ]"

3、開啟InstantRun之后無法跳轉(高版本Gradle插件下無法跳轉)?
因為開啟InstantRun之后,很多類文件不會放在原本的dex中,需要單獨去加載,ARouter默認不會去加載這些文件,因為安全原因,只有在開啟了openDebug之后,ARouter才回去加載InstantRun產生的文件,所以在以上的情況下,需要在init之前調用openDebug。

4、TransformException:java.util.zip.ZipException: duplicate entry ....
ARouter有按組加載的機制,ARouter允許一個module中存在多個分組,但是不允許多個module中存在相同的分組,會導致映射文件沖突。

5、Kotlin類中的字段無法注入如何解決?
首先,Kotlin中的字段是可以自動注入的,但是注入代碼為了減少反射,使用的字段賦值的方式來注入的,Kotlin默認會生成set/get方法,並把屬性設置為private,所以只要保證Kotlin中字段可見性不是private即可,簡單解決可以在字段上添加 @JvmField

6、通過URL跳轉之后,在intent中拿不到參數如何解決?
需要注意的是,如果不使用自動注入,那么可以不寫 ARouter.getInstance().inject(this),但是需要取值的字段仍然需要標上 @Autowired 注解,因為只有標上注解之后,ARouter才能知道以哪一種數據類型提取URL中的參數並放入Intent中,這樣您才能在intent中獲取到對應的參數。

7、新增頁面之后,無法跳轉?
ARouter加載Dex中的映射文件會有一定耗時,所以ARouter會緩存映射文件,直到新版本升級(版本號或者versionCode變化),而如果是開發版本,ARouter 每次啟動都會重新加載映射文件,開發階段一定要打開 Debug 功能。

2018-12-15


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM