阿里巴巴arouter組件化總結

第1節 組件化原因

  1. 抽離出常用的公共模塊,如公司內部封裝的工具庫,網絡訪問庫,公共資源等。各公共模塊單獨維護,逐漸完善。工作多年深刻感覺到公共模塊代碼對一個公司的重要性:
    1. 抽離出公共模塊,通過共同維護和使用能明顯提高公司內部項目開發效率,減少內部消耗。
    2. 抽離出公共模塊,能達到強調的效果,通過逐步的版本迭代,達到公司內部技術積累的目的。
    3. 把功能模塊放入內部版本倉庫,使代碼更簡潔,各版本修改bug追蹤有日誌可查,使代碼更健壯。
  2. 各業務模塊間解耦
    1. 能達到各模塊組合快速完成一個App,現實開發場景中,一個公司很多項目並行開發,其實很多業務模塊是可以共用的。
    2. 能動態替換某一模塊;
  3. 頁面跳轉,服務調用URL化,更切合hybird和多端開發的場景,如
    1. 由於路徑化Web和原生界面相互跳轉會很方便;
    2. Web調用Android\IOS原生頁面統一化提高開發效率;
    3. 通過動態加載路由,能實現動態化路由這種黑科技;
  4. 路由跳轉監聽和攔截
    1. 通過路由攔截的功能,可以在很輕鬆的實現權限分組,權限細分及權限動態配置;
    2. 通過路由監聽可以把一些埋點統一放到jiant
  5. 路由降級尋址
    1. 處理尋址異常降級尋址的問題;
    2. 在原生頁面崩潰異常時,可以通過動態路由表,攔截原生路由跳轉到H5;

第2節 組件化須考慮的問題

  1. 組件化之間的通信;
  2. 組件生命週期管理(模塊能處理Application的生命週期);
  3. 混合開發,網頁端跳轉到Android 和IOS統一跳轉參數;

注意:實踐心得體會,統一參數必須用String類型

舉個栗子:

用Html 傳遞json格式的5.0參數{“key”:5.0}, JavaScript語法會自動把5.0變成5也就是, double 變成 int,如果Android 和IOS有做類型區分就會傳參失敗,Android經典場景如下:

intent.getIntExtra();
intent.getDoubleExtra()

然後本來是邏輯走double的沒有走。

第二栗子:
json數據中false 和true 專遞也最好用"1"和"0"代替
跟IOS同事討論了很久,IOS Oject-C 處理true和false的機制和Android 不同
故爲了防止不同編程語言中處理方式不同儘量使用字符會少很多麻煩

  1. 統一各模塊依賴版本庫
  2. 混淆文件管理

第3節 ARouter使用

3.1 初始化配置

第一步:build.gradle(project) 設置 使用gradle插件自動化註冊

 classpath "com.alibaba:arouter-register:$arouter_register_version" //1.0.2

gradle.properties

arouter_register_version=1.0.2

第二步:新建lib_base 模塊存放各子模塊公用類,如路由表

libs_base build.gradle 配置

apply plugin: 'com.alibaba.arouter'

android {
   defaultConfig {
          javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        	}
    }
    
    compileOptions {
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    //可以定義一個公共資源模塊,用於各模塊資源共享
    implementation project(':lib_resource')
    api ('com.alibaba:arouter-api:1.4.1')
    annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'

    //json解析框架
    api 'com.google.code.gson:gson:2.8.2'
}

第三步: 子模塊build.gradle 配置

apply plugin: 'com.alibaba.arouter' //ARouter 路徑註冊優化
android {
   defaultConfig {
          javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        	}
    }
    
    compileOptions {
        sourceCompatibility = '1.8'
        targetCompatibility = '1.8'
    }
}
dependencies{
  //必須使用註解,自動生成路由
  annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
  //引用父模塊,用於共享類存放
  implementation project(':lib_base')
}

第三步:讓組件自動運行

Build.gradle

if (!isNeedMeModule.toBoolean()) {
	apply plugin: 'com.android.application'
} else {
	apply plugin: 'com.android.library'
}

gradle.properties

isNeedMeModule=true

第四步:殼工程配置

