【Android】Arouter的使用

发布于:2024-12-18 ⋅ 阅读:(78) ⋅ 点赞:(0)

使用

集成ARouter框架

  • 添加依赖
    • 在项目的根目录下的 build.gradle 文件中,添加ARouter的依赖:
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        // 其他依赖项...
        classpath "com.alibaba:arouter-register:1.5.2" 
    }
}

allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

​ - 在应用模块的 build.gradle 文件中,添加以下依赖:

dependencies {
    // 其他依赖项...
    implementation 'com.alibaba:arouter-api:1.5.2'
    annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
}

使用ARouter框架

  • 初始化ARouter
    • 一般在自定义的 Application 类的 onCreate 方法中初始化ARouter,示例代码如下:
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (isDebug()) {           // 判断是否是调试模式
            ARouter.openLog();     // 开启日志
            ARouter.openDebug();   // 开启调试模式
        }
        ARouter.init(this);        // 初始化ARouter
    }

    private boolean isDebug() {
        return BuildConfig.DEBUG;
    }
}
  • 定义路由路径
    • 使用 @Route 注解来定义路由路径,通常在 ActivityFragment 或其他需要被路由的组件类上添加该注解。例如:
@Route(path = "/main/homeActivity")
public class HomeActivity extends AppCompatActivity {
    //...
}
  • 发起路由跳转
    • 在需要进行页面跳转的地方,可以使用 ARouter.getInstance().build() 方法构建一个路由请求,然后调用 navigation() 方法进行跳转,示例如下:
ARouter.getInstance().build("/main/homeActivity").navigation();
  • 传递参数
    • 可以在路由跳转时传递参数。首先在目标 ActivityFragment 中定义接收参数的字段,并使用 @Autowired 注解标记,然后在发起路由跳转时通过 withXXX 方法传递参数,示例如下:
// 目标Activity
@Route(path = "/main/detailActivity")
public class DetailActivity extends AppCompatActivity {
    @Autowired
    public String itemId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_detail);
        ARouter.getInstance().inject(this);
        // 使用itemId进行相关操作
    }
}

// 发起跳转并传递参数
ARouter.getInstance().build("/main/detailActivity")
       .withString("itemId", "12345")
       .navigation();
  • 处理返回结果
    • 如果需要在跳转到新页面并返回结果,可以使用 ARouter.getInstance().build() 方法构建路由请求时,调用 navigation() 方法的重载版本,传入一个 NavigationCallback 回调接口来处理返回结果,示例如下:
ARouter.getInstance().build("/main/selectActivity")
       .navigation(this, new NavigationCallback() {
            @Override
            public void onFound(Postcard postcard) {
                // 找到目标页面
            }

            @Override
            public void onLost(Postcard postcard) {
                // 找不到目标页面
            }

            @Override
            public void onArrival(Postcard postcard) {
                // 到达目标页面
            }

            @Override
            public void onInterrupt(Postcard postcard) {
                // 路由被拦截
            }
        });

高级功能

  • 拦截器****的使用
    • 可以定义路由拦截器,用于在路由跳转前进行一些全局的拦截和处理,如权限验证、登录检查等。创建一个类实现 IInterceptor 接口,并使用 @Interceptor 注解标记,然后在 init 方法中进行拦截逻辑的处理,示例如下:
@Interceptor(priority = 7)
public class LoginInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        if (isLoggedIn()) {
            callback.onContinue(postcard);
        } else {
            callback.onInterrupt(null);
            // 可以跳转到登录页面
            ARouter.getInstance().build("/login/loginActivity").navigation();
        }
    }

    @Override
    public void init(Context context) {
        // 初始化拦截器
    }

    private boolean isLoggedIn() {
        // 判断用户是否登录的逻辑
        return false;
    }
}
  • 动态注册路由
    • 除了使用注解定义路由,还可以通过代码动态注册路由,适用于一些需要根据运行时条件动态添加路由的场景。使用 ARouter.getInstance().addRoute() 方法来动态注册路由,示例如下:
