Router:一款單品、組件化、插件化全支持的路由框架

簡介

因爲如今已經有不少各類各樣的路由框架了,因此在這裏。我也再也不贅述什麼是路由?路由框架的意義是什麼之類的了。java

特性

  • 安全: 路由啓動過程當中。全程catch住異常並通知用戶。徹底不用擔憂crash問題。
  • 強大的攔截器功能:與大部分的路由不一樣。提供三種路由攔截器機制,對應不一樣業務下使用。
  • 方便: 使用apt註解生成路由表,配置方便,易維護。
  • 靈活: 配置路由表方式多樣,知足你在任意條件下進行使用。
  • 支持兩種路由:頁面路由與動做路由。
  • 支持重啓路由:路由被攔截後。可經過一行代碼無縫恢復重啓路由。在登陸檢查中會頗有用。
  • 高度可定製:單品、組件化完美支持,對於插件化環境。也能夠針對性的定製使用。

用法

點擊前往Github頁git

本篇文章主要介紹Router在單品、組件化環境下的使用方式,針對插件化環境下的使用適配。請參考如下文章:github

Router: 教你如何進行任意插件化環境下的路由適配json

依賴

  • 添加jitpack倉庫
allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
複製代碼
  • 添加依賴
compile "com.github.yjfnypeu.Router:router-api:2.6.0"
annotationProcessor "com.github.yjfnypeu.Router:router-compiler:2.6.0"
複製代碼

路由表

路由表的定義

要理解什麼是路由表。須要先明確如下幾點定義:api

路由映射: 一組特定url特定頁面的映射。好比www.baidu.com映射的是百度頁面。安全

路由表: 也叫路由映射表,一個用於存儲全部的路由映射的容器。bash

路由: 是指經過一個url在路由表中匹配到對應的頁面。並完成啓動的過程。稱爲一次路由。app

如下就是一個簡單的路由流程:框架

因此對於Android端來講。路由表能夠理解爲一系列特定url與特定Activity之間的映射集合maven

建立路由表

與不少其餘的路由框架不一樣。此路由框架並未使用自動註冊路由表的方式來作。由於自動註冊路由表在靈活性上有所欠缺。

建立路由表分爲兩種方式:手動建立使用註解在編譯時動態生成路由表。

這裏主要介紹經過使用註解動態生成的建立方式。手動建立路由表的使用場景只在部分不支持運行時註解的編譯環境下推薦使用。

上面提到了,一個路由映射是一組特定url與特定頁面之間的映射關係。因此經過對指定頁面。添加註解配置上指定url。便可獲得一組對應的路由映射:

@RouterRule("haoge://page/user")
public class UserActivity extends Activity {
	...
}
複製代碼

添加好此註解後。便可對項目觸發一次編譯。使其自動生成對應的路由表類:

自動生成的路由表,類名爲RouterRuleCreator:

// 此類爲編譯時註解自動生成的類。
public class RouterRuleCreator implements RouteCreator {
  @Override
  public Map<String, ActivityRouteRule> createActivityRouteRules() {
    Map<String,ActivityRouteRule> routes = new HashMap<>();
    routes.put("haoge://page/user", new ActivityRouteRule(UserActivity.class));
    return routes;
  }

  @Override
  public Map<String, ActionRouteRule> createActionRouteRules() {
    Map<String,ActionRouteRule> routes = new HashMap<>();
    ...
    return routes;
  }
}
複製代碼

PS:若是當前環境不支持編譯時註解。能夠選擇手動建立此RouteCreator路由表實例類進行使用。

註冊路由表

生成具體的路由表類後。便可經過如下代碼進行路由表註冊了:

RouterConfiguration.get().addRouteCreator(new RouterRuleCreator());
複製代碼

註冊成功以後。則經過如下方式進行啓動:

Router.create(url).open(context);
複製代碼

以上是最簡單的路由配置及用法。下面將一步步的更深刻的介紹更多用法

一對多

對於同一個頁面。能夠配置多個不重複的路由連接:

@RouterRule({url1, url2, url3})
public class ExampleActivity extends Activity {
	...
}
複製代碼

頁面內獲取啓動的uri

全部的路由啓動事件,都會將啓動的url連接,存入bundle中進行傳遞。可經過如下key值進去讀取:

Uri uri = getIntent().getParcelableExtra(Router.RAW_URI);
複製代碼

配置baseUrl

通常來講:一個app所定義使用的路由url都會有個特定的前綴。而若是是在插件化環境下。也推薦對各個插件分別定義一份獨有的路由前綴。緣由將在下一篇介紹插件化環境路由配置的文章中進行具體說明。

框架提供RouteConfig註解。一個module只能配置一次且必須配置於Application子類之上:

@RouteConfig(baseUrl="haoge://page/")
public class App extends Application {
	...
}
複製代碼

下表是路由前綴與路由地址以前的匹配關係,橫排表示RouteRule配置的路由地址,豎排表示baseUrl:

自動解析url參數

Router的自動參數解析。是結合的Parceler框架來進行使用的,關於Parceler框架的介紹能夠參考下方的連接:

Parceler: 優雅的使用Bundle進行數據存取就靠它了!

請注意:Parceler框架並非Router所必須依賴的框架。只是添加此框架使用後,能使用更強大的特性。

若是不使用的話。全部的url參數。都將默認解析爲String並進行傳遞。

  1. 參數自動轉換:

首先。咱們先在Activity基類中配置注入入口,配置此入口後。就會自動從intent中讀取對應的數據。注入到子類中的被Arg註釋過的成員變量中去了:

public class BaseActivity extends Activity {

	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Parceler.toEntity(this,getIntent());
    }
} 
複製代碼

假設當前咱們有如下一個頁面:

@RouterRule("haoge://page/example")
public class ExampleActivity extends BaseActivity {
	@Arg
	String name;
	@Arg
	long id;
	@Arg
	boolean isLogin;
	...
}
複製代碼

能夠看到。這個頁面含有三個屬性。並都添加了Arg註解。那麼此時咱們能夠如下方的連接來進行跳轉傳參:

Router.create("haoge://page/example?name=haoge&id=10086&isLogin=false").open(context);
複製代碼

連接中的參數將會自動根據Arg註解的類型進行自動轉換。並在轉換後轉載入Intent中進行傳遞. 即至關於如下的操做:

String url = "haoge://page/example?name=haoge&id=10086&isLogin=false";
Uri uri = Uri.parse(url);
Bundle bundle = new Bundle;
bundle.putString("name", uri.getQueryParameter("name"))
bundle.putLong("id", 
	Long.parseLong(uri.getQueryParameter("id")));
bundle.putBoolean("isLogin", 
	Boolean.parseBoolean(uri.getQueryParameter("isLogin")));

// 啓動路由並傳遞bundle
...
複製代碼

此種自動轉換類型的參數。只支持基本數據類型。若Arg所註釋的屬性類型不爲基本數據類型。則不觸發自動轉換,將讀取的String串直接存入Intent中。

  1. 傳遞複雜參數

可是不少時候咱們的傳參數據又不止是基本數據類型。好比說普通實體bean。好比說一個列表。因此這就是Parceler展示光芒的時候了!

Parceler自帶JSON數據轉換功能。對於傳遞的是非基本數據類型的。則能夠在參數中傳遞此類型的json串:這也是爲何推薦引入Parceler框架進行使用的緣由:小而強大!

複雜參數須要使用Parceler的轉換功能,關於數據轉換器。也在上面那篇文章中有描述。這裏我使用的是FastJson的轉換器:

Parceler.setDefaultConverter(FastJsonConverter.class);
複製代碼

首先假設當前有個此頁面, 須要傳參爲普通實體類User:

@RouterRule("usercenter")
public class UserCenterActivity extends BaseActivity{
	@Arg
	User user;
}

public class User {
	public String username;
	public String password;
	...
}
複製代碼

那麼就能夠經過如下連接進行跳轉:

// 這裏爲了理解方便,我沒有直接拼裝連接。
User user = new User("router", "123456");
String json = JSON.toJSONString(user);
// 對json串須要先進行url編碼。
String encodeJson = URLEncoder.encode(json);
String url = String.format("haoge://page/usercenter?user=%s", encodeJson);
Router.create(url).open(context);
複製代碼

能夠看到,經過此種方式,能夠直接傳遞具體的json數據進行傳遞。請注意對於連接中的json數據。必定要先進行encode編碼。避免內部uri解析異常。

因爲目標頁對應的user類型不爲基本數據類型。因此此處將直接將user所指代的值。自動解碼後直接放入Intent中傳遞入目標頁。目標頁中則會使用Parceler自動將此json轉換解析成User類。完成複雜參數的傳遞

動做路由

上面所介紹的。都是經過一個連接。打開一個對應的頁面。此種路由跳轉稱爲頁面路由。

Router也支持另外一種路由:動做路由,此種路由沒有頁面跳轉。只是用於作一些特殊的操做。

好比說:加入購物車、清空購物車數據、退出登陸等。

@RouterRule("shopcar.clear")
public class ClearShopcarAction extends ActionSupport {
	@Override
    public void onRouteTrigger(Context context, Bundle bundle) {
        // TODO 清空購物車操做
    }
}
複製代碼

