Android路由框架-ARouter 使用

ARouter是什么

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

第一次接觸可能會比較模糊,我畫圖講講它到底是解決什么問題。
一般情況下我們的項(xiàng)目結(jié)構(gòu)應(yīng)該是這樣:


ARouter-1.jpg

但是我們的需求可能是APP、Module1、Module2、Module3、Module4之間能夠交互,至少是Activity能夠跳轉(zhuǎn)?;ハ嘁蕾嚸黠@是最差的方案。

ARouter的主要功能就是解決這個(gè)問題,使用ARouter的項(xiàng)目結(jié)構(gòu)會有一些改變,不過不大至少比互相引用強(qiáng)多了

ARouter-2.jpg

這個(gè)時(shí)候APP模塊有一個(gè)名稱:宿主APP,由于ARouter的限制,宿主APP必須對所有的Module依賴(不懂內(nèi)部實(shí)現(xiàn)所有我就不發(fā)表評論了)。
使用ARouter之后不僅APP可以跳轉(zhuǎn)到各個(gè)Module,而且APP與Module、Module與Module之間都是可以互相跳轉(zhuǎn)的。

ARouter能為我們做什么

雖然上面簡單的說了下ARouter可以實(shí)現(xiàn)模塊間交互,但是只是ARouter的功能之一。下面引用github上的介紹

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

ARouter配置

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'
    ...
}
// 舊版本gradle插件(< 2.2),可以使用apt插件,配置方法見文末'其他#4'
// Kotlin配置參考文末'其他#5'

每個(gè)module都需要這么配置,我使用的版本

implementation 'com.alibaba:arouter-api:1.4.0'
annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'

2、添加注解
注意不同模塊的一級路徑必須不同,建議使用module名。比如/navigation/testActivity

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

3、初始化SDK

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        if (isDebug()) {           // These two lines must be written before init, otherwise these configurations will be invalid in the init process
            ARouter.openLog();     // Print log
            ARouter.openDebug();   // Turn on debugging mode (If you are running in InstantRun mode, you must turn on debug mode! Online version needs to be closed, otherwise there is a security risk)
        }
        ARouter.init(this); // As early as possible, it is recommended to initialize in the Application

    }
}

5、添加混淆規(guī)則(如果使用了Proguard)

-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,需添加下面規(guī)則,保護(hù)接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider

# 如果使用了 單類注入,即不定義接口實(shí)現(xiàn) IProvider,需添加下面規(guī)則,保護(hù)實(shí)現(xiàn)
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider

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

apply plugin: 'com.alibaba.arouter'

buildscript {
    repositories {
        jcenter()
    }

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

我當(dāng)前使用classpath "com.alibaba:arouter-register:1.0.2"
7、使用 IDE 插件導(dǎo)航到目標(biāo)類 (可選) 非常好用,強(qiáng)烈推薦

在 Android Studio 插件市場中搜索 ARouter Helper, 或者直接下載文檔上方 最新版本 中列出的 arouter-idea-plugin zip 安裝包手動安裝,安裝后 插件無任何設(shè)置,可以在跳轉(zhuǎn)代碼的行首找到一個(gè)圖標(biāo)

arouter1.png

點(diǎn)擊該圖標(biāo),即可跳轉(zhuǎn)到標(biāo)識了代碼中路徑的目標(biāo)類

估計(jì)你已經(jīng)看出來了,上面少了4、 因?yàn)槲蚁氚阉鼏为?dú)說

ARouter使用

頁面跳轉(zhuǎn)有時(shí)候我們需要攜帶參數(shù),有時(shí)候還需要獲取頁面關(guān)閉返回的數(shù)據(jù),往下看
a.不攜帶數(shù)據(jù)直接跳轉(zhuǎn)
ARouter.getInstance().build("/matthew/activity").navigation();
b.攜帶數(shù)據(jù)

ARouter.getInstance().build("/test/1")
            .withLong("key1", 666L)
            .withString("key3", "888")
            .withObject("key4", new Test("Jack", "Rose"))
            .navigation();

注意: 攜帶非基礎(chǔ)類型數(shù)據(jù) 需要實(shí)現(xiàn)SerializationService并添加@Route注解

@Route(path = "/base/json")
public class JsonServiceImpl implements SerializationService {
    @Override
    public void init(Context context) {

    }

    @Override
    public <T> T json2Object(String text, Class<T> clazz) {
        return new Gson().fromJson(text, clazz);
    }

    @Override
    public String object2Json(Object instance) {
        return new Gson().toJson(instance);
    }

    @Override
    public <T> T parseObject(String input, Type clazz) {
        return new Gson().fromJson(input,clazz);
    }
}

c.需要獲取返回?cái)?shù)據(jù)
navigation函數(shù)的最后一個(gè)參數(shù)就是requestCode

 ARouter.getInstance().build("/matthew/activity")
                .withObject("json", new TestBean())
                .navigation(context, 100);
...
 @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        ...
    }

d、URL跳轉(zhuǎn)
需要先配置目標(biāo)Activity

 <activity android:name=".TestArouteActivity">

            <!-- Scheme -->
            <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>

然后

        //URL跳轉(zhuǎn)
        Uri url = Uri.parse("arouter://m.aliyun.com/matthew/activity");
        ARouter.getInstance().build(url)
                .withObject("json", new TestBean())
                .navigation();