ARouter.getInstance().addRoute("/dynamic/activity", DynamicActivity.class);
  • addRoute("/dynamic/activity", DynamicActivity.class)
    • 这是调用获取到的 ARouter 实例的 addRoute 方法,它接受两个参数:
    • 第一个参数 "/dynamic/activity" 是一个字符串类型的路由路径。这个路径就像是一个地址,当在应用的其他地方发起路由跳转,指定要跳转到这个路径对应的页面或者组件时,ARouter 就会依据这个路径找到对应的目标。路由路径的命名一般按照一定的规范,方便团队成员理解和维护,比如可以按照模块名、页面功能等来定义,例如这里的 dynamic 可能表示和动态相关的某个页面,路径名称在整个应用的路由体系里要保证唯一性,避免出现路由冲突的情况。
    • 第二个参数 DynamicActivity.class 是一个 Class 类型的参数,它指定了路由指向的目标 Activity 类(当然也可以是其他组件类,不过常见的是 Activity 用于页面跳转场景)。当通过 ARouter 发起的路由请求匹配到 "/dynamic/activity" 这个路径时,ARouter 就会实例化这个 DynamicActivity.class 对应的 Activity ,然后进行页面跳转等相关操作,就如同使用 @Route 注解标记 DynamicActivity 类并指定相同路径的效果一样。

ARouter里Activity之间的跳转

Activity与Activity里的跳转只需调用如下代码,build里的路径,就是我们用标注@Route path标注的路径:

ARouter.getInstance().build("/myapplication/MainActivity").navigation();

从上面代码我们发现。路径"/myapplication/MainActivity",得统一用个常量类管理起来,不然这样找起来,简直费事,如下:

public final class Constance {
    public static final String ACTIVITY_PATH_MAIN = "/myapplication/MainActivity";
}

这里除了路径跳转外,从他的参数类型中,还能看到有Uri跳转:

        Uri uri = Uri.parse(Constance.ACTIVITY_PATH_MAIN);
        ARouter.getInstance().build(uri).navigation();

带参数跳转 & 获取参数

带参数跳转

只需在后面.withString(),即可,里面包含了所有你想传的类型

ARouter.getInstance()
                .build("/myapplication/MainActivity")
                .withString("name", "lihang")
                .withInt("age", 27)
                .withSerializable("human", new Person("力哈", "11"))
                .navigation();

获取参数

在获取参数页面,注意这里有个大坑:

@Route(path = "/myapplication/SimpleActivity")
public class SimpleActivity extends BaseActivity {
    @Autowired()
    String name;
    @Autowired(name = "age")
    int mAge;
    //    @Autowired(name = "human")这里注意,如果是传Serializable,注解是得不到的。除非是Paceable
    Person mPerson;

    @Override
    public int getContentViewId() {
        return R.layout.activity_simple;
    }

    @Override
    public void progressLogic() {
//        mPerson = (Person) getIntent().getSerializableExtra("human");
    }
}

这里获取参数有3种方式:

  • 使用@Autowired标注后,下方的值是之间传值的key。
@Autowired()
String name;
  • 使用@Autowired标注后,name = key,name等于之间传值的key,下方参数名随便定
@Autowired(name = "age")
int mAge;
  • 带序列化这里有些特殊,如果是Parcelable类型,可以用上方的方法获取。但是如果是Serializable类型,却会发现,没办法获取到。但是这里用我们传统的getIntent也能获取到

我们发现Serializable序列化的对象为null,我们查看withSerializable方法发现其被装进了Bundle

public Postcard withSerializable(@Nullable String key, @Nullable Serializable value) {
        mBundle.putSerializable(key, value);        
        return this;    
}

因此换一种方法来取值,发现打印成功

mPerson = (Person) getIntent().getSerializableExtra("human");
携带无序列化对象的界面跳转

没有进行过序列化的对象也可以通过withObject对象进行传递,接收方式相同

NormalTest normalTest = new NormalTest();
normalTest.setName("normal");
ARouter.getInstance()
    .build("/app/login")
    .withObject("normalTest", normalTest)
    .navigation();

但是我们直接使用该方法运行会报错,分析源码发现该方法中用到了SerializationService

public Postcard withObject(@Nullable String key, @Nullable Object value) {
        serializationService = ARouter.getInstance().navigation(SerializationService.class);
        mBundle.putString(key, serializationService.object2Json(value));        
        return this;    
}

