ARouter-Android路由中間件


目錄

  • 1)依賴和配置
  • 2)初始化
  • 3)路由操作
    • 3.1)跳轉(zhuǎn)并傳參
    • 3.2)跳轉(zhuǎn)回調(diào)(startActivityForResult)
    • 3.3)通過URL跳轉(zhuǎn)
    • 3.4)監(jiān)聽路由過程
    • 3.5)分組
    • 3.6)fragment路由
  • 4)攔截器
  • 5)降級策略
  • 6)依賴注入服務(wù)(服務(wù)解耦)
  • 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'
    ...
}
//混淆規(guī)則
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}

# 如果使用了 byType 的方式獲取 Service,需添加下面規(guī)則,保護(hù)接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider

# 如果使用了 單類注入,即不定義接口實(shí)現(xiàn) IProvider,需添加下面規(guī)則,保護(hù)實(shí)現(xiàn)
-keep class * implements com.alibaba.android.arouter.facade.template.IProvider

2)初始化

~/MainApplication.java

        if (BuildConfig.DEBUG) {           // 這兩行必須寫在init之前,否則這些配置在init過程中將無效
            ARouter.openLog();     // 打印日志
            ARouter.openDebug();   // 開啟調(diào)試模式(如果在InstantRun模式下運(yùn)行,必須開啟調(diào)試模式!線上版本需要關(guān)閉,否則有安全風(fēng)險(xiǎn))
        }
        ARouter.init(this); // 盡可能早,推薦在Application中初始化

3)路由操作

工程結(jié)構(gòu)

3.1)跳轉(zhuǎn)并傳參

應(yīng)用內(nèi)跳轉(zhuǎn)

ARouter.getInstance().build("/test/second").navigation(); 
//這里的路徑需要注意的是至少需要有兩級,/xx/xx
@Route(path = "/test/second")
public class SecondActivity extends AppCompatActivity {
...
}

跳轉(zhuǎn)并傳參

參數(shù)類型 基本類型
.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)
參數(shù)類型 數(shù)組類型
.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)
參數(shù)類型 其他類型
.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參數(shù)")
  .withObject("key2",new UserBean("我是對象的name參數(shù)","我是對象的age參數(shù)"))
  .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,在目標(biāo)Activity中直接通過注入的方式獲取對象即可。
-還有一種方式是withObject,對于withObject,ARouter會(huì)將其轉(zhuǎn)換為json字符串,所以在目標(biāo)Activity中獲取的時(shí)候,可以通過 @Autowired public UserBean key2;注解來注入直接使用,但是前提是實(shí)現(xiàn)ARouter的SerializationService接口!!!。
-關(guān)于自定義服務(wù)將在 -6)依賴注入服務(wù)(服務(wù)解耦)中說明

ARouter定義了一個(gè)服務(wù)接口SerializationService.java


SerializationService.java

我們可以定義一個(gè)自定義服務(wù)實(shí)現(xiàn)此接口

@Route(path = "/service/json")
//因?yàn)閷?shí)現(xiàn)了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) {

    }
}

這樣即可在目標(biāo)Activity即可使用注解的方式注入一個(gè)普通對象

    @Autowired
    public UserBean key2;


