Fork me on GitHub

安卓框架之ARouter的学习

ARouter路由框架实现组件化

1.简介

  • 路由是映射页面跳转关系的,当然它也包含跳转相关的一切功能。
  • 在组件化当中,路由可以充当各个模块联系的中间角色,解耦页面的依赖关系,实现模块的跳转。

2.使用

1.添加依赖

在每个需要跳转的Module配置gradle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
android {
...
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
}

dependencies {
...
//最新版本可在Github上面查看
implementation 'com.alibaba:arouter-api:1.2.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.1.2'
}

2.初始化

官方建议在Application中进行初始化,并且在Application结束时候,关闭销毁它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyApplication extends Application {
private boolean isDebugArouter = true;

@Override
public void onCreate() {
super.onCreate();
if(isDebugArouter){
// 打印日志
ARouter.openLog();
// 开启调试模式(如果在InstantRun(就是AndroidStudio2.0以后新增的一个可以减少很多编译时间的运行机制)模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
ARouter.openDebug();
}
// 初始化尽可能早,推荐在Application中初始化
ARouter.init(this);
}

@Override
public void onTerminate(){
super.onTerminate();
ARouter.getIntance().destory();
}
}

3.简单使用

  • 在Activity/Fragment类上面写上 Route path 注解,注意:这里的路径需要注意的是至少需要有两级,/xx/xx
1
2
3
4
5
6
7
8
9
@Route(path = "/app/Main2Activity")
public class Main2Activity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
}
  • 在需要跳转的界面
1
2
3
4
5
ARouter.getInstance()
.build("/app/Main2Activity")
.navigation();
//如果需要实现startActivityForResult()功能
.navigation(Activity mContext, int requestCode)

上面就是最简单的进行跳转的例子,只需一行代码便可以搞定。特别人性化的是,如果找不到对应路径的Activity,程序不会崩溃,只会打印出错误信息。(在debug模式下)

  • 需要带数值的跳转
  • ARouter中提供的api
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//基础类型
.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)

//数组类型
.withParcelableArrayList( String key, ArrayList<? extends Parcelable > value)
.withStringArrayList( String key, ArrayList<String> value)
.withIntegerArrayList( String key, ArrayList<Integer> value)
.withSparseParcelableArray( String key, SparseArray<? extends Parcelable> value)
.withCharSequenceArrayList( String key, ArrayList<CharSequence> value)
.withShortArray( String key, short[] value)
.withCharArray( String key, char[] value)
.withFloatArray( String key, float[] value)
.withCharSequenceArray( String key, CharSequence[] value)

//Bundle 类型
.with( Bundle bundle )

//Activity 跳转动画
.withTransition(int enterAnim, int exitAnim)

//其他类型
.withParcelable( String key, Parcelable value)
.withParcelableArray( String key, Parcelable[] value)
.withSerializable( String key, Serializable value)
.withByteArray( String key, byte[] value)
.withTransition(int enterAnim, int exitAnim)
  • 需要跳转页面
1
2
3
4
ARouter.getInstance()
.build("/app/Main2Activity)
.withString("test","测试")
.navigation();
  • 目标页面
1
2
3
4
5
6
//添加接收数据的代码
Intent intent = getIntent();
String test=intent.getStringExtra("test");
//另外一种方式,使用注解,即可获得它的值
@Autowired(name="test")
String mTest;
  • 带监听的跳转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ARouter.getInstance()
.build("/app/Main2Activity")
.navigation(this, new NavCallback() {
@Override
public void onFound(Postcard postcard) {
Log.e(TAG, "onArrival: 找到了 ");
}

@Override
public void onLost(Postcard postcard) {
Log.e(TAG, "onArrival: 找不到了 ");
}

@Override
public void onArrival(Postcard postcard) {
Log.e(TAG, "onArrival: 跳转完了 ");
}

@Override
public void onInterrupt(Postcard postcard) {
Log.e(TAG, "onArrival: 被拦截了 ");
}
});

通过传入一个监听器,就能监听ARouter的跳转过程了。

  • 跳转Fragment
1
2
3
4
5
6
7
8
9
10
11
@Route(path = "/app/TestFragment")
public class TestFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_test, container, false);
return view ;
}
}
//获取Fragment实例
Fragment fragment = (Fragment) ARouter.getInstance().build( "/app/TestFragment" ).navigation();
  • 使用Uri进行跳转
