ARouter使用和案例

ARouter的使用

官網github:
https://github.com/alibaba/ARouter/blob/master/README_CN.md
這裏對其做了分類整理,以及一些想法,便於更方便理解。

跳轉

跳轉Activity

1、path跳轉

path的規則:/group/child…至少兩個“/”;和Activity的@Route註解值匹配

// 構建標準的路由請求

ARouter.getInstance().build("/home/main").navigation();

// 構建標準的路由請求,並指定分組

ARouter.getInstance().build("/home/main", "ap").navigation();

2、uri跳轉

Uri uri;
ARouter.getInstance().build(uri).navigation();

3、startActivityForResult

ARouter.getInstance().build("/home/main", "ap").navigation(this, 5);

navigation的第一個參數***必須是Activity***,第二個參數則是RequestCode,因爲不指定activity,無法得知應該從哪個任務棧啓動,導致收不到result。

總結:
跳轉activity的時候,但凡涉及activity的任務棧,必須使用navigation(context)並且context只能是activity。建議,如果當前可以獲取activity,最好傳了。

跳轉Fragment

使用方式同Activity,navigation()方法會返回要跳到的對象實例,跳轉Fragment可以拿到Fragment實例操作。

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

傳值

ARouter提供了豐富的傳值方式,基本滿足使用。
// 直接傳遞Bundle

Bundle params = new Bundle();
ARouter.getInstance()
	.build("/home/main")
	.with(params)
	.navigation();

// 指定Flag

ARouter.getInstance()
	.build("/home/main")
	.withFlags();
	.navigation();

// 獲取Fragment

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

// 對象傳遞

ARouter.getInstance()
	.withObject("key", new TestObj("Jack", "Rose"))
	.navigation();

// 覺得接口不夠多,可以直接拿出Bundle賦值

ARouter.getInstance()
	    .build("/home/main")
	    .getExtra();

還提供了int、long、short等基本變量和String、Object、Serializable、Parcelable和對應的數組、List。種類相當豐富。

帶動畫的跳轉

// 轉場動畫(常規方式)

ARouter.getInstance()
    .build("/test/activity2")
    .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
    .navigation(this);

// 轉場動畫(API16+)

ActivityOptionsCompat compat = ActivityOptionsCompat.
    makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);

ARouter.getInstance()
	.build("/test/activity2")
	.withOptionsCompat(compat)
	.navigation();

注意 ,makeSceneTransitionAnimation 使用共享元素的時候,需要在navigation方法中傳入當前Activity

more

我們經常需要在目標頁面中配置一些屬性,比方說"是否需要登陸"之類的
可以通過 Route 註解中的 extras 屬性進行擴展,這個屬性是一個 int值,換句話說,單個int有4字節,也就是32位,可以配置32個開關 剩下的可以自行發揮,通過字節操作可以標識32個開關,通過開關標記目標頁面的一些屬性,在攔截器中可以拿到這個標記進行業務邏輯判斷
@Route(path = “/test/activity”, extras = Consts.XXXX)

服務

服務是ARouter中很重要的一個概念,ARouter的所有功能都是服務,本質上是接口IProvider的實現類,它也是組件化之間通信的橋樑:

public interface IProvider {

/**
 * Do your init work in this method, it well be call when processor has been load.
 *
 * @param context ctx
 */
void init(Context context);

}
後面的攔截器、重定向、降級、di注入都是服務。只需要實現對應接口,並添加註解,就能自動將服務註冊到框架。

攔截器

攔截器可以控制是不是可以繼續跳轉,以及跳轉到哪裏。

public interface IInterceptor extends IProvider {

    /**
     * The operation of this interceptor.
     *
     * @param postcard meta
     * @param callback cb
     */
    void process(Postcard postcard, InterceptorCallback callback);
}

如果可以繼續,調用callback.continue(postcard), postcard是跳轉的信息類,包含了uri、path、傳遞的數據等,可以在這裏對其做處理;如果不繼續,可以調用callback.interrept()停止跳轉。

注意:
1、使用綠色通道可以跳過所有攔截器:

ARouter.getInstance().build("/home/main").greenChannel().navigation();

2、服務本身不會被攔截;
3、fragment 的navigation()不會被攔截

重定向

重定向是在跳轉開始前發生的“準備工作”

public interface PathReplaceService extends IProvider {

