Android路由框架Router分析

Android路由框架Router

什么是路由?說簡單點就是映射頁面跳轉(zhuǎn)關(guān)系的哑舒,當然它也包含跳轉(zhuǎn)相關(guān)的一切功能琼稻。

路由框架的意義

Android系統(tǒng)已經(jīng)給我們提供了api來做頁面跳轉(zhuǎn),比如startActivity提鸟,為什么還需要路由框架呢?我們來簡單分析下路由框架存在的意義:

  1. 在一些復雜的業(yè)務(wù)場景下仅淑,靈活性比較強称勋,很多功能都是動態(tài)配置的,比如下發(fā)一個活動頁面涯竟,我們事先并不知道具體的目標頁面赡鲜,但如果事先做了約定空厌,提前做好頁面映射,便可以自由配置银酬。
  2. 隨著業(yè)務(wù)量的增長嘲更,客戶端必然隨之膨脹,開發(fā)人員的工作量越來越大揩瞪,比如64K問題赋朦,比如協(xié)作開發(fā)問題。App一般都會走向組件化的道路李破,而組件化的前提就是解耦宠哄,那么我們首先要做的就是解耦頁面之間的依賴關(guān)系。
  3. 簡化代碼嗤攻。數(shù)行跳轉(zhuǎn)代碼精簡成一行代碼琳拨。
  4. 其他...

特性

Router有哪些特性或者優(yōu)點呢?

  1. 基于注解屯曹,使用方便狱庇,源碼簡潔
  2. 鏈式調(diào)用,api友好
  3. 多路徑支持
  4. 結(jié)果回調(diào)恶耽,每次跳轉(zhuǎn)都會回調(diào)跳轉(zhuǎn)結(jié)果
  5. 編譯期處理注解密任,不影響運行時性能
  6. 除了可以使用注解定義路由,還支持手動分配路由
  7. 自定義攔截器偷俭,可以對路由進行攔截浪讳,比如登錄判斷和埋點處理
  8. 自定義路由匹配規(guī)則,相比較其他路由框架涌萤,該項目并沒有限制路由的寫法淹遵,除了內(nèi)置的幾個匹配器,用戶完全可以定義自己的規(guī)則
  9. 支持隱式Intent跳轉(zhuǎn)
  10. 支持多模塊使用负溪,支持組件化開發(fā)
  11. 不僅支持注解Activity透揣,還支持注解Fragment
  12. 支持Kotlin

其他功能正在添加中...

集成

router-gradle-plugin router router-compiler
最新版本
[站外圖片上傳中……(3)]

集成過程也可參考項目主頁README

  1. 在項目級的build.gradle中加入依賴:
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.2.x ↑'
        classpath 'com.chenenyu.router:gradle-plugin:latest.integration'
    }
}

注意川抡,Router需要使用2.2.0及以上版本的Android gradle plugin來處理注解辐真,截至寫作時,最新版本為2.3.3崖堤。

  1. 在module級的build.gradle中使用plugin:
apply plugin: 'com.android.application/library'
apply plugin: 'com.chenenyu.router'

至此侍咱,集成工作就完成了,簡單的兩步:添加插件路徑和應(yīng)用插件密幔。這應(yīng)該是類似框架中最簡單的集成方式了楔脯。

使用

  1. Router需要初始化,用于初始化路由表胯甩,建議放到Application中做:
public class App extends MultiDexApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        // 開啟log昧廷,要放到前面才能看到初始化過程的log
        if (BuildConfig.DEBUG) {
            Router.openLog();
        }
        // 初始化
        Router.initialize(this);
    }
}
  1. 添加路由注解
// 單路徑注解
@Route("test")
public class TestActivity extends Activity {
  ...
}

// 多路徑注解堪嫂,這幾個注解都能打開該Activity
@Route({"user", "example://user", "http://example.com/user"})
public class UserActivity extends Activity {
  ...
}

@Route("fragment")
public class TestFragment extends Fragment {
  ...
}
  1. 發(fā)起路由操作
// 最簡單的路由跳轉(zhuǎn),打開TestActivity
Router.build("test").go(context);

