ARouter-Android路由中间件


目录

  • 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)读源码

未完待续...


参考资料

ARouter-Github
阿里ARouter使用及源码解析
ARouter解析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容