    /**
     * For normal path.
     *
     * @param path raw path
     */
    String forString(String path);

    /**
     * For uri type.
     *
     * @param uri raw uri
     */
    Uri forUri(Uri uri);
}

提供了對path和Uri兩種重定向。入參爲當前路徑,出參爲重定向後的路徑。

降級

降級是一個成熟框架應有的優秀設計。

當跳轉失敗(路由不正確、版本未升級等),會回掉給onLost方法,可以在這時給出提示或者轉到備用方案。

public interface DegradeService extends IProvider {

    /**
     * Router has lost.
     *
     * @param postcard meta
     */
    void onLost(Context context, Postcard postcard);
}

di自動注入

前面說到ARouter提供了強大豐富的跳轉傳參,與其匹配的,就是怎麼接參數。

public interface AutowiredService extends IProvider {

/**
 * Autowired core.
 * @param instance the instance who need autowired.
 */
void autowire(Object instance);
}

這個服務框架幫我們實現好了,只需要在跳轉的對象裏使用Autowired註解,並且調用了

ARouter.getInstance().inject(this);

就可以獲取到對應的值。示例:

/**
     * 身份證名字
     */
    @Autowired(name = Extras.ID_CARD_NAME,required = true)
    String name ;

required = true 表示必傳。

自定義服務

除了框架定義的服務,我們可以自己定義服務,這是組件化中最重要的一環,通過定義服務,註冊到ARouter,實現Client-Router-Client隔離。
可以對着上述的幾個服務依樣畫葫蘆:
服務提供方Client1:

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

// 實現接口
@Route(path = "/service/hello", name = "測試服務")
public class HelloServiceImpl implements HelloService {

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

    @Override
    public void init(Context context) {

    }
}

然後服務使用方Client2:

public class Test {
    @Autowired
    HelloService helloService;

    @Autowired(name = "/service/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("/service/hello").navigation();
	helloService3.sayHello("Vergil");
	helloService4.sayHello("Vergil");
    }
}

這樣就可以完成調用。從上面的示例可以看出,服務本身就支持AOP。
注意:這種調用方式,決定了自定義的服務必須要在基礎架構的包裏,Client1和Client2都依賴這個基礎架構包,不然Client2找不到這個類;

applink

從外部喚起app有兩種場景,applink和推送,這時候沒有辦法直接使用ARouter,依然需要一箇中間Activity來做中轉,在中轉Activity中再使用ARouter跳轉:

<activity
            android:name=".RouterActivity"
            android:exported="true"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Translucent.NoTitleBar">

            <!-- App Links -->
            <intent-filter android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                    android:host="yourhost"
                    android:scheme="yourscheme" />
            </intent-filter>
        </activity>

點擊帶有scheme/host 的url,會喚起RouterActivity:

public class RouterManagerActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Uri uri = getIntent().getData();
        ARouter.getInstance().build(uri).navigation();
        finish();

    }
}

踩坑

1、startActivityForResult 跳轉,一定要帶上當前的activity;
2、withFlag 時,如果是clearTop的flag,也要帶當前activity;
3、使用Arouter接受參數,要用autowired,也要inject(this),不然getIntent拿不到數據;
4、path必須至少有兩個"/";
5、攔截器、重定向器、降級服務的執行順序是:起點-》重定向器-》降級服務-》攔截器-》終點;如果降級onLost了,攔截器不會生效。
6、1.2.4版本build的時候,強制要求uri有path,就是說,像https://www.baidu.com 這樣的uri會直接報錯
7、降級發生後,debug下ARouter會默認Toast,但是這個我們可能並不想要。可以在初始化時不開啓debugable。
8、如果在navigation 方法中給了callback,降級service會被中斷(個人覺得實在不合理。。。一個service級別的東西被一個callback 給中斷了)。

思考

ARouter應該怎麼用?從上面的分析看,ARouter功能兩個大板塊,一個是跳轉,一個是服務。那麼使用也該分兩個方向:
1、跳轉
使用路由跳轉屏蔽了具體要跳到的XXXActivity.class,用字符串(path)做映射,有兩個好處,第一個是多目錄包下不需要依賴XXXActivity.class,方便拆成多個aar或者apk;第二個是可以動態切換跳轉的目的地,只要build的path由服務端下發。
但是是不是每個Activity都要註冊在ARouter呢?其實不需要,對於每個組件來說,前一個頁面和後一個頁面基本是確定的,並且耦合了參數,其實只要控制好組件的入口activity就行了,組件內部怎麼跳轉並不重要,因爲他們的關係是固定的。
2、服務
除了框架提供的,自己自定義的服務可以用來做組件間通信。

