1. 程式人生 > >從零開始的ARouter新手篇

從零開始的ARouter新手篇

1、ARouter是什麼?

ARouter是阿里開源的一款android路由框架。按照git上的原說法:他是一個用於幫助 Android App 進行元件化改造的框架 —— 支援模組間的路由、通訊、解耦。

2、ARouter有什麼作用?

解耦、方便管理多頁面的動態跳轉,提供元件化解耦的一種方案,同一Native與H5的跳轉方法。

3、為什麼要用ARouter?

動態跳轉:針對複雜邏輯下,多個頁面進行跳轉。

元件化:針對元件化,隨著業務量的不斷增長,app也會不斷的膨脹,app一般都會走向元件化。元件化就是將APP按照一定的功能和業務拆分成多個元件module,不同的元件獨立開發,元件化不僅能夠提供團隊的工作效率,還能夠提高應用效能。而元件化的前提就是解耦,那麼我們首先要做的就是解耦頁面之間的依賴關係。

Native與H5:因為在H5中是無法使用StartActivity()跳轉到Native頁面的,而從Native跳轉到H5頁面也只能通過配置瀏覽器的方式實現。

4、與常規頁面跳轉的比較

顯式跳轉:

Intent intent = new Intent(activity, XXActivity.class);


由於需要直接持有對應class,從而導致了強依賴關係,提高了耦合度


隱式跳轉:

Intent intent = new Intent(); 
intent.setAction(“com.android.activity.MY_ACTION”);


其中action等屬性的定義在Manifest,導致了擴充套件性較差,規則集中式管理,導致協作變得非常困難。

5、ARouter的基礎使用 

  A、新增依賴和配置

android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ moduleName : project.getName() ]
            }
        }
    }
}

dependencies {
    // 替換成最新版本, 需要注意的是api
    // 要與compiler匹配使用,均使用最新版可以保證相容
    implementation 'com.alibaba:arouter-api:1.3.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
}
// 舊版本gradle外掛(< 2.2),可以使用apt外掛,配置方法見文末'其他#4'
// Kotlin配置參考文末'其他#5'

注意:這裡有個坑

git官方用的是下面的,然後當你執行的時候,會丟擲ARouter::Compiler >>> No module name, for more information, look at gradle log. 的錯誤,我對官網留下的坑和其他哥們兒不負責的轉載表示絕望……至少我這裡是丟擲這個錯的。所以在按照官方配置時遇到這個報錯時,將arguments的引數改成上面的程式碼arguments = [ moduleName : project.getName() ] 應該就能解決,其次如果使多model時,請在每個module裡面的build.gradle中進行配置。

android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

 

  B、添加註解

@Route(path = "/aroute/SecondActivity")
public class SecondActivity extends AppCompatActivity {

}

在支援路由的頁面上添加註解, 這裡的路徑需要注意的是至少需要有兩級,/xx/xx

注:在 path 這個字串裡面,”aroute” 就代表組的標識;“SecondActivity” 代表是 SecondActivity類的具體表示。組的標識和類的標識都可以自己定義的,即我把它寫成@Route(path = "/aaa/SecondActivity11") 也是可以的。

需要記住的是組標識和類標識之間用斜槓來區分 ”\” 。

  C、初始化SDK

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (isDebug()) {           // 這兩行必須寫在init之前,否則這些配置在init過程中將無效
            ARouter.openLog();     // 列印日誌
            ARouter.openDebug();   // 開啟除錯模式(如果在InstantRun模式下執行,必須開啟除錯模式!線上版本需要關閉,否則有安全風險)
        }
        ARouter.init(this); // 儘可能早,推薦在Application中初始化
    }


    /**僅作參考用,免得有小夥伴說找不到方法
     */
    public boolean isDebug() {
        return true;
    }
}

  D、發起路由操作

    a、應用內簡單的跳轉(通過URL跳轉在'進階用法'中)

ARouter.getInstance().build("/aroute/SecondActivity").navigation();

如我從MainActivity跳轉到SecondActivity,我可以這樣去寫點選MainActivity的button觸發to2()

@Route(path = "/aroute/MainActivity")
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void to2(View view) {
        ARouter.getInstance().build("/aroute/SecondActivity").navigation();
    }
}

好了,跳轉完成,很簡單的寫法,大家都去試一下吧。

   b、跳轉並攜帶引數

    public void to2(View view) {
//        ARouter.getInstance().build("/aroute/SecondActivity").navigation();
        ARouter.getInstance().build("/aroute/SecondActivity")
                .withString("key1", "xiaoming")
                .withInt("key2",18)
                .withBoolean("key3",true )
                .navigation();
    }

引數的接收,給要注入的對應變數寫上註解@Autowired,並賦予對應的跳轉key,並執行ARouter.getInstance().inject(this);完成引數注入

@Route(path = "/aroute/SecondActivity")
public class SecondActivity extends AppCompatActivity {

    @Autowired
    public String key1;//第一種引數寫法,直接寫傳入過來的key

    @Autowired(name = "key2")//第二種引數寫法,在註解的name裡傳入過來的key
    public int age;//並對該值進行重新命名