而後便可經過如下連接觸發清空購物車操做:

Router.create("haoge://page/shopcar.clear").open(context);
複製代碼

額外請求參數

上面舉的例子。都是所有數據經過一個url直接傳遞。可是不少時候。咱們是須要在此url的基礎上。再額外添加一些別的數據進行傳遞的。好比轉場動畫配置、requestCode配置、Intent.flags配置等.

Router.create(url)
	.addExtras(bundle) // 添加額外bundle數據參數
	.requestCode(code) // 用於startActivityForResult
	.setAnim(enterAnim, exitAnim)// 轉場動畫
	.addFlags(flag)// intent.addFlags(flag);
	.addInterceptor(interceptor)// 添加攔截器
	.setCallback(callback)// 設置路由回調
	.open(context);
複製代碼

添加路由回調

在講路由表的時候有提到過,路由是一種特殊的啓動流程,且啓動不必定成功。因此很天然的,這個時候就須要有一個路由回調接口。

路由回調接口爲RouteCallback類:

public interface RouteCallback {
	// 當路由尋址失敗時。觸發此回調
	void notFound(Uri uri, NotFoundException e);
	// 當路由啓動成功時。觸發此回調
	void onOpenSuccess(Uri uri, RouteRule rule);
	// 當路由啓動失敗時。觸發此回調。包括
	void onOpenFailed(Uri uri, Throwable e);
}
複製代碼

路由回調配置分爲兩種:

  • 全局路由回調:全部的路由啓動事件。都會觸發此回調
RouterConfiguration.get().setCallback(callback);
複製代碼
  • 局部路由回調:只被當前路由啓動觸發。
Router.create(url)
	.setCallback(callback)
	.open(context);
複製代碼

路由回調在進行頁面跳轉埋點時,會是很是有用的一個特性。

日誌打印

當配置Router.DEBUG爲true時(默認爲false)。框架將會啓用內部日誌輸出。建議使用BuildConfig.DEBUG進行日誌開關控制, 達到在只在開發時進行日誌輸出的做用:

Router.DEBUG = BuildConfig.DEBUG
複製代碼

日誌可經過RouterLog進行過濾查看

攔截器

顧名思義:攔截器就是用於在路由啓動過程當中,進行一系列的提早檢查,當檢查不符合規則時,則使這次路由啓動失敗。

舉個最經典的案例:登陸檢查

當不使用路由進行跳轉時,這種狀況就會致使你本地寫上了大量的登陸判斷邏輯代碼。這在維護起來是很費勁的。並且也很是不靈活,而使用攔截器的方式來作登陸檢查,就會很方便了:

下面是一個簡單的登陸攔截實現:

// 實現RouteInterceptor接口
public class LoginInterceptor implements RouteInterceptor{
    @Override
    public boolean intercept(Uri uri, RouteBundleExtras extras, Context context){
    	// 未登陸時進行攔截
        return !LoginChecker.isLogin();
    }

    @Override
    public void onIntercepted(Uri uri, RouteBundleExtras extras, Context context) {
    	// 攔截後跳轉登陸頁並路由信息傳遞過去,便於登陸後進行恢復
        Intent loginIntent = new Intent(context,LoginActivity.class);
        // uri爲路由連接
        loginIntent.putExtra("uri",uri);
        // extras中裝載了全部的額外配置數據
        loginIntent.putExtra("extras",extras);
        context.startActivity(loginIntent);
    }
}
複製代碼
public class LoginActivity extends BaseActivity {

	@Arg
	Uri uri;
	@Arg
	RouteBundleExtras extras;
	
	void onLoginSuccess() {
		if(uri != null) {
			// 登陸成功。使用此方法直接無縫恢復路由啓動
			Router.resume(uri, extras).open(context);
		}
		finish();
	}
}
複製代碼

攔截器功能是Router框架的重點,且通過長時間的迭代。Router目前提供三種攔截器提供使用,你能夠根據你本身的須要。靈活的選擇使用什麼類型的攔截器。

1. 全局默認攔截器:

設置方式:RouterConfiguration.get().setInterceptor(interceptor);

做用域:此全局默認攔截器。將會被全部啓動的路由事件所觸發。

推薦使用場景

一些須要進行全局判斷的檢查:好比登陸檢查等。且最好此處所配置的攔截器。添加上對應的開關控制。

好比說作登陸檢查的,控制若是有requestlogin參數的才啓用登陸檢查。將登陸檢查控制交給提供url的地方。

2. 針對某次路由所特別設置的攔截器

設置方式:Router.create(url).addInterceptor(interceptor);

做用域:只被此處所建立的路由觸發。