if (!isNeedMeModule.toBoolean()) {
  compile project(":module_home")
}

第五步: 初始化

 if (BuildConfig.DEBUG) {
      ARouter.openDebug();
      ARouter.openLog();
 }
  ARouter.init(this);

子模塊引用gradle.properties

isNeedMeModule=true

3.2 高階使用

1. 模塊間調用

第一步: lib_base 模塊定義接口

public interface IHelloModuleService extends IProvider {
    String getUserName(String usrID);
}

第二步:子模塊實現接口

@Route(path = Router.SERVICE_HELLO,name = "測試服務")
public class HelloServiceImpl implements IHelloModuleService {
    @Override
    public String getUserName(String usrID) {
        return "Test provider";
    }

    @Override
    public void init(Context context) {

    }
}

第三步:調用其他模塊提供的接口

  public static String getUserName(String userId) {
        IHelloModuleService chatModuleService = ARouterHelper.getInstance().navigation(IHelloModuleService.class);
        if (chatModuleService != null) {
            return chatModuleService.getUserName(userId);
        }
        return "";
    }

2. 全局分組攔截

@Interceptor(priority = 1,name = "全局分組攔截")
public class GlobalDefaultInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
//        獲取當前組
//        postcard.getGroup()
//        直接執行
//        callback.onContinue(postcard);
//        onLost
//        callback.onInterrupt(null);
        callback.onContinue(postcard);
    }

    @Override
    public void init(Context context) {

    }
}

3. 降級處理

注意: path 必須有值否則,安裝Arouter的架構無法實例化GlobalDegradeServiceImpl

@Route(path = "/root/degrade_service",name = "全局降級策略")
public class GlobalDegradeServiceImpl implements DegradeService {
    private static final String TAG = "GlobalDegradeServiceImp";
    @Override
    public void onLost(Context context, Postcard postcard) {
        Log.d(TAG, String.format("Degrade path %s", postcard.getPath()));
    }

    @Override
    public void init(Context context) {

    }
}

4. 路徑替換

@Route(path = "/root/path_replace") // 必須標明註解
public class PathReplaceServiceImpl implements PathReplaceService {

    /**
     * 重寫路徑
     * @param path raw path
     * @return new path
     */
    @Override
    public String forString(String path) {
        return path;// 按照一定的規則處理之後返回處理後的結果
    }

    @Override
    public Uri forUri(Uri uri) {
        return uri;
    }

    @Override
    public void init(Context context) {

    }
}

5. 默認JavaBean序列化控制類

@Route(path = "/root/serialization_service",name = "默認JavaBean序列化控制類")
public class JsonServiceImpl implements SerializationService {
    private final Gson gson = new Gson();

    @Override
    public <T> T json2Object(String input, Class<T> clazz) {
        return gson.fromJson(input,clazz);
    }

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

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

    @Override
    public void init(Context context) {

    }
}

第4節 Arouter採坑記錄

前言

開源庫遇到問題到issue裏找

開源庫遇到問題到issue裏找

開源庫遇到問題到issue裏找

4.1 AutoWrite

錯誤寫法

@Autowired
private int type ;

❓原因: 自動注入是不能用private關鍵字的

4.2 navigation

navigation() vs navigation(Context)

使用過ARouter的同學,有知道區別的嗎?當然我也是被坑才知道,之前也掃了一遍源碼,還是沒有get關鍵點

4.3 finish 時機不對產生白屏

第5節 實戰總結 ARouter封裝類

Postcard 的包裝類

因爲Postcard 是區分類型的在改造的時候用

  • Java的多態的言語特性能簡化很多工作量
  • 可以在後期全局修改添加屬性

PostcardWrapper.java

public class PostcardWrapper {

    private final Postcard mPostcard;

    public PostcardWrapper(Uri uri) {
        mPostcard = ARouter.getInstance().build(uri);
    }

    public Postcard getPostcard() {
        return mPostcard;
    }

    public Bundle getOptionsBundle() {
        return mPostcard.getOptionsBundle();
    }

    public int getEnterAnim() {
        return mPostcard.getEnterAnim();
    }

    public int getExitAnim() {
        return mPostcard.getExitAnim();
    }

    public IProvider getProvider() {
        return mPostcard.getProvider();
    }