// 其他部分api
Router.build("test")
   .requestCode(int) // 調(diào)用startActivityForResult
   .with(bundle)  // 攜帶跳轉(zhuǎn)參數(shù)
   .addFlags(flag)  // 添加標記麸粮,比如intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
   .anim(enter, exit)  // 添加跳轉(zhuǎn)動畫
   .callback(calback)  // 跳轉(zhuǎn)結(jié)果回調(diào)
   .go(this);

全部可用API可參考IRouter接口溉苛。

  1. startActivityForResult

Router.build(uri).requestCode(int).go(this); 其中requestCode一定得是一個非負數(shù)镜廉。


go()有如上幾個重載方法弄诲,如果是在Fragment中發(fā)起startActivityForResult路由操作,切記傳當前fragment實例哦娇唯!不然在fragment中是接受不到回調(diào)的齐遵。

  1. 添加跳轉(zhuǎn)動畫
// 1. 基礎(chǔ)動畫
Router.build(uri).anim(enter, exit).go(this);
// 2. 轉(zhuǎn)場動畫ActivityOptions
Router.build(uri).activityOptions(options).go(this);
  1. 獲取目標頁面
// 1. 獲取intent,然后可以操作intent
Intent intent = Router.build(uri).getIntent(context);
// 2. 獲取fragment塔插,然后可以將該fragment添加到Activity中
Fragment fragment = (Fragment) Router.build(uri).getFragment(context);
  1. 全局攔截器
// 1. 添加全局攔截器
Router.addGlobalInterceptor(routeInterceptor);
// 2. 跳過全局攔截器
Router.build(uri).skipInterceptors().go(this);
  1. 添加攔截器

攔截器是Router的功能之一梗摇,作用就是在執(zhí)行路由之前判斷是否需要攔截該次路由請求。比如可以在攔截器中做登錄狀態(tài)判斷想许。

// 定義攔截器(通過注解)
@Interceptor("SampleInterceptor")
public class SampleInterceptor implements RouteInterceptor {
    @Override
    public boolean intercept(Context context, @NonNull Uri uri, @Nullable Bundle extras) {
      // 返回true表示攔截當前路由  
      return true;
    }
}
// 定義攔截器(通過代碼)
Router.handleInterceptorTable(new InterceptorTable() {
    @Override
    public void handle(Map<String, Class<? extends RouteInterceptor>> map) {
        map.put("SampleInterceptor", SampleInterceptor.class);
    }
});

// 應(yīng)用攔截器(通過注解)
@Route(value = "test", interceptors = "SampleInterceptor")
public class TestActivity extends AppCompatActivity {
    ...
}
// 應(yīng)用攔截器(通過代碼)
Router.handleTargetInterceptors(new TargetInterceptors() {
    @Override
    public void handle(Map<Class<?>, String[]> map) {
        map.put(TestActivity.class, new String[]{"SampleInterceptor"});
    }
});
  1. 自定義路由

除了可以使用注解來添加路由外(上面步驟2介紹的方式)伶授,還可以通過代碼手動控制路由表。

// 動態(tài)添加路由表
Router.handleRouteTable(new RouteTable() {
    @Override
    public void handle(Map<String, Class<?>> map) {
        map.put("dynamic1", TestActivity.class);
        map.put("dynamic2", TestFragment.class);
        ...
    }
});

該方式與注解沒有沖突流纹,可以同時使用糜烹。

  1. 路由匹配規(guī)則

該功能是Router最大的特色功能,不同于其他框架漱凝,Router并沒有規(guī)定路由的寫法規(guī)則疮蹦,而是抽象出Matcher的概念,交給用戶去控制茸炒。但是Router仍然內(nèi)置了4個常用的Matcher愕乎。

MatcherRegistry

匹配優(yōu)先級從高到低依次是DirectMatcherSchemeMatcher壁公、ImplicitMatcherBrowserMatcher感论,關(guān)于原理將在后序的原理篇中進行講解。
假設(shè)有如下路由頁面:

@Route({"user", "http://example.com/user"})
public class UserActivity extends Activity {
    ...
}

