初探Arouter框架

 

学习Arouter框架

 

最近公司需要重构项目,需要用到jecket page和Arouter框架。利用一晚上的时间将这两个东西快速地学习一下。

·首先是Arouter

ARouter官方地址:https://github.com/alibaba/ARouter/blob/master/README_CN.md

 

背景

传统的Activity页面跳转有两种方式,一种是显示跳转(通过Intent),一种是隐式跳转(通过scheme的方式匹配AndroidManifest中的规则)。他们都存在的缺点,

显示跳转的缺点:

因为会存在直接的类依赖的问题,导致耦合非常严重

 

隐式跳转的缺点:

出现规则集中式管理,导致协作变得非常困难。一般而言配置规则都是在Manifest中的,这就导致了扩展性较差

 

共同的缺点:

使用原生的路由方案会出现跳转过程无法控制的问题,因为一旦使用了StartActivity()就无法插手其中任何环节了,只能交给系统管理,这就导致了在跳转失败的情况下无法降级,而是会直接crash。

 

Arouter是什么?

一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦。

 

有什么优点?

简单来说就是独立性强,容错性强,兼容性强,非常适合团队模块化开发。

说复杂点就是下面这些了:

  1. 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
  2. 支持多模块工程使用
  3. 支持添加多个拦截器,自定义拦截顺序
  4. 支持依赖注入,可单独作为依赖注入框架使用
  5. 支持InstantRun
  6. 支持MultiDex(Google方案)
  7. 映射关系按组分类、多级管理,按需初始化
  8. 支持用户指定全局降级与局部降级策略
  9. 页面、拦截器、服务等组件均自动注册到框架
  10. 支持多种方式配置转场动画
  11. 支持获取Fragment
  12. 完全支持Kotlin以及混编(配置见文末 其他#5)
  13. 支持第三方 App 加固(使用 arouter-register 实现自动注册)
  14. 支持生成路由文档
  15. 提供 IDE 插件便捷的关联路径和目标类

如果你看到这里还不知道这个框架是拿来干嘛的,那么你可以狭义地把它认为是一种打开activity的框架,该框架提供了很多实用的功能。

 

用法

零、引入库

模块的build.gradle中

dependencies {
...
// 替换成最新版本, 需要注意的是api
    // 要与compiler匹配使用,均使用最新版可以保证兼容
    implementation 'com.alibaba:arouter-api:1.5.0'
    annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
    implementation 'com.alibaba:fastjson:1.2.58'
}

 

Application,别忘记在AndroidManifest中引用咯

public class MyApp extends Application {
    private boolean isDebug  = true;

    @Override
    public void onCreate() {
        super.onCreate();
        if(isDebug){//需要写在init前面,否者无效
            ARouter.openLog();
            ARouter.openDebug();
        }
        ARouter.init(this);
    }
}

 

一、简单跳转Activity

Test1Activity

// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/activity1")
public class Test1Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test1);
    }
}

 

MainActivity  

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void clickLis(View view) {
        switch (view.getId()){
            case R.id.btn_next:
                // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
                ARouter.getInstance().build("/test/activity1").navigation();
                break;
        }
    }
}

 

 

二、带参数跳转Activity

MainActivity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void clickLis(View view) {
        switch (view.getId()){
            case R.id.btn_next:
                // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
                ARouter.getInstance().build("/test/activity1").navigation();
                break;
            case R.id.button2:

                List<TestObj> objList = new ArrayList<>();
                objList.add(new TestObj("name1", 21));
                objList.add(new TestObj("name2", 22));


                Map<String, List<TestObj>> hashMap = new HashMap<>();
                hashMap.put("n3", objList);

                // 2. 跳转并携带参数
                ARouter.getInstance().build("/test/activity2")
                        .withLong("key1", 666L)
                        .withString("key2", "888")
                        .withObject("obj",  new TestObj("Gavin", 27))
                        .withObject("list",  objList)
                        .withObject("map",  hashMap)
                        .navigation();
                break;

        }
    }
}

 

Test2Activity

// 为每一个参数声明一个字段,并使用 @Autowired 标注
// URL中不能传递Parcelable类型数据,通过ARouter api可以传递Parcelable对象
@Route(path = "/test/activity2")
public class Test2Activity extends AppCompatActivity {

    TextView textview;

    @Autowired
     long key1;

    @Autowired(name = "key2")
     String name;