    @Autowired(name = "key3")
    public boolean isMan;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        ARouter.getInstance().inject(this);
        Log.i("Tomes","key1="+key1);
        Log.i("Tomes","key2="+age);
        Log.i("Tomes","key3="+isMan);
    }
}

這裡打出來的日誌為:

注意:前方高能,非戰鬥人員退避

巨坑,照著官方文件的寫的我,一度讓我懷疑人生

    public void to2(View view) {
        Student student=new Student("Tomes",18);
        ARouter.getInstance().build("/aroute/SecondActivity")
                .withString("key1", "xiaoming")
                .withInt("key2",18)
                .withBoolean("key3",true )
                .withObject("key4",student)
                .navigation();
    }

官網說可以用withObject()傳遞引數,如果有小夥伴敢這樣寫……

public class Student{

    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;

    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

很好……

Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.String com.alibaba.android.arouter.facade.service.SerializationService.object2Json(java.lang.Object)' on a null object reference

驚不驚喜,意不意外!!!

很好,看來你看到大坑了。

這裡你需要加上一個類。不懂沒關係,先照著抄下來吧。使用fastjson的小夥伴使用下面的類

@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {
    @Override
    public void init(Context context) {

    }

    @Override
    public <T> T json2Object(String text, Class<T> clazz) {
        return JSON.parseObject(text, clazz);
    }

    @Override
    public String object2Json(Object instance) {
        return JSON.toJSONString(instance);
    }

    @Override
    public <T> T parseObject(String input, Type clazz) {
        return JSON.parseObject(input, clazz);
    }
}

使用GSON的小夥伴用下面的類

@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {

    private Gson mGson;

    @Override
    public void init(Context context) {
        mGson = new Gson();

    }

    @Override
    public <T> T json2Object(String text, Class<T> clazz) {
        checkJson();
        return mGson.fromJson(text, clazz);
    }

    @Override
    public String object2Json(Object instance) {
        checkJson();
        return mGson.toJson(instance);
    }

    @Override
    public <T> T parseObject(String input, Type clazz) {
        checkJson();
        return mGson.fromJson(input, clazz);
    }

    public void checkJson() {
        if (mGson == null) {
            mGson = new Gson();
        }
    }
}

ok,終於不報錯了,血淚的教訓啊。

好了接受資料吧。

@Route(path = "/aroute/SecondActivity")
public class SecondActivity extends AppCompatActivity {

    @Autowired
    public String key1;//第一種引數寫法,直接寫傳入過來的key

    @Autowired(name = "key2")//第二種引數寫法,在註解的name裡傳入過來的key
    public int age;//並對該值進行重新命名

    @Autowired(name = "key3")
    public boolean isMan;

    @Autowired(name = "key4")
    public Student student;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        ARouter.getInstance().inject(this);
        Log.i("Tomes","SecondActivity-->onCreate");
        Log.i("Tomes","key1="+key1);
        Log.i("Tomes","key2="+age);
        Log.i("Tomes","key3="+isMan);
        Log.i("Tomes","key4 name="+student.getName()+"<>age="+student.getAge());

    }
}

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.example.admin.aroute.Student.getName()' on a null object reference

第二次衝擊來襲……

這一刻我回想起被ARouter支配的恐懼……

這坑有完沒完?

告訴你,沒完……

教練我想放棄……

來回到Student類

public class Student{

    public String name;
    public int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;

    }

    public Student(){}
}

這裡陣亡次數太多,我就不一一複述了,總結如下:

注意修改點:

1、將用到的屬性都從private改成public;

2、沒有預設構造的都加上預設構造;

3、如果Student類裡面還包含了其他物件,其他物件也按照Student類的標準;

一二三四,再試一次。bug解除,bug解除,bug解除。

引數終於傳遞過來了,真不容易啊。


  c、路由過程監聽

在路由跳轉的過程中,我們可以監聽路由的過程。有需要的同學可以根據需求加上。

public Object navigation(Context context, NavigationCallback callback) 
    public void to2(View view) {
        ARouter.getInstance().build("/aroute/SecondActivity")
                .withString("key1", "xiaoming")
                .withInt("key2",18)
                .withBoolean("key3",true )
                .navigation(this, new NavigationCallback() {
                    @Override
                    public void onFound(Postcard postcard) {
                        Log.i("Tomes","onFound");
                    }

                    @Override
                    public void onLost(Postcard postcard) {
                        Log.i("Tomes","onLost");
                    }

                    @Override
                    public void onArrival(Postcard postcard) {
                        Log.i("Tomes","onArrival");
                    }

                    @Override
                    public void onInterrupt(Postcard postcard) {
                        Log.i("Tomes","onInterrupt");
                    }
                });
    }

如果不需要這麼多回調實現,可以NavCallback替代NavigationCallback則

    public void to2(View view) {
        ARouter.getInstance().build("/aroute/SecondActivity")
                .withString("key1", "xiaoming")
                .withInt("key2",18)
                .withBoolean("key3",true )
                .navigation(this, new NavCallback() {
                    @Override
                    public void onArrival(Postcard postcard) {
                        Log.i("Tomes","onArrival");
                    }
                });
    }

E、新增混淆規則

這裡以基礎混淆proguard-rules.pro檔案為例,混淆配置檔案里加入

-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-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

基礎執行demo避免踩坑

ARouter git地址:https://github.com/alibaba/ARouter