ARouter探索和使用分析

一、ARouter含义

简单说就是映射页面跳转关系的一个中间件,其中包含有一些跳转时的辅助功能(参数传递、动画、拦截器、服务绑定、碎片对象获取等)。

二、原理 / 流程

ARouter内部使用了注解处理工具APT(Annotation Processing Tool )技术,在代码编译期间对注解进行处理,生成JAVA文件,形成映射关系类。_ARouter中的init方法内,通过系统ClassUtils寻找到‘com.alibaba.android.arouter.routes’相关的类集合然后添加到一个IProviderGroup中的一个代理Map集合(key是path,value是RouteMeta参数体)中完成所有的初始化工作,具体生成的文件在 build/generated/source/kapt/下可以看到;在执行跳转时通过ARouter的build接收path,navigation接收配置的跳转模式设置,最终根据path找到对应的RouteMeta里的destination,也就是className,利用系统的startActivity/startActivityForResult完成跳转.

二、优点

  • 跨模块跳转,模块间解耦;
  • 拦截跳转过程,开发者可控;
  • 统一处理了跳转时各种参数和目标类检索出现的异常;
  • 可从外部URL映射到内部页面(js/html等与本地直接交互和界面切换);
  • 通过给任意一个类(fragment、boardcast、content_provider、object、any等)添加注解,都可以获取到该实例;
  • 通过给变量配置Autowired注解,可以自动获取并解析外界传进来的参数(配合ARouter.getInstance().inject(this)使用)

三、不足

  • 每个需要跳转或者构造的类都需要添加注解;
  • 当在使用从外部URL映射到内部页面时存在安全风险;
  • 增加项目编译时长.
  • 暂时无法跳转到系统界面(不能设置intent的type、action、data等属性,这是由于Arouter依赖注解所决定的)
  • 在遇到有回调的跳转(startActivityForResult)时,requestCode必须大于0,不能设置0或者小于0的值(_ARouter类中的_navigation方法在判断时根据requestCode > 0选择跳转的模式)

四、配置方式

Kotlin 或 Kotlin与Java混编的模式项目

apply plugin: 'kotlin-kapt'
kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
}
...
dependencies {
   ...
    implementation 'com.alibaba:arouter-api:x.x.x'
    kapt 'com.alibaba:arouter-compiler:x.x.x'
}

Java模式项目

android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

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

五、功能

  1. 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中(需要在manifest中给对应的Activity设置scheme和host以及设置属性);
  2. 支持多模块工程使用(在多个插件化和模块化的工程中只要保持路由地址一直可以任意跳转,不受Activity或Fragment等类名的影响);
  3. 支持添加多个拦截器,自定义拦截顺序;
  4. 支持InstantRun;
  5. 支持MultiDex(Google方案);
  6. 映射关系按组分类、多级管理,按需初始化(每个路由地址都可以根据具体的页面在注解时进行分组,方便在拦截时统一管理判断和维护,最主要的是方便在参数初始化时的灵活设置);
  7. 支持多种方式配置转场动画;
  8. 支持获取Fragment、object、any等实例;
  9. 完全支持Kotlin以及混编;
  10. 支持第三方 App 加固(使用 arouter-register 实现自动注册);
  11. 支持生成路由文档;
  12. 页面、拦截器、服务等组件均自动注册到框架.

六、使用方式

6.1、初始化SDK

// 这两行必须写在init之前,否则这些配置在init过程中将无效
if (isDebug()) {    
 	// 打印日志      
    ARouter.openLog();    
     // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
    ARouter.openDebug();  
}
 // 建议在Application中初始化
ARouter.init(mApplication);

6.2、基本用法

// 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
ARouter.getInstance().build("/test/activity").navigation();

// 2. 应用内携带参数跳转
ARouter.getInstance().build("/test/activity")
            .withLong("key1", 666L)
            .withString("key3", "888")
            .withObject("key4", new Test("Hxd", "hxjr"))
            .navigation();
            
//3. 跳转结果监听
ARouter.getInstance().build("/test/activity").navigation(this, object : NavCallback() {
                override fun onFound(postcard: Postcard?) {
                    ToastUtils.showText(this@MainActivity, "界面找到了,分组是${postcard?.group}")
                }

                override fun onLost(postcard: Postcard?) {
                    Log.d("ARouter", "找不到了")
                }

                override fun onArrival(postcard: Postcard) {
                    Log.d("ARouter", "跳转完了")
                }

                override fun onInterrupt(postcard: Postcard?) {
                    Log.d("ARouter", "被拦截了")
                }
            })