    // 支持解析自定义对象,URL中使用json传递
    // 如果需要传递自定义对象,新建一个类(并非自定义对象类),
    // 然后实现 SerializationService,并使用@Route注解标注(方便用户自行选择序列化方式),例如:JsonServiceImpl
    @Autowired
    TestObj obj;

    // 使用 withObject 传递 List 和 Map 的实现了
    // Serializable 接口的实现类(ArrayList/HashMap)
    // 的时候,接收该对象的地方不能标注具体的实现类类型
    // 应仅标注为 List 或 Map,否则会影响序列化中类型
    // 的判断, 其他类似情况需要同样处理
    @Autowired
    List<TestObj> list;

    @Autowired
    Map<String, List<TestObj>> map;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ARouter.getInstance().inject(this);
        setContentView(R.layout.activity_test2);

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("基本数据类型参数-->\n");
        stringBuilder.append("key1 = " + key1 + "  ,key2 = " + name + "\n");


        stringBuilder.append("获取自定义对象参数-->\n");
        stringBuilder.append(JSON.toJSONString(obj) + "\n");


        stringBuilder.append("获取List参数-->\n");
        stringBuilder.append(JSON.toJSONString(list)+"\n");

        stringBuilder.append("获取Map参数-->\n");
        stringBuilder.append(JSON.toJSONString(map));


        textview = findViewById(R.id.textview);
        textview.setText(stringBuilder.toString());

    }
}
然后我们成功地发现会报下面SerialiationService.obj的错误:

如果需要传递自定义对象,新建一个类(并非自定义对象类),然后实现 SerializationService,并使用@Route注解标注,否则会出现以下错误。而且,这个类不需要引用的,存在项目中就好。如下:

// 如果需要传递自定义对象,新建一个类(并非自定义对象类),然后实现 SerializationService,并使用@Route注解标注(方便用户自行选择序列化方式),例如:
@Route(path = "/yourservicegroupname/json1")
public class JsonServiceImpl implements SerializationService {
    @Override
    public <T> T json2Object(String input, Class<T> clazz) {
//        return null;
        return JSON.parseObject(input, clazz);
    }

    @Override
    public String object2Json(Object instance) {
        return JSON.toJSONString(instance);
    }

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

    @Override
    public void init(Context context) {

    }
}

加上上面的类即可,结果如下:

三、隐式跳转Activity

MainActivity


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void clickLis(View view) {
        switch (view.getId()){
            case R.id.btn_next:
                // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
                ARouter.getInstance().build("/test/activity1").navigation();
                break;
            case R.id.button2:

                List<TestObj> objList = new ArrayList<>();
                objList.add(new TestObj("name1", 21));
                objList.add(new TestObj("name2", 22));


                Map<String, List<TestObj>> hashMap = new HashMap<>();
                hashMap.put("n3", objList);

                // 2. 跳转并携带参数
                ARouter.getInstance().build("/test/activity2")
                        .withLong("key1", 666L)
                        .withString("key2", "888")
                        .withObject("obj",  new TestObj("Gavin", 27))
                        .withObject("list",  objList)
                        .withObject("map",  hashMap)
                        .navigation();
                break;
            case R.id.button3:
                //3. 通过URL跳转
                Uri uri = Uri.parse("arouter://m.aliyun.com/test/activity3");
                startActivity(new Intent("android.intent.action.VIEW",uri));
                break;
           
        }
    }
}

 

Test3Activity, 跟Test1Activity差不多,只是path不一样

@Route(path = "/test/activity3")
public class Test3Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test3);
    }
}

AndroidManifest文件中Test3Activity的标签

<activity android:name=".router.SchameFilterActivity">

            <!-- Schame -->
            <intent-filter>
                <data
                    android:host="m.aliyun.com"
                    android:scheme="arouter" />

                <action android:name="android.intent.action.VIEW" />

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

四、跳转页面拦截

只要在项目中存在即可

// 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查
// 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行

@Interceptor(priority = 8,name = "testinterceptor")
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {


        LogUtils.i("interceptor.process ... do something work");

        callback.onContinue(postcard);  // 处理完成,交还控制权
        // callback.onInterrupt(new RuntimeException("我觉得有点异常"));      // 觉得有问题,中断路由流程

        // 以上两种至少需要调用其中一种,否则不会继续路由
    }

    @Override
    public void init(Context context) {
// 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次

        LogUtils.i("TestInterceptor.init");
    }
}

 

