Android 路由框架ARouter最佳實踐

一:什麼是路由?

說簡單點就是映射頁面跳轉關係的,固然它也包含跳轉相關的一切功能。php

二:爲何須要路由

Android系統已經給咱們提供了api來作頁面跳轉,好比startActivity,爲何還須要路由框架呢?咱們來簡單分析下路由框架存在的意義:css

  • 在一些複雜的業務場景下(好比電商),靈活性比較強,不少功能都是運營人員動態配置的,好比下發一個活動頁面,咱們事先並不知道具體的目標頁面,但若是事先作了約定,提早作好頁面映射,即可以自由配置。java

  • 隨着業務量的增加,客戶端必然隨之膨脹,開發人員的工做量愈來愈大,好比64K問題,好比協做開發問題。App通常都會走向組件化、插件化的道路,而組件化、插件化的前提就是解耦,那麼咱們首先要作的就是解耦頁面之間的依賴關係。android

  • 簡化代碼。數行跳轉代碼精簡成一行代碼。git

  • 其餘…github

三:ARouter 簡介

是ARouter是阿里巴巴開源的Android平臺中對頁面、服務提供路由功能的中間件,提倡的是簡單且夠用。web

GitHub:https://github.com/alibaba/ARouterapi

四:ARouter 優點

從 ARouter Github 瞭解到它的優點:數組

  • 支持直接解析標準URL進行跳轉,並自動注入參數到目標頁面中
  • 支持多模塊工程使用
  • 支持添加多個攔截器,自定義攔截順序
  • 支持依賴注入,可單獨做爲依賴注入框架使用
  • 支持InstantRun
  • 支持MultiDex(Google方案)
  • 映射關係按組分類、多級管理,按需初始化
  • 支持用戶指定全局降級與局部降級策略
  • 頁面、攔截器、服務等組件均自動註冊到框架
  • 支持多種方式配置轉場動畫
  • 支持獲取Fragment
  • 徹底支持Kotlin以及混編

典型的應用:安全

  • 從外部URL映射到內部頁面,以及參數傳遞與解析
  • 跨模塊頁面跳轉,模塊間解耦
  • 攔截跳轉過程,處理登錄、埋點等邏輯
  • 跨模塊API調用,經過控制反轉來作組件解耦

五:ARouter 配置

android {
    defaultConfig {
    ... javaCompileOptions { annotationProcessorOptions { arguments = [ moduleName : project.getName() ] } } } } dependencies { compile 'com.alibaba:arouter-api:1.2.1.1' annotationProcessor 'com.alibaba:arouter-compiler:1.1.2.1' } 

api 的版本和 compiler 的版本號須要用最新的。最新的版本在 Github上能夠找到。

這裏寫圖片描述

六:ARouter 初始化

public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); ARouter.openLog(); // 打印日誌 ARouter.openDebug(); // 開啓調試模式(若是在InstantRun模式下運行,必須開啓調試模式!線上版本須要關閉,不然有安全風險) ARouter.init( this ); // 儘量早,推薦在Application中初始化 } }

七:ARouter 註解發起路由

新建一個 Activity1 做爲測試 ,在 Activity1 添加註解的代碼以下:

package com.router; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.alibaba.android.arouter.facade.annotation.Route; // 在支持路由的頁面上添加註解(必選) // 這裏的路徑須要注意的是至少須要有兩級,/xx/xx @Route(path = "/com/Activity1") public class Activity1 extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_1); } } 

 

在 MainActivity 的佈局中添加一個按鈕

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.router.MainActivity"> <TextView android:id="@+id/bt1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="應用內跳轉" android:gravity="center" android:padding="10dp" android:background="#666666" /> </LinearLayout> 

MainActivity 裏面的跳轉邏輯是:

public class MainActivity extends AppCompatActivity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById( R.id.bt1).setOnClickListener( this ); } @Override public void onClick(View v) { switch ( v.getId() ){ case R.id.bt1 : //發起路由跳轉 ARouter.getInstance().build("/com/Activity1").navigation(); break; } } }

 

效果圖以下:

這裏寫圖片描述

若是你想實現像 startActivityForResult() 功能,能夠這樣使用:

navigation(Activity mContext, int requestCode) 

 

具體使用以下:

ARouter.getInstance() .build("/com/Activity1") .navigation( this , 100 );

 

若是 Path 路徑不正確會發生什麼,咱們來測試一下,如今咱們把路由操做改成

ARouter.getInstance().build("/com/Test").navigation();

效果以下:

這裏寫圖片描述

能夠看到有一個 Toast 提示咱們找不到 「/com/Test」 路徑,咱們來看看源碼

這裏寫圖片描述

經過源碼咱們能夠看到在 debug 狀態下,當找不到路由路徑目標是,會有 Toast 提示。

八:監聽路由過程

在路由跳轉的過程當中,咱們能夠監聽路由的過程,只須要使用:

navigation(Context context, NavigationCallback callback)

NavigationCallback 的源碼以下:

public interface NavigationCallback { /** * Callback when find the destination. * 找到了 * @param postcard meta */ void onFound(Postcard postcard); /** * Callback after lose your way. * 找不到了 * @param postcard meta */ void onLost(Postcard postcard); /** * Callback after navigation. * 跳轉完了 * @param postcard meta */ void onArrival(Postcard postcard); /** * Callback on interrupt. * 被攔截了 * @param postcard meta */ void onInterrupt(Postcard postcard); } 

具體使用以下

ARouter.getInstance()
       .build("/com/Activity1") .navigation(this, new NavCallback() { @Override public void onFound(Postcard postcard) { Log.e("zhao", "onArrival: 找到了 "); } @Override public void onLost(Postcard postcard) { Log.e("zhao", "onArrival: 找不到了 "); } @Override public void onArrival(Postcard postcard) { Log.e("zhao", "onArrival: 跳轉完了 "); } @Override public void onInterrupt(Postcard postcard) { Log.e("zhao", "onArrival: 被攔截了 "); } }); 

 

九:發起路由而且傳遞參數

ARouter.getInstance() .build("/com/Activity1") .withString( "key" , "123") //參數:鍵:key 值:123 .navigation();

 

ARouter 提供了豐富大量的參數類型,供咱們選擇。

//基礎類型 .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) 

 

十:路由分組

在前面咱們講到在對 Activity1 作註解的時候,用到了

@Route(path = "/com/Activity1") public class Activity1 extends AppCompatActivity { }

 

在 path 這個字符串裏面,」com」 就表明組的標識;「Activity1」 表明是 Activity1 類的具體表示。組的標識和類的標識均可以本身定義的,須要記住的是組標識和類標識之間用斜槓來區分 」\」 .

什麼是組?

這裏就須要提下,ARouter框架是分組管理,按需加載。提起來很高深的樣子呢!其實解釋起來就是,在編譯期框架掃描了全部的註冊頁面/服務/字段/攔截器等,那麼很明顯運行期不可能一股腦所有加載進來,這樣就太不和諧了。因此就分組來管理,ARouter在初始化的時候只會一次性地加載全部的root結點,而不會加載任何一個Group結點,這樣就會極大地下降初始化時加載結點的數量。好比某些Activity分紅一組,組名就叫test,而後在第一次須要加載組內的某個頁面時再將test這個組加載進來。

測試一下:

ARouter.getInstance()
       .build("/com/Activity1") .navigation(this, new NavCallback() { @Override public void onArrival(Postcard postcard) { String group = postcard.getGroup(); Log.e("zhao", "分組是: " + group); } }); 

 

結果是

07-27 17:32:17.880 19449-19449/com.router E/zhao: 分組是: com

ARouter 默認狀況下的分組就是第一個 / / 之間的內容。

自定義分組:

建立 CustomGroupActivity 而且添加 註解,而且指定路由分組。自定義分組的就是在原來的註解上添加 group 字段, 以下所示。

@Route(path = "/com/CustomGroupActivity" , group = "customGroup") public class CustomGroupActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_custom_group); } } 

 

自定義分組,發起路由:第二個參數就是路由的分組

build(String path, String group)

具體實現以下所示:

ARouter.getInstance().build("/com/CustomGroupActivity", "customGroup").navigation(); 

 

十一:Fragment 路由

建立 Fragment 類,而且添加路由註解