第一種

假設Client1、Client2、ARouter是三個獨立的包,那麼Provider放在ARouter所在的包裏,Client1和Client2依賴ARouter,那麼這種可以直接拿到服務實例,向上文自定義服務寫的例子。但是這種例子的問題是,需要把這個服務的類,放到ARouter的包裏,否則Client2拿不到實例。

使用ARouter實現全路由分發

背景:許多跳轉路由都是服務下發的,客戶端透轉給路由,交由路由跳轉,但是服務端下發的,可能是本地在ARouter中註冊的path,也可能是一個h5鏈接,希望用h5打開。原來的方案,是Arouter處理本地路由,對於h5,再單獨處理,這樣,如果一個接口後端原來給的是本地path,後來希望指向另一個h5頁面,就沒辦法動態切換;反之亦然。
解決方案:
1、升級ARouter(我本地用的1.3.1),升級後的版本,可以用重定向,在url後面追加固定的path如“/DEAULT/PATH”,繞開build時必須要有path,這樣就可以處理類似“https://www.baidu.com” 這樣的鏈接;示例:

@Route(path = "/router/MyPathReplaceService") // 必須標明註解
public class MyPathReplaceService implements PathReplaceService {
    public static final String DEFAULT_PATH = "/DEFAULT/PATH";

    @Override
    public String forString(String path) {
    }

    @Override
    public Uri forUri(Uri uri) {

		// 空path的uri 添加默認path
        if (TextUtils.isEmpty(uri.getPath())) {
            uri = uri.buildUpon().appendEncodedPath(DEFAULT_PATH).build();
        }
        return uri;
    }

    @Override
    public void init(Context context) {

    }
}

2、利用降級服務,示例如下:
@Route(path = “/router/MyDegradeService”) // 必須標明註解
public class MyDegradeService implements IProvider{
public void onLost(Context context, Postcard postcard) {
//判斷是否是想跳轉本地,如果是,走降級邏輯;不是,認爲是跳轉到h5.
if (isJumpToNative(postcard)) {
Toasts.shortToast(“升級版本!”);
} else {
Uri targetUri = postcard.getUri();
//如果有默認path,恢復
if (MyPathReplaceService.DEFAULT_PATH.equals(postcard.getUri().getPath())) {
targetUri = targetUri.buildUpon().path("").build();
}
jumpToWebView(targetUri);
}
}
}
說明:
1、爲什麼不用攔截器?因爲攔截器的執行順序是在降級之後,並且很多情況攔截器不起效(上文有說,個人認爲攔截器的定位沒有設計好)
2、跳轉h5會有toast彈出來,這個是ARouter走到降級後自己在debug環境下彈出的(如果想用降級搞點事情,這個Toast有些多餘)
3、判斷是否是想跳本地,可以通過path判斷,因爲本地路由,都是/group/child或者nativeshceme/group/child這種格式。
4、由於ARouter自己降級服務會被callback中斷,所以可以不實現ARouter自己的降級服務,改爲設置默認callback代理:

...
degradeService = ARouter.getInstance().navigation(MyDegradeService::class.java)
...
private var defaultCallBack: NavigationCallback = object : NavigationCallback {
			var callback : NavigationCallback?=null
            override fun onLost(postcard: Postcard?) {
                degradeService?.onLost(postcard)
                callback?.onLost(postcard)
            }

            override fun onFound(postcard: Postcard?) {
                callback?.onLost(postcard)
            }

            override fun onInterrupt(postcard: Postcard?) {
                callback?.onInterrupt(postcard)
            }

            override fun onArrival(postcard: Postcard?) {
                callback?.onArrival(postcard)
            }
        }
  ....
  fun navigation(context: Context?): Any? {
            return ARouter.getInstance().navigation(context, postcard, -1, defaultCallBack)
        }
   ...
  fun navigation(context: Context?,callback :NavigationCallback?): Any? {
  			defaultCallBack.callback = callbak
            return ARouter.getInstance().navigation(context, postcard, -1, defaultCallBack)
        }
        
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章