ARouter使用&源码小结[版本1.5.2]

简介

官方介绍:一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
Git项目地址:https://github.com/alibaba/ARouter
官方中文ARouter详细使用教程:https://github.com/alibaba/ARouter/blob/master/README_CN.md

kotlin使用注意点:

  • 需要使用【kapt】引入注解处理器【com.alibaba:arouter-compiler:1.5.2】
    • kapt 'com.alibaba:arouter-compiler:1.5.2'
  • 需要引入kotlin反射库,传递自定义类型对象序列化时需要用到反射
    • implementation 'org.jetbrains.kotlin:kotlin-reflect:1.5.20'
  • 项目中同时包含kotlin和java类,需要同时配置两个版本的模块名
android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
        kapt {
            arguments {
                arg("AROUTER_MODULE_NAME", project.getName())
            }
        }
    }
}

其他注意点:

  • 跳转时携带自定义类型对象需要实现【SerializationService】接口,否则会报错(不知道官方为啥不提供默认实现?)
    • 例如:ARouter.getInstance().build(“/xxx/xxx”).withObject(“user”,User()).navigation()

      @Route(path = "/base/SerializationServiceImpl")
      class SerializationServiceImpl : SerializationService {
          override fun init(context: Context?) {
              
          }
      
          override fun <T : Any?> json2Object(input: String?, clazz: Class<T>?): T {
              return JSON.parseObject(input, clazz)
          }
      
          override fun object2Json(instance: Any?): String {
              return JSON.toJSONString(instance)
          }
      
          override fun <T : Any?> parseObject(input: String?, clazz: Type?): T {
              Log.d("ARouterTest", "SerializationServiceImpl parseObject")
              return JSON.parseObject(input, clazz)
          }
      }
      
    • 上面示例中用到的json库是【fastjson】

      • implementation 'com.alibaba:fastjson:1.2.69'
      • 开源库地址:https://github.com/alibaba/fastjson
    • 如果实现多个【SerializationService】接口会怎么样呢?
      经过试验,会以APT处理注解时后处理的类为准
      这点可以通过生成的中间类结合源码可以看出来:
      中间类里是以【SerializationService】类名作为key保存它的实现类,根据Map的特点我们知道,相同的key后面put的值会覆盖掉前面的;而源码中获取【SerializationService】就是通过该类的类名来获取的;

      public class ARouter$$Providers$$base implements IProviderGroup {
        @Override
        public void loadInto(Map<String, RouteMeta> providers) {
          providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, SerializationServiceImpl.class, "/base/SerializationServiceImpl", "base", null, -1, -2147483648));
          providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, SerializationServiceImpl2.class, "/base/SerializationServiceImpl2", "base", null, -1, -2147483648));
        }
      }
      
      public Postcard withObject(@Nullable String key, @Nullable Object value) {
              serializationService = ARouter.getInstance().navigation(SerializationService.class);
              mBundle.putString(key, serializationService.object2Json(value));
              return this;
          }
      
  • 编译时报错:Duplicate class androidx.versionedparcelable.VersionedParcelable found in moudle
    只要在project 的gradle.properties 内加入
    以下两句
    android.useAndroidX=true
    android.enableJetifier=true
    
  • 如果有多个Module,每隔Module都要配置注解处理器
id 'kotlin-kapt'
kapt "com.alibaba:arouter-compiler:$arouterVersion"

源码分析小结

整体实现方式

  1. 通过【Autowired】、【Interceptor】、【Route】等注解声明类/字段
  2. 注解处理器【arouter-compiler】在编译时分析源码搜集并整理所有注解了的类/字段,然后生成中间类保存这些类/字段的集合
  3. Application初始化时,执行ARouter.init()方法通过调用中间类的方法将相关数据加载到内存中
  4. 如果有使用【arouter-register】Gradle插件,则会在编译时期就把生成的中间类加载到代码中,省去了从Dex文件中查找中间类这个过程,也避免了应用加固后无法访问Dex文件问题
    class LogisticsCenter{
    	void init(Context context, ThreadPoolExecutor tpe){
    		//load by plugin first
    		loadRouterMap();
    		if (registerByPlugin) {
    			logger.info(TAG, "Load router map by arouter-auto-register plugin.");
    		} else {
    			... load from Dex file
    		}
    	}
    }
    public class LogisticsCenter {
        /**
         * arouter-auto-register plugin will generate code inside this method
         * call this method to register all Routers, Interceptors and Providers
         */
        private static void loadRouterMap() {
            registerByPlugin = false;
            // auto generate register code by gradle plugin: arouter-auto-register
            // looks like below:
            // registerRouteRoot(new ARouter..Root..modulejava());
            // registerRouteRoot(new ARouter..Root..modulekotlin());
        }
    }
    

页面跳转源码分析

示例代码:

  • @Route(path = "/settings/SettingsActivity", extras = 0) 这里的extras是Int类型,可以在拦截器中通过postcard.extra获取,用于判断哪些页面需要拦截
@Route(path = "/settings/SettingsActivity", extras = 0)
class SettingsActivity : AppCompatActivity() {
    @Autowired(name = "message")
    lateinit var mMessage: String