五、跳转回调

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void clickLis(View view) {
        switch (view.getId()){
            ...
            case R.id.button4:
                // 设置跳转回调,可以获得跳转结果
                ARouter.getInstance().build("/test/activity4").navigation(this, new NavCallback() {
                    @Override
                    public void onFound(Postcard postcard) {
                        LogUtils.i("ARouter - 找到了");
                    }

                    @Override
                    public void onLost(Postcard postcard) {
                        LogUtils.i("ARouter - 找不到了");
                    }

                    @Override
                    public void onArrival(Postcard postcard) {
                        LogUtils.i("ARouter - 跳转完成");
                    }

                    @Override
                    public void onInterrupt(Postcard postcard) {
                        LogUtils.i("ARouter - 被拦截了");
                    }
                });
                break;
           
        }
    }
}

 

六、全局降级策略

使用系统自带的StartActivity()启动后就无法插手其中任何环节了,只能交给系统管理,这就导致了在跳转失败的情况下无法降级,而是会导致应用崩溃,这个给用户的感觉就不是很好。ARouter的降级策略就允许我们在自定义降级服务,在跳转失败的时候可以自行处理,比如可以加载一个专门提示页面丢失的Activity去处理这种错误情况。

有2种方式,

1、局部的,通过上面的回调方式,复写lost方法

2、全局的也就是即将要将的

// 实现DegradeService接口,并加上一个Path内容任意的注解即可
@Route(path = "/xxx/xxx")
public class DegradeServiceImpl implements DegradeService {
@Override
public void onLost(Context context, Postcard postcard) {
    // do something.
}

@Override
public void init(Context context) {

}
}

七、为目标页面声明更多信息

设置信息

// 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆"之类的
// 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关
// 剩下的可以自行发挥,通过字节操作可以标识32个开关,通过开关标记目标页面的一些属性,在拦截器中可以拿到这个标记进行业务逻辑判断
@Route(path = "/test/activity5", extras = Const.MUST_LOGIN)
public class Test5Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test5);

    }
}

 获取信息

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void clickLis(View view) {
        switch (view.getId()){
            ...
            case R.id.button5:
                ARouter.getInstance().build("/test/activity5").navigation(this, new NavCallback() {
                    @Override
                    public void onArrival(Postcard postcard) {

                    }

                    @Override
                    public void onFound(Postcard postcard) {
                        if (postcard.getExtra() == Const.MUST_LOGIN) {
                            Toast.makeText(MainActivity.this, "需要登陆", Toast.LENGTH_SHORT).show();
                        }
                        super.onFound(postcard);

                    }
                });
                break;
        }
    }
}

八、通过依赖注入解耦:服务管理

有2种方式,自动和手动,详情看注释

// 声明接口,其他组件通过接口来调用服务
public interface HelloService extends IProvider {
    String sayHello(String name);
}

 

// 实现接口
@Route(path = "/yourservicegroupname/hello",name = "测试服务")
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        LogUtils.i(name+" sayHello");
        return name+" sayHello";
    }

    @Override
    public void init(Context context) {

        LogUtils.i("HelloServiceImpl init ");
    }
}
public class TestService {
    @Autowired
    HelloService helloService;

    @Autowired(name = "/yourservicegroupname/hello")
    HelloService helloService2;

    HelloService helloService3;

    HelloService helloService4;

    public TestService() {
        ARouter.getInstance().inject(this);

    }

    public void testService() {
        // 1. (推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取
        // Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,不设置name属性,
        // 会默认使用byType的方式发现服务(当同一接口有多个实现的时候,必须使用byName的方式发现服务)
        helloService.sayHello("gavin");
        helloService2.sayHello("gavin2");


        // 2. 使用依赖查找的方式发现服务,主动去发现服务并使用,下面两种方式分别是byName和byType
        helloService3 = ARouter.getInstance().navigation(HelloService.class);
        helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();

        helloService3.sayHello("gavin3");
        helloService4.sayHello("gavin4");
    }
}

 

九、预处理服务

先于拦截器,全局的,从日志可以看出来!

// 实现 PretreatmentService 接口,并加上一个Path内容任意的注解即可
@Route(path = "/test/preService")
public class PretreatmentServiceImpl implements PretreatmentService {
    @Override
    public boolean onPretreatment(Context context, Postcard postcard) {
        // 跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可


        LogUtils.i("预处理服务");

        //需要预处理服务return false,就无法打开activity
        //不需要预处理服务return true
        return true;
    }

    @Override
    public void init(Context context) {

    }
}

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值