ARouter-Android路由中间件
2018-02-12 本文已影响576人
玄策
目录
- 1)依赖和配置
- 2)初始化
- 3)路由操作
- 3.1)跳转并传参
- 3.2)跳转回调(startActivityForResult)
- 3.3)通过URL跳转
- 3.4)监听路由过程
- 3.5)分组
- 3.6)fragment路由
- 4)拦截器
- 5)降级策略
- 6)依赖注入服务(服务解耦)
- 7)读源码
1)依赖和配置
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
}
}
dependencies {
// 替换成最新版本, 需要注意的是api
// 要与compiler匹配使用,均使用最新版可以保证兼容
api 'com.alibaba:arouter-api:x.x.x'
annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
...
}
//混淆规则
-keep public class com.alibaba.android.arouter.routes.**{*;}
-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
2)初始化
~/MainApplication.java
if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(this); // 尽可能早,推荐在Application中初始化
3)路由操作
工程结构3.1)跳转并传参
应用内跳转
ARouter.getInstance().build("/test/second").navigation();
//这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/second")
public class SecondActivity extends AppCompatActivity {
...
}
跳转并传参
参数类型 | 基本类型 |
---|---|
.withString( String key, String value ) | |
.withBoolean( String key, boolean value) | |
.withChar( String key, char value ) | |
.withShort( String key, short value) | |
.withInt( String key, int value) | |
.withLong( String key, long value) | |
.withDouble( String key, double value) | |
.withByte( String key, byte value) | |
.withFloat( String key, float value) | |
.withCharSequence( String key, CharSequence value) |
参数类型 | 数组类型 |
---|---|
.withParcelableArray(String key, Parcelable[] value) | |
.withParcelableArrayList( String key, ArrayList<? extends Parcelable > value) | |
.withSparseParcelableArray(String key, SparseArray<? extends Parcelable> value) | |
.withStringArrayList( String key, ArrayList<String> value) | |
.withIntegerArrayList( String key, ArrayList<Integer> value) | |
.withCharSequenceArrayList( String key, ArrayList<CharSequence> value) | |
. withByteArray(String key, byte[] value) | |
.withShortArray( String key, short[] value) | |
.withCharArray( String key, char[] value) | |
.withFloatArray( String key, float[] value) | |
.withCharSequenceArray( String key, CharSequence[] value) |
参数类型 | 其他类型 |
---|---|
.with( Bundle value ) | |
.withBundle(String key, Bundle value ) | |
.withObject(String key, Object value ) | |
.withParcelable(String key,Parcelable value) | |
.withSerializable(String key, Serializable value) |
ARouter.getInstance()
.build("/test/second")
.withString("key1","我是传递的String参数")
.withObject("key2",new UserBean("我是对象的name参数","我是对象的age参数"))
.withParcelable("key3",new TestParcelable("我是Parcelable的name",1))
.navigation();
Postcard
@Route(path = "/test/second")
public class SecondActivity extends AppCompatActivity {
@Autowired
//或自定义名称 @Autowired(name = "xxx")
public String key1;
@Autowired
public UserBean key2;
@Autowired
public TestParcelable key3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
//inject来注入@Autowired注解的字段
ARouter.getInstance().inject(this);
TextView txt = findViewById(R.id.txt);
txt.setText("key1= "+key1+" , key2= "+(key2!=null?key2.getName():"未获取值")+" , key3= "+(key3!=null?key3.getName():"未获取值"));
}
}
针对自定义对象的传递,
-可以采用withParcelable,在目标Activity中直接通过注入的方式获取对象即可。
-还有一种方式是withObject,对于withObject,ARouter会将其转换为json字符串,所以在目标Activity中获取的时候,可以通过 @Autowired public UserBean key2;注解来注入直接使用,但是前提是实现ARouter的SerializationService接口!!!。
-关于自定义服务将在 -6)依赖注入服务(服务解耦)中说明
ARouter定义了一个服务接口SerializationService.java
SerializationService.java
我们可以定义一个自定义服务实现此接口
@Route(path = "/service/json")
//因为实现了ARouter的SerializationService接口,我们自定义的对象即可不用写代码序列化而直接使用
public class JsonServiceImpl implements SerializationService {
@Override
public <T> T json2Object(String input, Class<T> clazz) {
return null;
}
@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即可使用注解的方式注入一个普通对象
@Autowired
public UserBean key2;
3.2)跳转回调(startActivityForResult)
可以使用如下方法
navigation(Activity mContext, int requestCode)
ARouter.getInstance()
.build("/com/second")
.navigation( this , 100 );
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode){
case 100:
if (resultCode == RESULT_OK){
Toast.makeText(this,"收到onActivityResult",Toast.LENGTH_SHORT).show();
}
break;
}
}
3.3)通过URL跳转
通过URL跳转// 新建一个Activity用于监听Schame事件,之后直接把url传递给ARouter即可
public class SchameFilterActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getIntent().getData();
ARouter.getInstance().build(uri).navigation();
finish();
}
}
~/AndroidManifest.xml
<activity android:name=".filter.SchameFilterActivity">
<intent-filter>
<data
android:host="app"
android:scheme="jsksy" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
</activity>
~/调试用html
注意URL中传递的JSON数据要转码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<br/>
<!-- <a href="[scheme]://[host]/[path]?[query]">启动应用程序</a> -->
<a href="jsksy://app/test/second?key1=tgf&key2=%7b%22name%22%3a%22我是对象的name参数%22%2c%22age%22%3a%22我是对象的age参数%22%7d">jsksy://app/test/second?key1=tgf&key2={"name":"我是对象的name参数","age":"我是对象的age参数"}</a><br/>
<!-- <a href="jsksy://app/GK_Home/tgf/16">tgftgf</a><br/> -->
<a id="url_addr" href="">jszk://app//view/point/pointsearch</a><br/>
<script type="text/javascript">
/*
* 智能机浏览器版本信息:
*/
var browser={
versions:function(){
var u = navigator.userAgent, app = navigator.appVersion;
return {//移动终端浏览器版本信息
trident: u.indexOf('Trident') > -1, //IE内核
presto: u.indexOf('Presto') > -1, //opera内核
webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/)||!!u.match(/AppleWebKit/), //是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者QQ HD浏览器
iPad: u.indexOf('iPad') > -1, //是否iPad
webApp: u.indexOf('Safari') == -1 //是否web应该程序,没有头部与底部
};
}(),
language:(navigator.browserLanguage || navigator.language).toLowerCase()
}
if (browser.versions.mobile && browser.versions.android) {
document.getElementById('url_addr').href = "jszk://app/view/school/schooldetail?uCode=1101";
}else if(browser.versions.mobile && browser.versions.ios){
document.getElementById('url_addr').href = "jszk://app/view/school/schooldetail/:1101";
}
document.writeln("语言版本: "+browser.language);
document.writeln(" 是否为移动终端: "+browser.versions.mobile);
document.writeln(" ios终端: "+browser.versions.ios);
document.writeln(" android终端: "+browser.versions.android);
document.writeln(" 是否为iPhone: "+browser.versions.iPhone);
document.writeln(" 是否iPad: "+browser.versions.iPad);
document.writeln(navigator.userAgent);
</script>
</html>
- URL中不能传递Parcelable类型数据,JSON可以通过ARouter api传递Parcelable对象
@Autowired
public UserBean key2; //可以接收URL中的json!
@Autowired
public TestParcelable key3; //不可以接收URL中的json!
3.4)监听路由过程
ARouter.getInstance()
.build("/test/second")
.withString("key1","我是传递的String参数")
.withObject("key2",new UserBean("我是对象的name参数","我是对象的age参数"))
.withParcelable("key3",new TestParcelable("我是Parcelable的name",1))
.navigation(this, new NavCallback() {
@Override
public void onFound(Postcard postcard) {
Logger.d("路由被目标发现");
super.onFound(postcard);
}
@Override
public void onInterrupt(Postcard postcard) {
Logger.d("路由被拦截");
super.onInterrupt(postcard);
}
@Override
public void onArrival(Postcard postcard) {
Logger.d("路由到达");
}
@Override
public void onLost(Postcard postcard) {
Logger.d("路由丢失");
super.onLost(postcard);
}
});
3.5)分组
- SDK中针对所有的路径(/test/1 /test/2)进行分组,分组只有在分组中的某一个路径第一次被访问的时候,该分组才会被初始化。所以使用分组来管理,ARouter在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,然后在第一次需要加载组内的某个页面时再将test这个组加载进来
- 可以通过 @Route 的group注解主动指定分组,否则使用路径中第一段字符串(/*/)作为分组
@Route(path = "/test/second" ,group = "test")
public class SecondActivity extends AppCompatActivity {
}
- 注意:一旦主动指定分组之后,应用内路由需要使用 ARouter.getInstance().build(path, group) 进行跳转,手动指定分组,否则无法找到
ARouter.getInstance().build("test/second", "test")
-
但是最新api手动指定分组已经过时语法
手动指定分组已经过时语法
3.6)fragment路由
- 创建fragment
@Route(path = "/test/fragment")
public class TestFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_test,container,false);
return view;
}
}
- 调用
Fragment fragment = (Fragment) ARouter.getInstance().build( "/test/fragment" ).navigation();
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frame_layout,fragment);
ft.commit();
4)拦截器
@Interceptor(priority = 1, name = "测试用拦截器")
public class TestInterceptor implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
Logger.d(postcard.getPath()); //打印:/test/second
Logger.d(postcard.getGroup()); //打印:test
Logger.d(postcard.getExtra()); //打印:1
Logger.d(postcard.getExtras()); //打印:Bundle[{key1=我是传递的String参数, key2={"age":"我是对象的age参数","name":"我是对象的name参数"}, key3=com.tgf.studyarouter.bean.TestParcelable@52254f}]
// if ("/test/second".equals(postcard.getPath())) //可以对单个路由使用
//也可以使用extras 属性进行标识
if (postcard.getExtra() == 1){
callback.onInterrupt(null);
ARouter.getInstance()
.build("/test/login")
.navigation();
}else
{
callback.onContinue(postcard); // 处理完成,交还控制权
}
// callback.onContinue(postcard); // 处理完成,交还控制权
// callback.onInterrupt(new RuntimeException("我觉得有点异常")); // 觉得有问题,中断路由流程
// **以上两种至少需要调用其中一种,否则不会继续路由**
}
@Override
public void init(Context context) {
// 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
}
}
postcard
// 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆"之类的
// 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关
// 剩下的可以自行发挥,通过字节操作可以标识32个开关,通过开关标记目标页面的一些属性,在拦截器中可以拿到这个标记进行业务逻辑判断
@Route(path = "/test/second" ,extras = 1)
public class SecondActivity extends AppCompatActivity {
...
}
登录拦截
- 可使用绿色通道(跳过所有的拦截器) greenChannel()
ARouter.getInstance()
.build("/test/second")
.greenChannel()
.navigation();
5)降级策略
ARouter定义了服务接口DegradeService.java,能让我们在route lost的时候,做点事儿。
DegradeService.java
我们自定义一个接口去实现DegradeService,当route丢失的时候,我们让路由进入首页。
// 自定义全局降级策略
// 实现DegradeService接口,并加上一个Path内容任意的注解即可
// 注意不要在 navigationnew NavCallback()
@Route(path = "/service/degrade")
public class DegradeServiceImpl implements DegradeService {
@Override
public void onLost(Context context, Postcard postcard) {
//路由进入首页
ARouter.getInstance()
.build("/test/first")
.navigation();
}
@Override
public void init(Context context) {
Logger.d("DegradeServiceImpl - init");
}
}
6)依赖注入服务(服务解耦)
可以通过依赖注入解耦服务,有点类似mvp中的model,可通过此方式将所有服务按类别抽离。
-暴露服务
//声明接口,继承IProvider,其他组件通过接口来调用服务
public interface HelloService extends IProvider {
void sayHello(String str);
}
// 实现接口
@Route(path = "/service/hello", name = "测试服务")
public class HelloServiceImpl implements HelloService {
private Context mContext;
@Override
public void sayHello(String str) {
Toast.makeText(mContext,"hello"+str,Toast.LENGTH_SHORT).show();
}
@Override
public void init(Context context) {
mContext = context;
}
}
-发现服务
@Route(path = "/test/login")
public class LoginActivity extends AppCompatActivity {
@Autowired(name = "/service/hello")
HelloService helloService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
//第1种方式(推荐): 通过@Autowired依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取
//Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,不设置name属性,会默认使用byType的方式发现服务(当同一接口有多个实现的时候,必须使用byName的方式发现服务)
ARouter.getInstance().inject(this);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//第2种方式 :通过依赖查找的方式 ,可不用inject 和 Autowired
// ((HelloService)ARouter.getInstance().build("/service/hello")
// .navigation())
// .sayHello("涂高峰");
//第3种方式 :通过依赖查找的方式,可不用inject 和 Autowired
// ARouter.getInstance().navigation(HelloService.class).sayHello("涂高峰");
helloService.sayHello("涂高峰");
}
});
}
}
7)读源码
未完待续...