因此我们需要实现该服务

@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {
    private Gson gson;

    @Override
    public <T> T json2Object(String input, Class<T> clazz) {
        return gson.fromJson(input, clazz);
    }

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

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

    @Override
    public void init(Context context) {
        gson = new Gson();
    }
}

我们可以在里面定义所需的json解析器,再次运行成功打印该对象。那序列化的对象可以使用该方法传递吗?

TestParcelableBean objParcelableBean = new TestParcelableBean();
objParcelableBean.setName("objParcelable");

TestSerializableBean objSerializableBean = new TestSerializableBean();
objSerializableBean.setName("objSerializable");

NormalTest normalTest = new NormalTest();
normalTest.setName("normal");

ARouter.getInstance().build("/app/login")
        .withObject("objParcelableBean", objParcelableBean)
        .withObject("objSerializableBean", objSerializableBean)
        .withObject("normalTest", normalTest)
        .navigation();

//目标界面
@Autowired(name = "objParcelableBean")
TestParcelableBean objParcelableBean;
@Autowired(name = "objSerializableBean")
TestSerializableBean objSerializableBean;
@Autowired(name = "normalTest")
NormalTest normalTest;

Log.e(TAG, objParcelableBean + "");
Log.e(TAG, objSerializableBean + "");
Log.e(TAG, normalTest + "");

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们发现用 Parcelable 序列化的对象为空,分析build的编译文件

@Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    LoginActivity substitute = (LoginActivity)target;
    substitute.objParcelableBean = substitute.getIntent().getParcelableExtra("objParcelableBean");
    if (null != serializationService) {
      substitute.objSerializableBean = serializationService.parseObject(substitute.getIntent().getStringExtra("objSerializableBean"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestSerializableBean>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'objSerializableBean' in class 'LoginActivity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    if (null != serializationService) {
      substitute.normalTest = serializationService.parseObject(substitute.getIntent().getStringExtra("normalTest"), new com.alibaba.android.arouter.facade.model.TypeWrapper<NormalTest>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'normalTest' in class 'LoginActivity' , then you should implement 'SerializationService' to support object auto inject!");
    }
  }

我们可以看到唯独通过 Parcelable 方式序列化的对象没有使用SerializationService进行解析,而是直接从Bundle去取,但我们并不是通过withParcelable方法去设置的值,因此取得的数据为null。

小结:因此,为了方便我们的操作,没有序列化和使用 Serializable 序列化的对象使用 withObject 方法传递,使用 Parcelable 方式序列化的对象则采用 withParcelable 方法进行传递。

带动画跳转

之前我们要带动画跳转,要用到overridePendingTransition(int anim_in,int anim_out);这里也有提供,但是有个大坑哦:

ARouter.getInstance().build(Constance.ACTIVITY_PATH_SIMPLE).withTransition(R.anim.alpha_activity_in, R.anim.alpha_activity_out)                
//navigation里,一定要加上当前的Context,不然动画不起效                
.navigation(MainActivity.this, 99);

同时ARouter也提供了新版动画,但是需要api>= 16才能使用,效果还是很酷炫的:

public class SomeClass {

    private Button btn;

    public void someMethod() {
        if (Build.VERSION.SDK_INT >= 16) {
            ActivityOptionsCompat compat = ActivityOptionsCompat.makeScaleUpAnimation(
                    btn, 
                    btn.getWidth(), 
                    btn.getHeight(), 
                    0, 
                    0
            );
            Postcard postcard = ARouter.getInstance()
                   .build(Constant.ACTIVITY_PATH_SIMPLE)
                   .withOptionsCompat(compat)
                   .build();
            ARouter.getInstance().navigation(postcard);
        } else {
            ToastUtils.showToast("api < 16,不支持新版动画");
        }
    }
}

类似Activity里的startActivityForResult(Intent intent, int requestCode);

带requestCode跳转

也是在.navigation()里,只要带上requestCode就Ok了。然后onActivityResult和正常使用一样。

ARouter.getInstance().build(Constance.ACTIVITY_PATH_SIMPLE).navigation(Context context, int requestCode);

setResult返回界面,你没看错,就是这么复杂!

Postcard postcard = ARouter.getInstance().build(Constance.ACTIVITY_PATH_MAIN);
LogisticsCenter.completion(postcard);
Class<?> destination = postcard.getDestination();
Intent intent = new Intent(SimpleActivity.this, destination);
setResult(1, intent);
finish();

Fragment的使用

之间说了Activity和Fragment都需要用@Route标注加上path。 这里Fragment的使用和Activity一样,但唯一的区别是,Activity是跳转,而Fragment是实例化Fragmen的实例,如下:

//记得要强转一下,这里传值什么的和Activity的用法一样。
HomeFragment homeFragment = (HomeFragment)ARouter.getInstance().build(Constance.Fragment_PATH_SIMPLE).navigation();

拦截器的使用

我们先看一段拦截器的代码:

import com.alibaba.android.arouter.facade.Postcard;
import com.alibaba.android.arouter.facade.callback.InterceptorCallback;
import com.alibaba.android.arouter.facade.template.IInterceptor;
import android.content.Context;

import static com.example.yourpackage.Constant.ACTIVITY_PATH_SIMPLE;
import static com.example.yourpackage.LogUtils;

@Interceptor(priority = 1)
public class FirstInterceptor implements IInterceptor {

    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        if (postcard.getPath().equals(ACTIVITY_PATH_SIMPLE)) {
            LogUtils.i("ARouter拦截器", "FirstInterceptor 开始拦截 ======");
        }
        callback.onContinue(postcard);
    }

    @Override
    public void init(Context context) {
        LogUtils.i("ARouter拦截器", "first init");
    }
}

1、拦截器使用很奇特,只要在你代码里写上这个类,就起作用了。估计@Interceptor标注,apt帮我们干了不少事。

2、首先用@Interceptor标注,(priority = 1)优先级,越小,越先执行。

3、实现IInterceptor接口。实现2个方法:init(),process()。

4、init()方法,在项目启动时候首先执行,process()方法在Activity跳转时调用

5、如果我们要为拦截器加上一些帅选条件的话可以通过Postcard

6、注意,只要设置了拦截器,这里不调用callback.onContinue(postcard);就会卡在当前的拦截器内。当然跳转就卡在当前页面。

跳转时加监听,配合拦截器使用

直接上代码。在代码里注释

        ARouter.getInstance()
                .build(Constance.ACTIVITY_PATH_SIMPLE)
                .navigation(MainActivity.this, new NavigationCallback() {
                    @Override
                    public void onFound(Postcard postcard) {
                        //路由目标被发现时调用(Activity执行跳转代码,第一个执行)
                        //group 为路径的组。如果不自定义,会默认成path的/x/y的x
                        //group 可以自定义,如:@Route(path = Constance.ACTIVITY_PATH_MAIN,group = Constance.GROUP_FIRST)
                        //当然自定义组之后,跳转的时候要带上组名。
                        String group = postcard.getGroup();
                        //path 为全路径@Route(path = Constance.ACTIVITY_PATH_MAIN)
                        String path = postcard.getPath();
                        LogUtils.i("ARouter拦截器", "onFound ------>  group == " + group + "    " + "path == " + path);
                    }

                    @Override
                    public void onArrival(Postcard postcard) {
                        //路由到达后调用(注意这里是所有拦截器执行完之后才会调用)
                        String group = postcard.getGroup();
                        String path = postcard.getPath();
                        LogUtils.i("ARouter拦截器", "onArrival ------>  group == " + group + "    " + "path == " + path);
                    }

                    @Override
                    public void onLost(Postcard postcard) {
                        //路由被丢失时调用
                        LogUtils.i("ARouter拦截器", "onLost");
                    }

                    @Override
                    public void onInterrupt(Postcard postcard) {
                        //路由被拦截时调用
                        LogUtils.i("ARouter拦截器", "onInterrupt");

                    }

                });
oup == " + group + "    " + "path == " + path);
                    }

                    @Override
                    public void onLost(Postcard postcard) {
                        //路由被丢失时调用
                        LogUtils.i("ARouter拦截器", "onLost");
                    }

                    @Override
                    public void onInterrupt(Postcard postcard) {
                        //路由被拦截时调用
                        LogUtils.i("ARouter拦截器", "onInterrupt");

                    }

                });