3.2)跳轉(zhuǎn)回調(diào)(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跳轉(zhuǎn)

通過URL跳轉(zhuǎn)
// 新建一個(gè)Activity用于監(jiān)聽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>

~/調(diào)試用html
注意URL中傳遞的JSON數(shù)據(jù)要轉(zhuǎn)碼

<!DOCTYPE html>
    <html>
    
        <head>
            <meta charset="UTF-8">
            <title></title>
        </head>
    
        <body>
            <br/>
            <!-- <a href="[scheme]://[host]/[path]?[query]">啟動(dòng)應(yīng)用程序</a> -->
            <a href="jsksy://app/test/second?key1=tgf&key2=%7b%22name%22%3a%22我是對象的name參數(shù)%22%2c%22age%22%3a%22我是對象的age參數(shù)%22%7d">jsksy://app/test/second?key1=tgf&key2={"name":"我是對象的name參數(shù)","age":"我是對象的age參數(shù)"}</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"> 
                /* 
                * 智能機(jī)瀏覽器版本信息: 
                */ 
                var browser={ 
                versions:function(){ 
                var u = navigator.userAgent, app = navigator.appVersion; 
                    return {//移動(dòng)終端瀏覽器版本信息 
                        trident: u.indexOf('Trident') > -1, //IE內(nèi)核 
                        presto: u.indexOf('Presto') > -1, //opera內(nèi)核 
                        webKit: u.indexOf('AppleWebKit') > -1, //蘋果、谷歌內(nèi)核 
                        gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐內(nèi)核 
                        mobile: !!u.match(/AppleWebKit.*Mobile.*/)||!!u.match(/AppleWebKit/), //是否為移動(dòng)終端 
                        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應(yīng)該程序,沒有頭部與底部 
                    }; 
                }(), 
                    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(" 是否為移動(dòng)終端: "+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類型數(shù)據(jù),JSON可以通過ARouter api傳遞Parcelable對象
    @Autowired
    public UserBean key2; //可以接收URL中的json!

    @Autowired
    public TestParcelable key3; //不可以接收URL中的json!

3.4)監(jiān)聽路由過程

ARouter.getInstance()
  .build("/test/second")
  .withString("key1","我是傳遞的String參數(shù)")
  .withObject("key2",new UserBean("我是對象的name參數(shù)","我是對象的age參數(shù)"))
  .withParcelable("key3",new TestParcelable("我是Parcelable的name",1))
  .navigation(this, new NavCallback() {
    @Override
    public void onFound(Postcard postcard) {
      Logger.d("路由被目標(biāo)發(fā)現(xiàn)");
      super.onFound(postcard);
    }
    @Override
    public void onInterrupt(Postcard postcard) {
      Logger.d("路由被攔截");
      super.onInterrupt(postcard);
    }
    @Override
    public void onArrival(Postcard postcard) {
      Logger.d("路由到達(dá)");
    }
    @Override
    public void onLost(Postcard postcard) {
      Logger.d("路由丟失");
      super.onLost(postcard);
    }
 });

3.5)分組

  • SDK中針對所有的路徑(/test/1 /test/2)進(jìn)行分組,分組只有在分組中的某一個(gè)路徑第一次被訪問的時(shí)候,該分組才會(huì)被初始化。所以使用分組來管理,ARouter在初始化的時(shí)候只會(huì)一次性地加載所有的root結(jié)點(diǎn),而不會(huì)加載任何一個(gè)Group結(jié)點(diǎn),然后在第一次需要加載組內(nèi)的某個(gè)頁面時(shí)再將test這個(gè)組加載進(jìn)來
  • 可以通過 @Route 的group注解主動(dòng)指定分組,否則使用路徑中第一段字符串(/*/)作為分組
@Route(path = "/test/second" ,group = "test")
  public class SecondActivity extends AppCompatActivity {
}
  • 注意:一旦主動(dòng)指定分組之后,應(yīng)用內(nèi)路由需要使用 ARouter.getInstance().build(path, group) 進(jìn)行跳轉(zhuǎn),手動(dòng)指定分組,否則無法找到
ARouter.getInstance().build("test/second", "test") 
  • 但是最新api手動(dòng)指定分組已經(jīng)過時(shí)語法


    手動(dòng)指定分組已經(jīng)過時(shí)語法

3.6)fragment路由

  • 創(chuàng)建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;
    }
}
  • 調(diào)用
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());  //打?。?
        Logger.d(postcard.getExtras()); //打?。築undle[{key1=我是傳遞的String參數(shù), key2={"age":"我是對象的age參數(shù)","name":"我是對象的name參數(shù)"}, key3=com.tgf.studyarouter.bean.TestParcelable@52254f}]


//        if ("/test/second".equals(postcard.getPath())) //可以對單個(gè)路由使用
        //也可以使用extras 屬性進(jìn)行標(biāo)識(shí)
        if (postcard.getExtra() == 1){
            callback.onInterrupt(null);
            ARouter.getInstance()
                    .build("/test/login")
                    .navigation();
        }else
        {
            callback.onContinue(postcard);  // 處理完成,交還控制權(quán)
        }
//        callback.onContinue(postcard);  // 處理完成,交還控制權(quán)
//         callback.onInterrupt(new RuntimeException("我覺得有點(diǎn)異常"));  // 覺得有問題,中斷路由流程
        // **以上兩種至少需要調(diào)用其中一種,否則不會(huì)繼續(xù)路由**
    }

    @Override
    public void init(Context context) {
        // 攔截器的初始化,會(huì)在sdk初始化的時(shí)候調(diào)用該方法,僅會(huì)調(diào)用一次
    }
}
postcard
// 我們經(jīng)常需要在目標(biāo)頁面中配置一些屬性,比方說"是否需要登陸"之類的
// 可以通過 Route 注解中的 extras 屬性進(jìn)行擴(kuò)展,這個(gè)屬性是一個(gè) int值,換句話說,單個(gè)int有4字節(jié),也就是32位,可以配置32個(gè)開關(guān)
// 剩下的可以自行發(fā)揮,通過字節(jié)操作可以標(biāo)識(shí)32個(gè)開關(guān),通過開關(guān)標(biāo)記目標(biāo)頁面的一些屬性,在攔截器中可以拿到這個(gè)標(biāo)記進(jìn)行業(yè)務(wù)邏輯判斷
@Route(path = "/test/second" ,extras = 1)
public class SecondActivity extends AppCompatActivity {
  ...
}
登錄攔截
  • 可使用綠色通道(跳過所有的攔截器) greenChannel()
ARouter.getInstance()
  .build("/test/second")
  .greenChannel()
  .navigation();

5)降級策略

ARouter定義了服務(wù)接口DegradeService.java,能讓我們在route lost的時(shí)候,做點(diǎn)事兒。


DegradeService.java

我們自定義一個(gè)接口去實(shí)現(xiàn)DegradeService,當(dāng)route丟失的時(shí)候,我們讓路由進(jìn)入首頁。

// 自定義全局降級策略
// 實(shí)現(xiàn)DegradeService接口,并加上一個(gè)Path內(nèi)容任意的注解即可
// 注意不要在 navigationnew NavCallback()
@Route(path = "/service/degrade")
public class DegradeServiceImpl implements DegradeService {
    @Override
    public void onLost(Context context, Postcard postcard) {
        //路由進(jìn)入首頁
        ARouter.getInstance()
                .build("/test/first")
                .navigation();
    }

    @Override
    public void init(Context context) {
        Logger.d("DegradeServiceImpl - init");
    }
}

6)依賴注入服務(wù)(服務(wù)解耦)

可以通過依賴注入解耦服務(wù),有點(diǎn)類似mvp中的model,可通過此方式將所有服務(wù)按類別抽離。

-暴露服務(wù)

//聲明接口,繼承IProvider,其他組件通過接口來調(diào)用服務(wù)
public interface HelloService extends IProvider {
    void sayHello(String str);
}

// 實(shí)現(xiàn)接口
@Route(path = "/service/hello", name = "測試服務(wù)")
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;
    }
}

-發(fā)現(xiàn)服務(wù)

@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依賴注入的方式發(fā)現(xiàn)服務(wù),通過注解標(biāo)注字段,即可使用,無需主動(dòng)獲取
        //Autowired注解中標(biāo)注name之后,將會(huì)使用byName的方式注入對應(yīng)的字段,不設(shè)置name屬性,會(huì)默認(rèn)使用byType的方式發(fā)現(xiàn)服務(wù)(當(dāng)同一接口有多個(gè)實(shí)現(xiàn)的時(shí)候,必須使用byName的方式發(fā)現(xiàn)服務(wù))
        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)讀源碼

未完待續(xù)...


參考資料

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容