ARouter-Android路由中间件

2018-02-12  本文已影响576人  玄策

目录


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>
    @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)分组

@Route(path = "/test/second" ,group = "test")
  public class SecondActivity extends AppCompatActivity {
}
ARouter.getInstance().build("test/second", "test") 

3.6)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 {
  ...
}
登录拦截
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解析

上一篇下一篇

猜你喜欢

热点阅读