    public PostcardWrapper setProvider(IProvider provider) {
        mPostcard.setProvider(provider);
        return this;
    }

    public PostcardWrapper(String path){
        this.mPostcard = ARouter.getInstance().build(path);
    }

    public PostcardWrapper(String path, String group) {
        mPostcard = new Postcard(path, group);
    }

    public PostcardWrapper(String path, String group, Uri uri, Bundle bundle){
        mPostcard = new Postcard(path, group, uri, bundle);
    }

    public boolean isGreenChannel(){
        return mPostcard.isGreenChannel();
    }

    public Object getTag(){
        return mPostcard.getTag();
    }

    public PostcardWrapper setTag(Object tag){
        mPostcard.setTag(tag);
        return this;
    }

    public int getTimeout(){
        return mPostcard.getTimeout();
    }

    public PostcardWrapper setTimeout(int timeout){
        mPostcard.setTimeout(timeout);
        return this;
    }

    public Uri getUri(){
        return mPostcard.getUri();
    }

    public PostcardWrapper setUri(Uri uri){
        mPostcard.setUri(uri);
        return this;
    }

    public PostcardWrapper greenChannel() {
        mPostcard.greenChannel();
        return this;
    }

    public Object navigation(){
        return navigation(null);
    }


    public Object navigation(Context context){
        return navigation(context,null);
    }

    public Object navigation(Context context, NavigationCallback callback) {
        return ARouter.getInstance().navigation(context, mPostcard, -1, callback);
    }


    public void navigation(Activity mContext, int requestCode) {
        navigation(mContext, requestCode,null);
    }