1
2
3
4
5
//需要跳转的页面
Uri uri = Uri.parse("/app/Main2Activity");
ARouter.getInstance()
.build(uri)
.navigation();

当parse传入的路径为完整的路径时候,同样可以跳转到Main2Activity。

  • 网页Url跳转到Activity
  • 首先定义一个Activity作为中转,网页的链接都跳到这个Activtity,然后再从这个Activity打开网页需要打开的Activity。
1
2
3
4
5
6
7
8
9
10
11
//对URI 数据分发
Uri uri = getIntent().getData();
ARouter.getInstance().build(uri).navigation(this, new NavCallback() {
@Override
public void onArrival(Postcard postcard) {
finish();
}
});
//manifest的intent-filter要添加如下
<data android:host="allenyu"
android:scheme="arouter" />
  • HTML片段
1
2
3
4
5
6
7
8
9
10
11
<h2>1:URL普通跳转</h2>

<p><a href="arouter://allenyu/com/URLActivity1">arouter://zhaoyanjun/com/URLActivity1 </a>
</p>

<h2>2:URL普通跳转携带参数</h2>

<p>
<a href="arouter://allenyu/com/URLActivity2?name=alex&age=18&boy=true&high=180&obj=%7b%22name%22%3a%22jack%22%2c%22id%22%3a666%7d">arouter://zhaoyanjun/test/URLActivity2?name=alex&age=18&boy=true&high=180
</a>
</p>

定义了两个URL,分别是带参数和不带参数,接下来讨论下接收带参数的目标Activity实现

  • 目标Actiity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Route(path = "/com/URLActivity2")
public class URLActivity2 extends AppCompatActivity{
@Autowired
String name;
@Autowired
int age;
@Autowired
boolean boy;
@Autowired
int high;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ARouter.getInstance().inject(this); //使用自动注入
setContentView(R.layout.activity_url2);

//解析参数
Bundle bundle = getIntent().getExtras();
String name1 = bundle.getString("name");
}
}
  • 其中如果使用自动注入,即Uri的参数的值会自动注入到变量里,注意:如果直接使用@Autowired注解没带参数,则变量名要和参数名一致,也可使用注解的name属性,赋值为参数名。
  • 如果不使用自动注入,则依旧要添加@Autowired注解,采用上述代码解析参数的形式解析参数。
  • 如果url带一串json的对象,此时我们要在该组件自定义对象接收,还需要自己实现SerializationService,举例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Author {
private String name;
private int age;
private String county;
}

@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {
@Override
public void init(Context context) {}
@Override
public <t> T json2Object(String text, Class<t> clazz) {
return JSON.parseObject(text, clazz);
}
@Override
public String object2Json(Object instance) {
return JSON.toJSONString(instance);
}
}

4. 进阶使用

4.1 拦截器

  • 自定义拦截器需要实现IInterceptor接口,并且添加@Interceptor的注解,其中priority为拦截器的优先级,值越小,优先级越高;然后实现pocess()和init()方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Interceptor(priority = 5)
public class Test1Interceptor implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
Log.e("testService", Test1Interceptor.class.getName() + " has process.");
//拦截跳转,进行一些处理
if (postcard.getPath().equals("/test/test1")) {
Log.e("testService", Test1Interceptor.class.getName() + " 进行了拦截处理!");
}
callback.onContinue(postcard);
}

