美文网首页
ARouter-Android路由中间件

ARouter-Android路由中间件

作者: 玄策 | 来源:发表于2018-02-12 15:30 被阅读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)读源码

    未完待续...


    参考资料

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

    相关文章

      网友评论

          本文标题:ARouter-Android路由中间件

          本文链接:https://www.haomeiwen.com/subject/ynvktftx.html