    public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
        ARouter.getInstance().navigation(mContext, mPostcard, requestCode, callback);
    }

    public PostcardWrapper with(Bundle bundle) {
        mPostcard.with(bundle);
        return this;
    }

    public PostcardWrapper withFlags(int flag) {
        mPostcard.withFlags(flag);
        return this;
    }

    public PostcardWrapper addFlags(int flags) {
        this.mPostcard.addFlags(flags);
        return this;
    }

    public int getFlags() {
        return mPostcard.getFlags();
    }


    public PostcardWrapper with(@Nullable String key, @Nullable Object value){
        mPostcard.withObject(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable String value){
        mPostcard.withString(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, boolean value){
        mPostcard.withBoolean(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, short value){
        mPostcard.withShort(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, int value){
        mPostcard.withInt(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, long value){
        mPostcard.withLong(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, double value){
        mPostcard.withDouble(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, char value){
        mPostcard.withChar(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, float value){
        mPostcard.withFloat(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable CharSequence value){
        mPostcard.withCharSequence(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable Parcelable value){
        mPostcard.withParcelable(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable Parcelable[] value){
        mPostcard.withParcelableArray(key, value);
        return this;
    }


    public PostcardWrapper with(@Nullable String key, @Nullable SparseArray<? extends Parcelable> value) {
        mPostcard.withSparseParcelableArray(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable Serializable value) {
        mPostcard.withSerializable(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable byte[] value) {
        mPostcard.withByteArray(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable short[] value) {
        mPostcard.withShortArray(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable char[] value) {
        mPostcard.withCharArray(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable float[] value) {
        mPostcard.withFloatArray(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable CharSequence[] value) {
        mPostcard.withCharSequenceArray(key, value);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable Bundle value) {
        mPostcard.withBundle(key, value);
        return this;
    }

    /**
     * 設置動畫效果
     * @param enterAnim 進入動畫
     * @param exitAnim 退出動畫
     * @return
     */
    public PostcardWrapper with(int enterAnim, int exitAnim) {
        mPostcard.withTransition(enterAnim, exitAnim);
        return this;
    }

    public PostcardWrapper withTransition(int enterAnim, int exitAnim) {
        mPostcard.withTransition(enterAnim, exitAnim);
        return this;
    }

    /**
     * 默認轉換動畫效果
     * @return
     */
    public PostcardWrapper withDefaultTrans(){
        mPostcard.withTransition(R.anim.anim_activity_slide_in_right, R.anim.anim_activity_slide_out_left);
        return this;
    }

    @RequiresApi(16)
    public PostcardWrapper withOptionsCompat(ActivityOptionsCompat compat) {
        mPostcard.withOptionsCompat(compat);
        return this;
    }

    /**
     * Set options compat
     *
     * @param compat compat
     * @return this
     */
    @RequiresApi(16)
    public PostcardWrapper with(ActivityOptionsCompat compat) {
        mPostcard.withOptionsCompat(compat);
        return this;
    }

    @Override
    public String toString() {
        return "PostcardWrapper ==> s\ns" + mPostcard.toString();
    }

    public String getAction(){
        return mPostcard.getAction();
    }

    public PostcardWrapper withAction(String action) {
        mPostcard.withAction(action);
        return this;
    }

    public PostcardWrapper with(String action) {
        mPostcard.withAction(action);
        return this;
    }

    public PostcardWrapper with(@Nullable String key, @Nullable ArrayList value) {
        if (value != null && value.size() > 0) {
            Object o = value.get(0);
            if (o instanceof String){
                mPostcard.withStringArrayList(key, value);
            }else if (o instanceof CharSequence) {
                mPostcard.withCharSequenceArrayList(key, value);
            }else if (o instanceof Integer){
                mPostcard.withIntegerArrayList(key, value);
            } else if (o instanceof Parcelable) {
                mPostcard.withParcelableArrayList(key, value);
            }
        }
        return this;
    }

}

ArouterHelper 幫助類

ARouterHelper.java

public class ARouterHelper {

    private ARouterHelper() {
    }

    private static class InstanceHolder {
        // static 只執行一次,並嚴格保證按照順序執行
        private final static ARouterHelper instance = new ARouterHelper();
    }

    public static ARouterHelper getInstance() {
        return InstanceHolder.instance;
    }

    public static void init(Application application) {
        if (BuildConfig.DEBUG) {
            ARouter.openDebug();
            ARouter.openLog();
            ARouter.printStackTrace();
        }
        ARouter.init(application);
    }

    public void inject(Object thiz) {
        ARouter.getInstance().inject(thiz);
    }

    /**
     * CMUBaseActivity CMUBaseFragment 兩個base類重寫 startActivity 默認跳轉動畫
     * @param path
     * @return
     */
    public PostcardWrapper build(String path) {
        return build(path,true);
    }

    /**
     * @param path
     * @param useDefaultTransition 使用 CMUBaseActivity CMUBaseFragment 配置的跳轉方式
     * @return
     */
    public PostcardWrapper build(String path, boolean useDefaultTransition) {
        PostcardWrapper postcardWrapper = new PostcardWrapper(path);
        if (useDefaultTransition){
            postcardWrapper.withDefaultTrans();
        }
        return postcardWrapper;
    }

    /**
     * 使用主題默認跳轉動畫
     * @param path
     * @return
     */
    public PostcardWrapper buildWithThemeAnim(String path) {
        return new PostcardWrapper(path);
    }


    /**
     * 無動畫轉換
     * @param path
     * @return
     */
    public PostcardWrapper buildWithoutAnim(String path){
        return new PostcardWrapper(path).withTransition(0, 0);
    }

    public PostcardWrapper build(Uri uri) {
        return new PostcardWrapper(uri);
    }

    public <T> T navigation(Class<? extends T> service) {
        return ARouter.getInstance().navigation(service);
    }

    public Object navigation(Context mContext, PostcardWrapper postcard, int requestCode, NavigationCallback callback) {
        return ARouter.getInstance().navigation(mContext, postcard.getPostcard(), requestCode, callback);
    }

}

附錄

參考

學到知識點

api與implementation的區別

implementation 不分享自己的依賴,只有在runtime是其他模塊的依賴纔可見
api 分享自己的依賴,但是在其變化時,其他依賴需重新編譯

Android Studio添加文件註釋頭模板?

/**
 *    @Author  linz
 *    @Date    $date$ $time$
 *    @Description   
 *    @Version: 1.0
 */

Arouter 源碼

/**
 * Provide degrade service for router, you can do something when route has lost.
 *
 * @author Alex <a href="mailto:[email protected]">Contact me.</a>
 * @version 1.0
 * @since 2016/9/22 14:51
 */
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章