@Route(path = "/com/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( "/com/TestFragment" ).navigation();

十二:URL 跳轉

web url 跳轉流程圖

這裏寫圖片描述

建立URL 中間跳轉頁

建立 URLReceiveActivity

/** * URL 中轉Activity */ public class URLReceiveActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView( R.layout.activity_url_receive ); //對URI 數據分發 Uri uri = getIntent().getData(); ARouter.getInstance().build(uri).navigation(this, new NavCallback() { @Override public void onArrival(Postcard postcard) { finish(); } }); } } 

 

URLReceiveActivity 添加註冊

<activity android:name=".URLReceiveActivity"> <!-- Schame --> <intent-filter> <data android:host="zhaoyanjun" android:scheme="arouter" /> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> </intent-filter> </activity> 

 

這裏面的 host 、scheme 字段很重要。點擊 url 會根據這兩個字段會調起本地的 Activity 。

下面是一段 HTML 片斷

<h2>1:URL普通跳轉</h2> <p><a href="arouter://zhaoyanjun/com/URLActivity1">arouter://zhaoyanjun/com/URLActivity1 </a> </p> <h2>2:URL普通跳轉攜帶參數</h2> <p> <a href="arouter://zhaoyanjun/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&obj={"name":"jack","id":"666"} </a> </p> 

 

注意 a 標籤裏面的 arouter://zhaoyanjun 分別表明着 scheme 、host ;/com/URLActivity1 就是目標 Activity 的註解。

若是須要接收 URL 中的參數,須要在 Activity 調用自動注入初始化方法;

ARouter.getInstance().inject(this);

 

須要注意的是,若是不使用自動注入,那麼能夠不寫 ARouter.getInstance().inject(this),可是須要取值的字段仍然須要標上 @Autowired 註解,由於 只有標上註解以後,ARouter才能知道以哪種數據類型提取URL中的參數並放入Intent中,這樣您才能在intent中獲取到對應的參數

具體的代碼以下:

@Route(path = "/com/URLActivity2") public class URLActivity2 extends AppCompatActivity{ private TextView textView; @Autowired String name; @Autowired int age; @Autowired boolean boy; @Autowired int high; @Autowired String obj ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ARouter.getInstance().inject(this); setContentView(R.layout.activity_url2); textView = (TextView) findViewById(R.id.tv); //解析參數 Bundle bundle = getIntent().getExtras(); String name1 = bundle.getString("name"); textView.setText("參數是: " + "name: " + name + " age: " + age + " boy: " + boy + " name1: " + name1 + " obj: " + obj.toString() ); } } 

 

效果圖以下:

這裏寫圖片描述

十三:暴露服務

這裏說到的服務不是Android四大組件中的Service,這裏的服務是接口開發的概念,就是將一部分功能和組件封裝起來成爲接口,以接口的形式對外提供能力,因此在這部分就能夠將每一個功能做爲一個服務,而服務的實現就是具體的業務功能。

咱們先自定義一個接口 IService 而且繼承 IProvider 。IService 接口裏面有一個 sayHello() 方法,具體代碼以下。

public interface IService extends IProvider { void sayHello(Context context ); }

 

先定義一個 IService 的實現類 MyService 而且添加註解,代碼以下

@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) { } } 

 

發現服務,首先定義服務對象,而且添加註解,咱們不須要知道接口的具體實現類。

@Autowired(name = "/service/hello") IService service; 

 

而後添加註解初始化,自動賦值。

ARouter.getInstance().inject(this);

最後咱們調用 service 裏面的 sayHello() 方法。

service.sayHello(this);

效果圖以下:

這裏寫圖片描述

發現服務這個功能的特色在於,咱們只須要知道接口,不須要關心接口的實現類,很好了實現瞭解耦。

十四:其餘

  • 關閉路由,這個操做慎用
ARouter.getInstance().destroy();

十五:混淆說明

-keep public class com.alibaba.android.arouter.routes.**{*;} -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}

總結

全部的代碼都上傳至Github: https://github.com/zyj1609wz/Router

參考資料

ARouter解析一:基本使用及頁面註冊源碼解析
ARouter解析二:頁面跳轉源碼分析

相關文章
相關標籤/搜索