//4 .自定义路由分组  (默认每个路径的第一级就是该路径的分组,这里可以动态指定和修改(目标界面里注解))
ARouter.getInstance().build(path2TestActivity, "main").navigation(this, object : NavCallback() {
                override fun onArrival(postcard: Postcard) {
                    ToastUtils.showText(this@MainActivity, "跳转完了,分组是${postcard.group}")
                }

//5.跳转回调
ARouter.getInstance().build(path2ForResultActivity).navigation(this, 1)

//6. 跳转时跳过所有的拦截器
ARouter.getInstance().build(path2InterceptorTestActivity).greenChannel().navigation()

//7.增加转场动画
ARouter.getInstance().build(path2InterceptorTestActivity) .withOptionsCompat(
                        ActivityOptionsCompat.makeCustomAnimation(
                            this,
                            R.anim.slide_in_bottom, R.anim.slide_out_bottom
                        )
                    ).navigation()
//8.设置跳转的flags
ARouter.getInstance().build(path2InterceptorTestActivity).withFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).navigation()

//9.获取Fragment
val fragment = ARouter.getInstance().build(path2TestFragment).navigation() as Fragment
	//添加Fragment
    supportFragmentManager.beginTransaction().run {
          add(R.id.fragmentLayout, fragment)
          commit()
   }

//10、 使用自己的日志工具打印日志
ARouter.setLogger();

//11、 使用自己提供的线程池
ARouter.setExecutor();

6.3、跳转时添加拦截器

/**
 * @Function 自定义拦截器 (定义完成后不需要手动调用该对象,在执行跳转时只要不调用ARouter的greenChannel()方法,默认都会走该拦截器)
 *
 * 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查
 * 其次是在获取或构造某个对象时,可以统一对属性值进行修改
 *
 * 拦截器会在跳转之间执行,多个拦截器会按优先级(priority)顺序依次执行(值越小级别越高)
 *
 * 拦截器是在线程里执行的,为了逻辑的处理不阻塞主线程
 */

@Interceptor(priority = 1, name = "登陆测试用拦截器")
class CustomLoginInterceptor : IInterceptor {

    override fun process(postcard: Postcard?, callback: InterceptorCallback?) {
		 //只对指定的界面做处理,这里也可以判断分组
        if (postcard?.path == "/test/interceptor") {
            try {
                val text = "拦截到了InterceptorTestActivity的跳转\n优先级别是:${postcard.priority}\n已登陆"

                MainActivity.getThis.activity?.runOnUiThread {
                    ToastUtils.showText(MainActivity.getThis.activity!!, text)
                }
                // 处理完成,交还控制权
                callback?.onContinue(postcard)
            } catch (e: Exception) {
                e.toString()
                // 觉得有问题,中断路由流程
                callback?.onInterrupt(RuntimeException("我觉得有点异常"));
            }
        } else
            callback?.onContinue(postcard)
    }

    override fun init(context: Context?) {
        // 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
    }

}

6.4、与Html / JS等外部Uri互调

6.4.1

//先在本地WebView中或者设备自带的浏览器加载对应的网页
webView.loadUrl("file:///android_asset/schame-test.html")

//注意网页里的跳转链接写法(arouter是scheme,hxd是host,。可以根据自己需要定义和命名):
arouter://hxd/test/uri

6.4.2

/**
 * 1、先在本地WebView中或者设备自带的浏览器加载对应的网页
 */
webView.loadUrl("file:///android_asset/schame-test.html")


/**
 * 2、接着定义一个接受外部网页回调的中转Activity
 */
public class URLReceiveActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //对URI 数据分发
        Uri uri = getIntent().getData();
		ARouter.getInstance().build(uri).navigation();
		 finish();
    }
}

// 中转Activity在manifest中的注册 (这里的host和scheme要和网页里的链接保持一致)
 
            
                
                
                
                
                
            

        
/**
 * 3、URL Activity
 */
@Route(path = "/test/uri")
public class URLActivity1 extends AppCompatActivity implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_url);
    }
}

6.5、重写跳转URL路径

/**
* 
 * @Function 在没有或过滤掉拦截器的情况下实现跳转时路径重定向、保存跳转的历史数据等功能(定义完成后不需要手动调用该对象,在执行跳转时默认都会走该服务类))
 *
 * 这里的注解路径随便定义,只要不为空就行
 */
@Route(path = "/xxx/xxx")
class PathReplaceServiceImpl : PathReplaceService {
    override fun forString(path: String): String {
        //按照一定的规则处理之后返回处理后的结果
        // TODO 
        return path
    }
    override fun forUri(uri: Uri): Uri {
        // 按照一定的规则处理之后返回处理后的结果
        return uri
    }
    override fun init(context: Context?) {
    }
}

6.6、生成路由文档

// 更新 build.gradle, 添加参数 AROUTER_GENERATE_DOC = enable
// 生成的文档路径 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
            }
        }
    }
}

七、添加混淆规则(如果使用了Proguard)

-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}

# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider

# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider

Demo地址:https://github.com/yinzhengwei/ArouterDemo
ARouter地址:https://github.com/alibaba/ARouter

你可能感兴趣的:(开源框架,android)