ARouter使用和案例

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/y1962475006/article/details/83026256

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、拦截器、重定向器、降级服务的执行顺序是:起点-》重定向器-》降级服务-》拦截器-》终点;
6、1.2.4版本build的时候,强制要求uri有path,就是说,像https://www.baidu.com 这样的uri会直接报错
7、降级发生后,ARouter会默认Toast,但是这个我们可能并不想要。

思考

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、利用降级服务,示例如下:

	@Override
    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环境下弹出的(个人认为不太合适)
3、判断是否是想跳本地,可以通过path判断,因为本地路由,都是/group/child或者nativeshceme/group/child这种格式。

猜你喜欢

转载自blog.csdn.net/y1962475006/article/details/83026256
今日推荐