除了可以通過Router.build("test").go(this)Router.build("http://example.com/user").go(this)打開UserActivity之外(DirectMatcher命中)紊册,還可以通過Router.build("http://example.com/user?id=9527&status=0").go(this)打開(通過SchemeMatcher命中)笛粘,并且自動幫你配置了Bundle參數(shù),即:

@Route({"user", "http://example.com/user"})
public class UserActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        Bundle bundle = getIntent().getExtras();
        String id = bundle.getString("id");
        String status = bundle.getString("status");
    }
}

Matcher支持配置多個湿硝,會根據(jù)優(yōu)先級依次進行匹配薪前。

  1. 參數(shù)注入

上面講了可以通過路由傳遞參數(shù),然后在目標頁面通過Bundle獲取关斜,其實這個過程也被Router簡化了示括。通過@InjectParam注解可以為Activity或者Fragment的成員變量添加參數(shù)注入

@Route({"test", "http://example.com/test", "router://test"})
public class TestActivity extends AppCompatActivity {
    @InjectParam
    int id = 123;
    @InjectParam(key = "status")
    private String sts = "default"; // 不建議使用private修飾符

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        Router.injectParams(this); // 實現(xiàn)參數(shù)注入
    }
}
  • @InjectParam會在Bundle中取出對應(yīng)key的值傳給成員變量,默認key為變量名痢畜,也可以通過key=""屬性指定
  • 參數(shù)注入支持變量的默認值垛膝,目前支持默認值的變量類型有基本數(shù)據(jù)類型鳍侣,String,CharSequence吼拥,其他類型的默認值都是null
  • 變量不建議使用private修飾符倚聚,因為私有的變量會采用反射的方式注入?yún)?shù)
  • 需要使用Router.injectParams(this)來實現(xiàn)最終的參數(shù)注入

Router還有其他一些人性化的小功能,在這里就不一一介紹了凿可,有問題可以在項目主頁提issue惑折。后續(xù)會給大家講解一下背后的實現(xiàn)原理。

總結(jié)

Router是一個十分小巧靈活的路由框架枯跑,代碼設(shè)計也很優(yōu)雅簡潔惨驶,且完美支持組件化開發(fā),目前仍在不斷地迭代中敛助,源碼地址為https://github.com/chenenyu/Router粗卜,歡迎各位試用點評。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末纳击,一起剝皮案震驚了整個濱河市续扔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌焕数,老刑警劉巖纱昧,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異百匆,居然都是意外死亡砌些,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門加匈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來存璃,“玉大人,你說我怎么就攤上這事雕拼∽荻” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵啥寇,是天一觀的道長偎球。 經(jīng)常有香客問我,道長辑甜,這世上最難降的妖魔是什么衰絮? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮磷醋,結(jié)果婚禮上猫牡,老公的妹妹穿的比我還像新娘。我一直安慰自己邓线,他們只是感情好淌友,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布煌恢。 她就那樣靜靜地躺著,像睡著了一般震庭。 火紅的嫁衣襯著肌膚如雪瑰抵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天器联,我揣著相機與錄音二汛,去河邊找鬼。 笑死主籍,一個胖子當著我的面吹牛习贫,可吹牛的內(nèi)容都是我干的逛球。 我是一名探鬼主播千元,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼颤绕!你這毒婦竟也來了幸海?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤奥务,失蹤者是張志新(化名)和其女友劉穎物独,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氯葬,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡挡篓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了帚称。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片官研。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖闯睹,靈堂內(nèi)的尸體忽然破棺而出戏羽,到底是詐尸還是另有隱情,我是刑警寧澤楼吃,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布始花,位于F島的核電站,受9級特大地震影響孩锡,放射性物質(zhì)發(fā)生泄漏酷宵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一躬窜、第九天 我趴在偏房一處隱蔽的房頂上張望浇垦。 院中可真熱鬧,春花似錦斩披、人聲如沸溜族。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽煌抒。三九已至仍劈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寡壮,已是汗流浹背贩疙。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留况既,地道東北人这溅。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像棒仍,于是被迫代替她去往敵國和親悲靴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容