//                .navigation(this, 100);
處理跳轉(zhuǎn)結(jié)果
// 設(shè)置跳轉(zhuǎn)回調(diào),可以獲得跳轉(zhuǎn)結(jié)果
                ARouter.getInstance().build("/test/activity4").navigation(this, new NavCallback() {
                    @Override
                    public void onFound(Postcard postcard) {
                        LogUtils.i("ARouter - 找到了");
                    }
 
                    @Override
                    public void onLost(Postcard postcard) {
                        LogUtils.i("ARouter - 找不到了");
                    }
 
                    @Override
                    public void onArrival(Postcard postcard) {
                        LogUtils.i("ARouter - 跳轉(zhuǎn)完成");
                    }
 
                    @Override
                    public void onInterrupt(Postcard postcard) {
                        LogUtils.i("ARouter - 被攔截了");
                    }
                });

如果是需要返回?cái)?shù)據(jù),
navigation(Activity mContext, int requestCode, NavigationCallback callback)

1597575437690.jpg

數(shù)據(jù)解析

上面講了攜帶數(shù)據(jù)跳轉(zhuǎn)。這里說說如何獲取攜帶過來的數(shù)據(jù);為每一個(gè)參數(shù)聲明一個(gè)字段,并使用 @Autowired標(biāo)注。并且(在使用參數(shù)之前)添加ARouter.getInstance().inject(this);對參數(shù)進(jìn)行自動注入。
比如上面我使用.withObject("json", new TestBean())攜帶了一個(gè)TestBean字段名是json
我們可以這樣獲取

 @Autowired
    TestBean json;

也可以這樣,使用別名

 @Autowired(name = "json")
    TestBean bean;

我的完整類

@Route(path = "/matthew/activity")
public class TestArouteActivity extends AppCompatActivity {
    // 支持解析自定義對象,URL中使用json傳遞
    @Autowired(name = "json")
    TestBean bean;

   // @Autowired
  //  TestBean json;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test_aroute);
        KLog.e(getLocalClassName());
        ARouter.getInstance().inject(this);


        ((TextView) findViewById(R.id.text_view)).setText(bean.toString());

    }

攔截器與extras

其實(shí)攔截器和extras是兩個(gè)分開的功能,我合到一起是因?yàn)橄胪祽小?br> 一個(gè)場景:我參與的電商項(xiàng)目中有多個(gè)界面都是必須登陸才能進(jìn)入的。是攔截器+extras可以非常完美的解決,不需要在每個(gè)界面去判斷是否登陸。如何做?
1、在需要驗(yàn)證的Activity上添加extras

@Route(path = "/matthew/activity",extras= Const.MUST_LOGIN)
public class TestArouteActivity extends AppCompatActivity {...}

2、實(shí)現(xiàn)IInterceptor接口,定義一個(gè)攔截器

@Interceptor(priority = 8, name = "TestInterceptor")
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
// callback.onInterrupt(new RuntimeException("我覺得有點(diǎn)異常"));      // 覺得有問題,中斷路由流程

    // 以上兩種至少需要調(diào)用其中一種,否則不會繼續(xù)路由
    }
        if (postcard.getExtra() == Const.MUST_LOGIN) {
            ARouter.getInstance().build("/base/loginactivity").navigation();
            callback.onInterrupt(new Throwable("需要登陸"));
        } else {
            callback.onContinue(postcard);
        }
    }

    @Override
    public void init(Context context) {
// 攔截器的初始化,會在sdk初始化的時(shí)候調(diào)用該方法,僅會調(diào)用一次
        KLog.i("TestInterceptor初始化");
    }
}

可以定義多個(gè)攔截器,設(shè)置不同的優(yōu)先級

降級策略

不明所以吧!讓我舉個(gè)??
在A頁面跳轉(zhuǎn)到B頁面,但是由于個(gè)別小伙伴的失誤B頁面被刪除處著路徑設(shè)置錯(cuò)誤了,反正就是無法正確跳轉(zhuǎn)B頁面了。ARouter的降級策略可以應(yīng)對這個(gè)場景。
1、定義頁面丟失時(shí)展示的默認(rèn)頁面

@Route(path = "/base/emptyactivity")
public class EmptyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_empty);
    }
}

2、定義全局降級策略

@Route(path = "/base/DegradeService")
public class DegradeServiceImpl implements DegradeService {

    @Override
    public void onLost(Context context, Postcard postcard) {
        ARouter.getInstance().build("/base/emptyactivity").navigation();
    }

    @Override
    public void init(Context context) {

    }
}

以下部分偷懶直接引用官例

通過依賴注入解耦:服務(wù)管理

暴露服務(wù)

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

// 實(shí)現(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ù),通過注解標(biāo)注字段,即可使用,無需主動獲取
    // Autowired注解中標(biāo)注name之后,將會使用byName的方式注入對應(yīng)的字段,不設(shè)置name屬性,會默認(rèn)使用byType的方式發(fā)現(xiàn)服務(wù)(當(dāng)同一接口有多個(gè)實(shí)現(xiàn)的時(shí)候,必須使用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");
    }
}

預(yù)處理服務(wù)

// 實(shí)現(xiàn) PretreatmentService 接口,并加上一個(gè)Path內(nèi)容任意的注解即可
@Route(path = "/xxx/xxx")
public class PretreatmentServiceImpl implements PretreatmentService {
    @Override
    public boolean onPretreatment(Context context, Postcard postcard) {
        // 跳轉(zhuǎn)前預(yù)處理,如果需要自行處理跳轉(zhuǎn),該方法返回 false 即可
    }

    @Override
    public void init(Context context) {

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

相關(guān)閱讀更多精彩內(nèi)容

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