@Override
public void init(Context context) {
Log.e("testService", Test1Interceptor.class.getName() + " has init.");
}
}
// 此处省略同样的原理建Test2Interceptor,把它的优先级设置为4;
  • 带回调的跳转
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ARouter.getInstance().build("/test/test1").navigation(this, new NavCallback() {
@Override
public void onFound(Postcard postcard) {
Log.e("testService", "找到了");
}

@Override
public void onLost(Postcard postcard) {
Log.e("testService", "找不到了");
}

@Override
public void onArrival(Postcard postcard) {
Log.e("testService", "跳转完了");
}

@Override
public void onInterrupt(Postcard postcard) {
Log.e("testService", "被拦截了");
}
});
  • 运行结果

  • 可以发现优先级越高,就越先执行。
  • 由跳转的执行顺序为,先执行回调函数的onFound(),之后是拦截器2的process(),拦截器1的process(),最后执行回调函数的onArrival()。
  • 在process()中通过postcard的属性值进行判断,然后进行拦截处理,处理成功调用callback.onContinue()方法继续往下执行,失败则调用callback.onInterrupt()方法中断跳转。其中,postcard包含了路由节点的各种信息。
  • 注意事项
  • 定义多个拦截器的时候, priority的值不能一样。
  • 在拦截器的process()方法中,如果对传入的 postcard对象设置了tag值,那么跳转会被当做拦截处理,通常来说,postcard的tag值会用来保存拦截处理过程中产生的异常对象。
1
2
postcard.setTag(new Object);
//此时跳转执行的是onInterrupt方法
  • 在拦截器的process()方法中,如果你即没有调用callback.onContinue(postcard)方法也没有调用callback.onInterrupt(exception)方法,那么不再执行后续的拦截器,需等待300s(默认值,可设置改变)的时间,才能抛出拦截器中断。
  • 拦截器的process()方法以及带跳转的回调中的onInterrupt(Postcard postcard)方法,均是在分线程中执行的,如果需要做一些页面的操作显示,必须在主线程中执行。

4.2 自定义分组

还记得之前跳转用的路径吗?/…/…,其中两个斜杠之间便是默认的组名,此情况是没自定义分组的情况。如何进行自定义分组,其实很简单。

1
2
3
4
@Route(path = "/com/CustomGroupActivity" , group = "customGroup"

//跳转时候
ARouter.getInstance().build("/com/CustomGroupActivity", "customGroup").navigation();

  • 之所以引进组的概念,是因为ARouter框架是分组管理,按需加载。在编译期框架扫描了所有的注册页面/服务/字段/拦截器等,那么很明显运行期不可能一股脑全部加载进来,这样就太不和谐了。所以就分组来管理,ARouter在初始化的时候只会一次性地加载所有的root结点,而不会加载任何一个Group结点,这样就会极大地降低初始化时加载结点的数量。比如某些Activity分成一组,组名就叫test,然后在第一次需要加载组内的某个页面时再将test这个组加载进来。

4.3 暴露服务

  • 先自定义一个接口继承IProvider
1
2
3
public interface IService extends IProvider {
void sayHello(Context context );
}
  • 定义一个实现类,并注解
1
2
3
4
5
6
7
8
9
10
11
12
13
@Route(path = "/service/hello", name = "测试服务")
public class MyService implements IService {

@Override
public void sayHello( Context context ) {
Toast.makeText( context , "hello", Toast.LENGTH_SHORT).show();
}

@Override
public void init(Context context) {

}
}
  • 具体使用(我们只需要知道接口,不需要关心接口的实现类,很好了实现了解耦)
1
2
3
4
5
6
7
//不需要知道接口的具体实现类,直接注解就可以了
@Autowired(name = "/service/hello")
IService service;
//直接注入
ARouter.getInstance().inject(this);
//调用接口
service.sayHello(this);
-------------本文结束感谢您的阅读-------------

本文标题:安卓框架之ARouter的学习

文章作者:AllenYu

发布时间:2018年11月25日 - 20:11

最后更新:2018年11月25日 - 20:11

原始链接:http://yuzeduan.github.io/2018/11/25/安卓框架之ARouter的学习/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。