推薦使用場景:一些只在此處啓動路由時才須要觸發的檢查:好比deeplink外部連接入口處,檢查外部連接是否合法等。

3. 針對某個目標路由所特別設置的攔截器

設置方式:在配置了RouterRule的目標類上。添加@RouteInterceptor()註解。將須要配置的註解Class加入:

@RouteInterceptors(CustomInterceptors.class)
@RouterRule(rule)
public class ExampleActivity extends Activity {}
複製代碼

做用域:當路由url所匹配的目標路由爲此路由時被觸發

推薦使用場景:針對此頁面跳轉的的檢查,好比對傳遞參數進行過濾,避免傳入無效數據致使不可期異常等。

此三種攔截器,觸發的優先順序爲:全局默認 > 某次路由 > 某個目標路由,且若當此路由已被某個攔截器攔截了。則將不會繼續觸發後續攔截器

Router在組件化環境下進行使用

對於組件化中使用Router框架。能夠參考此處放於github上的組件化demo

可結合上方demo與下方描述一同查看。達到更加方便理解的做用!

在組件化環境下使用路由框架。主要須要考慮如下幾點:

註冊多個路由表

因爲Router自己沒有使用自動註冊的方式來進行路由表註冊。而是提供了相應的接口、相應的方法來手動註冊,這種配置方式自己便可作到動態註冊多個路由表的效果:

RouterConfiguration.get().addRouteCreator(routeCreator);
複製代碼

因此在組件化中進行使用。只要想辦法註冊多個組件自身生成的路由表便可。

激活業務組件的註解處理器

路由表類的生成,是在編譯時自動生成的。而編譯時生成框架的做用域只在當前module中。因此須要將Router的註解處理器,分別配置添加到每一個業務組件之中,使每一個業務組件都可以使用註解生成自身的路由表類:

annotationProcessor "com.github.yjfnypeu.Router:router-compiler:2.6.0"
複製代碼

而組件化中,對於使用的註解處理器。推薦的作法是將全部須要使用的註解處理器都抽離到一個統一的gradle腳本中。而後由各個組件直接apply應用便可:

建立processor.gradle:

dependencies {
    annotationProcessor "com.github.yjfnypeu.Router:router-compiler:2.6.0"
    ...// 全部註解處理器均放置於此配置
}
複製代碼

而後在組件中的build.gradle中直接經過apply from方法應用此腳本便可。

這樣的作法有如下幾點好處:

  1. 因爲編譯時註解的註解處理器。是直接提供給IDE進行使用的。並不會再打包時將對應的代碼打包進apk中。因此不用擔憂引入額外的不須要的代碼進入apk中。
  2. 便於版本升級統一控制

避免多個組件生成的路由表衝突

在單品環境下使用時。有介紹會在編譯時生成一個具體的路由表類RouterRuleCreator, 而這個類生成的包名是默認寫死的:com.lzh.router.

因此在多組件環境下進行使用時,須要對每一個module指定不一樣的生成路由表類的包名。避免出現重複類衝突問題:

@RouteConfig(pack="com.router.usercenter")
public class UCApp extends Application {
	...
}
複製代碼

RouteConfig註解不止提供單品中使用的baseUrl方法進行路由前綴配置。也提供pack方法。用於指定此module所生成的路由表的具體包名。因此對於組件化環境。只要對不一樣組件指定不一樣的包名便可!

推薦的註冊方式

因爲組件化其實全部組件都是被app殼所加載的。並不像插件化那樣會出現按需加載的狀況。因此這種環境下,多路由表的註冊方式,推薦使用反射,一次性將全部有效組件所有加載的方式進行使用:

private void loadRouteRulesIfExist() {
	// 此packs爲全部組件中定義的路由表類生成包名集。
	String[] packs = ComponentPackages.Packages;
	String clzNameRouteRules = ".RouterRuleCreator";
	for (String pack : packs) {
	    try {
	        Class<?> creator = Class.forName(pack + clzNameRouteRules);
	        RouteCreator instance = (RouteCreator) creator.newInstance();
	        RouterConfiguration.get().addRouteCreator(instance);
	    } catch (Exception ignore) {
	    	// ignore
	    }
	}
}
複製代碼

由於使用的是反射註冊。因此請不要忘了加上混淆配置:

-keep class * implements com.lzh.nonview.router.module.RouteCreator
複製代碼

具體代碼能夠參考github上的組件化demo

插件化

對於介紹插件化環境下的使用方式的文章, 能夠參考下方連接

Router: 教你如何進行任意插件化環境下的路由適配

若是你當前的插件化方案是使用的Small或者RePlugin。那麼也能夠參考如下兩個demo

Small環境下使用Router

RePlugin環境下使用Router

相關文章
相關標籤/搜索