    @Autowired(name = "user")
    lateinit var mUser: User

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.settings_activity)
        ARouter.getInstance().inject(this)
        Toast.makeText(this, mUser.toString(), Toast.LENGTH_LONG).show()
    }
}
ARouter.getInstance().build("/settings/SettingsActivity")
        .withString("message", "hello, I am ARouter")
        .withObject("user", User("agile", 30))
        .navigation(this, object : NavCallback() {
            override fun onInterrupt(postcard: Postcard) {
                val exception = postcard.tag as Exception
                Log.d("ARouterTest", exception.message + "")
            }

            override fun onArrival(postcard: Postcard) {
                Log.d("ARouterTest", postcard.path + " arrived!")
            }
        })

实现过程:

  • 1.ARouter.build():根据路径(path)封装成Postcard对象并返回;

    • Postcard.java:包含所有要跳转相关路由信息
    • 可以通过实现PathReplaceService接口重写要跳转的路径,例如根据是否登录跳转不同页面
    • 实现类【_ARouter.java】
      protected Postcard build(String path, String group, Boolean afterReplace) {
         if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
              throw new HandlerException(Consts.TAG + "Parameter is invalid!");
          } else {
              if (!afterReplace) {
                  PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
                  if (null != pService) {
                      path = pService.forString(path);
                  }
              }
              return new Postcard(path, group);
          }
      }
      
  • 2.Postcard.withString/withObject:将要携带的参数保存到Postcard对象中

    • withString/withInt等基本数据类型:保存到Postcard对象中的Bundle变量中
      calss Postcard{
      	private Bundle mBundle;         // Data to transform
      
      	public Postcard withInt(@Nullable String key, int value) {
      		mBundle.putInt(key, value);
      		return this;
      	}
      		
      	public Postcard withString(@Nullable String key, @Nullable String value) {
      		mBundle.putString(key, value);
      		return this;
      	}
      }
      
    • withObject自定义类型对象:必须要提供实现SerializationService接口的类,否则无法成功跳转并抛出异常,对象转换成JSON字符串后存到Bundle中:
      java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String com.alibaba.android.arouter.facade.service.SerializationService.object2Json(java.lang.Object)' on a null object reference
      public Postcard withObject(@Nullable String key, @Nullable Object value) {
          serializationService = ARouter.getInstance().navigation(SerializationService.class);
          mBundle.putString(key, serializationService.object2Json(value));
          return this;
      }
      
  • 3.Postcard.navigation:执行跳转操作,实现类是【ARouter.java->_ARouter.java】

    public Object navigation(Context context, NavigationCallback callback) {
      return ARouter.getInstance().navigation(context, this, -1, callback);
    }
    
  • 4._ARouter.navigation:真正实现跳转逻辑

    • 1.判断有没有定义PretreatmentService服务,如果有则跳转前执行预处理操作
    • 2.LogisticsCenter.completion(postcard):根据要path查找要跳转的类,并将相关信息注入到Postcard对象中
      • 例如:要跳转的类信息、目标对象类型(Activity/Provider/Fragment/等)、优先级(priority)、携带的数据(extra)等
      • 如果跳转失败则优先回调NavigationCallback.onLost方法,其次回调全局DegradeService.onLost
      • 跳转成则回调NavigationCallback.onFound方法
    • 3.判断Postcard是否走的绿色通道,如果不是的话,则需要依次调用所有拦截器(IInterceptor)进行处理,如果被其中某个拦截器拦截了,则停止跳转并回调NavigationCallback.onInterrupt方法
      /**
      * Green channel, it will skip all of interceptors.
        *
        * @return this
        */
       public Postcard greenChannel() {
           this.greenChannel = true;
           return this;
       }
      
    • 4.根据Postcard要跳转的目标类型做不同处理:
      • Activity:将Postcard中要传递的参数交给Intent,并调用startActivity方法跳转
      • Provider:返回对应的Provider对象,该对象是在LogisticsCenter.completion(postcard)方法中通过反射创建,并回调它的init方法
      • Broadcast/ContentProvider/Fragment:通过反射创建对象并返回
      • method/service/其他:返回null
  • 关键源码:

    class _ARouter{
    	protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
            PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
            pretreatmentService.onPretreatment(context, postcard)
    		...
            try {
                LogisticsCenter.completion(postcard);
            } catch (NoRouteFoundException ex) {
                callback.onLost(postcard);
                    DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
    				degradeService.onLost(context, postcard);
                return null;
            }
            callback.onFound(postcard);
    		...
            if (!postcard.isGreenChannel()) {
                interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                    public void onContinue(Postcard postcard) {
                        _navigation(postcard, requestCode, callback);
                    }
    
                    public void onInterrupt(Throwable exception) {
                            callback.onInterrupt(postcard);
                    }
                });
            } else {
                return _navigation(postcard, requestCode, callback);
            }
            return null;
        }
    
        private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
            final Context currentContext = postcard.getContext();
    
            switch (postcard.getType()) {
                case ACTIVITY:
                    // Build intent
                    final Intent intent = new Intent(currentContext, postcard.getDestination());
                    intent.putExtras(postcard.getExtras());
    				...
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                    break;
                case PROVIDER:
                    return postcard.getProvider();
                case BOARDCAST:
                case CONTENT_PROVIDER:
                case FRAGMENT:
                    Class<?> fragmentMeta = postcard.getDestination();
    				...
    				Object instance = fragmentMeta.getConstructor().newInstance();
    				return instance;
                case METHOD:
                case SERVICE:
                default:
                    return null;
            }
            return null;
        }
    }
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值