阿里ARouter開源組件化框架項目實踐

1. App項目組件化

做移動開發(fā)的同學(xué)都會發(fā)現(xiàn)這兩年在移動開發(fā)圈子里最火的就是組件化了,組件化不同的實現(xiàn)方案也引起了各派技術(shù)大拿的爭吵,技術(shù)人員的一個通病就是,總覺得自己的方案是最好的,吵歸吵但是大家的目標(biāo)其實是一致的,都是實現(xiàn)APP的功能組件化,為什么這兩年大家都開始考慮做組件化了呢?這也是因為隨著這幾年APP的快速發(fā)展,各個活下來的APP安裝包都由原先的幾兆到了幾十兆,甚至有些應(yīng)用超過了100M,每個APP的開發(fā)團(tuán)隊也有原先的2,3個人到了幾十人的團(tuán)隊,這就帶來了兩個問題,我的項目里實施組件化主要也是為了解決這兩個問題,

1)隨著代碼量的增加,每次編譯都需要10分鐘以上,寫完代碼真機(jī)調(diào)試每次launch都是個痛苦的過程,用團(tuán)隊里同學(xué)的話來說一天8個小時超過5個小時都是在等待,在上家公司因為單應(yīng)用代碼量太大,甚至出現(xiàn)了開發(fā)同學(xué)在AS里點了run后,就出去抽煙的現(xiàn)象,抽完一支煙回來也差不多編譯完了;

2)第二個問題就是單應(yīng)用代碼量太大了以后,如果組內(nèi)成員開發(fā)水平層次不齊就有可能造成代碼的高度耦合,修改代碼的影響會非常大,可能修改了一個很小的地方,但是卻引起了很多地方出現(xiàn)了問題,并且代碼的復(fù)用性也下降的很厲害。

組件化能很好的解決這兩個開發(fā)痛點,通過組件化可以將android每個功能module都編譯成aar,并統(tǒng)一放到maven私有倉庫里進(jìn)行統(tǒng)一管理,ios的每個功能module可以都打包放入cocoapods里,這樣每次編譯工程的時候開發(fā)人員只需要編譯自己的模塊進(jìn)行開發(fā)調(diào)試了,那將是非常快的,如果需要全工程運(yùn)行,那也會非常快,因為組件都是事先編譯好的執(zhí)行碼,不需要重新編譯,只需要對殼工程進(jìn)行編譯就可以了,速度也是非常快的,這就解決了編譯速度的問題;

對于優(yōu)秀的coder,一般都是有代碼潔癖的,這里的潔癖不單單指代碼整潔,更多的指的是應(yīng)用項目中的模塊高度復(fù)用,沒有冗余代碼,最討厭的就是看到有開發(fā)人員在項目中ctrl c ctrl v,組件化就能很好的解決這個問題,但是前提是你的功能組件規(guī)劃切分的比較好,能夠方便的在其它模塊中進(jìn)行復(fù)用。

2. ARouter解決了什么問題

2.1 組件化技術(shù)問題

上面介紹了那么多組件化的好處,那么了解過組件化的同學(xué)應(yīng)該知道組件化實施的難點和需要攻克的問題,不然組件化實施還是有一定技術(shù)門檻的,做的不好反而會增加團(tuán)隊的開發(fā)成本,下面我們先討論下組件化實施會遇到的技術(shù)問題:

1)如何解決組件單獨(dú)編譯的問題;

在組件化過程中,我們面臨的第一個問題就是如果將組件單獨(dú)編譯調(diào)試,并且可以方便的與其它組件一起打包成一個應(yīng)用;目前解決組件單獨(dú)編譯編譯問題一般都是通過修改module的gradle文件的apply plugin來實現(xiàn),application和module的切換,做的好一些就是在gradle.properties中定義一個debug開關(guān),在gradle中增加一個if else判斷,如果當(dāng)前是debug模式,則module的gradle定義的apply plugin是application,如果非debug模式,那么module定義的apply plugin是module,這樣項目編譯的時候就會自動根據(jù)設(shè)置的debug參數(shù)來動態(tài)選擇是要單獨(dú)編譯模塊還是要編譯成整個app;

gradle.properties

IsDebug = false

module的build.gradle

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

2)如何解決組件件通訊問題;

組件化實施后必然存在各個組件之間的調(diào)用,目前的方案大部分都是通過intent進(jìn)行傳遞,這樣的話會造成傳遞的對象依賴,而且每個組件之間的調(diào)用也會比較混亂;在使用ARouter之前我們使用的方案是每個組件之間通過uri傳遞json,每個組件在被調(diào)用后從uri中獲取json,然后對json進(jìn)行反序列化成對象,再進(jìn)行后續(xù)動作,每個組件提供的服務(wù)都通過集中配置統(tǒng)一處理,在啟動的時候全部進(jìn)行初始化,在內(nèi)存中保存這些服務(wù)uri路由信息,之前casa提出的ios組件化方案是通過runtime反射出需要調(diào)用的組件,參數(shù)傳遞是通過Map來傳遞,實現(xiàn)了組件之間的實體依賴耦合;ARouter提出了類似目前服務(wù)端SOA的服務(wù)化概念,將每個組件都看成一個服務(wù)系統(tǒng),對其它組件都是通過提供服務(wù)的方式,讓其它組件完成調(diào)用,這樣可以做到每個組件的服務(wù)都是可管理的,而且組件之間的邊界是清晰的,耦合是松散的;個人覺得ARouter這種服務(wù)化的概念更理想化,在底層的服務(wù)注冊管理中心進(jìn)行統(tǒng)一管理,負(fù)責(zé)服務(wù)初始化、服務(wù)路由、服務(wù)調(diào)用、服務(wù)降級、服務(wù)資源回收等;

3)如何解決組件件頁面調(diào)用問題;

組件頁面的調(diào)用,按照服務(wù)化的理念,其實可以理解為服務(wù)的調(diào)用,原先我們實現(xiàn)的方案中,組件之間調(diào)用,如果是通過在自定義uri后面通過json傳遞需要傳遞的實體內(nèi)容,但對于非頁面的調(diào)用,就會導(dǎo)致產(chǎn)生組件間依賴是一種不干凈的實現(xiàn)方案,但是解決組件間的頁面調(diào)用用傳統(tǒng)的uri帶參數(shù)的方式是完全沒有問題的,這樣還可以在服務(wù)端統(tǒng)一管理所有的頁面調(diào)度策略,可以方便的做灰度發(fā)布,可以做到每個用戶的頁面調(diào)度策略都是由服務(wù)端下發(fā),對于需要進(jìn)行進(jìn)行灰度的用戶,可以使用灰度版本的頁面調(diào)度策略,非常方便;ARouter底層其實也是通過uri的方式來實現(xiàn),只不過又重新做了更好的封裝,能夠比較方便的使用,并且對非頁面的功能組件調(diào)用更加簡單,只需要定義好服務(wù)uri,然后在另一個組件中直接調(diào)用定義的uri就可以了;

4)如何解決組件間解耦問題;

組件間的解耦是組件化的另一個核心問題,評判一個組件化方案好不好,一個要看該組件化方案是否對工程的性能會造成影響,另一方面就是能夠很好的將組件化后的各個組件完美解耦;目前比較好的解耦方案一個是通過uri進(jìn)行服務(wù)化定義,另一個就是通過runtime反射來實現(xiàn)組件的調(diào)用,第一種是目前用的比較多的方案,第二種在部分方案中也有使用,反射方案上手會存在一定的難度,而且查詢問題會比較困難,但是性能方面反射方案會比uri方案要好,反正各有優(yōu)缺點,各家按照自己的實際情況對比后可以自行選擇;

2.2 ARouter功能介紹

https://github.com/alibaba/ARouter

  1. 支持直接解析標(biāo)準(zhǔn)URL進(jìn)行跳轉(zhuǎn),并自動注入?yún)?shù)到目標(biāo)頁面中
  2. 支持多模塊工程使用
  3. 支持添加多個攔截器,自定義攔截順序
  4. 支持依賴注入,可單獨(dú)作為依賴注入框架使用
  5. 支持InstantRun
  6. 支持MultiDex(Google方案)
  7. 映射關(guān)系按組分類、多級管理,按需初始化
  8. 支持用戶指定全局降級與局部降級策略
  9. 頁面、攔截器、服務(wù)等組件均自動注冊到框架
  10. 支持多種方式配置轉(zhuǎn)場動畫
  11. 支持獲取Fragment
  12. 完全支持Kotlin以及混編(配置見文末 其他#5)

2.3 ARouter典型應(yīng)用

  1. 從外部URL映射到內(nèi)部頁面,以及參數(shù)傳遞與解析
  2. 跨模塊頁面跳轉(zhuǎn),模塊間解耦
  3. 攔截跳轉(zhuǎn)過程,處理登陸、埋點等邏輯
  4. 跨模塊API調(diào)用,通過控制反轉(zhuǎn)來做組件解耦

從上面介紹的ARouter功能,可以發(fā)現(xiàn),第一節(jié)提到的這些組件化需要解決的技術(shù)問題,用ARouter基本都可以相對比較完美的解決, 如果項目使用ARouter后可以比較好的實現(xiàn)組件化,之前在2014年準(zhǔn)備實施組件化的時候,當(dāng)時還沒有特別好的組件化方案,所以項目是自己實現(xiàn)的組件化方案,實現(xiàn)的比較簡單,只是實現(xiàn)了組件頁面的跳轉(zhuǎn),參數(shù)的傳遞,將代碼從功能層面進(jìn)行了拆分,當(dāng)時拆了十幾個子工程,當(dāng)時拆分的過程還是比較順利的,當(dāng)時也希望能有一個比較完美的解決方案能在社區(qū)中冒出來,到了15年各種會議都有公司提出自己APP的組件化方案,其實也都是大同小異,而且也都沒有成體系,筆者也一直很關(guān)注這塊,直到阿里云的組件化方案ARouter發(fā)布,發(fā)現(xiàn)這就是我要的組件化方案,所以在新項目中也使用了該方案,目前所在公司的開發(fā)人員規(guī)模不是很大,沒有足夠的人力去自研組件化框架,所以使用阿里云的組件化方案還是比較方便穩(wěn)妥的,下面就講下怎么接入。

3. 快速接入ARouter

1)gradle中增加庫依賴

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.0'
    testCompile 'junit:junit:4.12'
    //下面兩行就是需要添加的ARouter的依賴,arouter-api這個庫是arouter的核心庫
    //arouter-compiler是annnotation的定義庫依賴,對于組件中使用到arouter注解的情況,一定要增加該依賴
    compile 'com.alibaba:arouter-api:1.2.1.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.0.3'

}

2)增加uri路徑注解

/**
 * https://m.shrb.com/modulea/activity1
 */
@Route(path = "/modulea/activity1")
public class Activity1 extends Activity {

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

ARouter管理的頁面都需要通過@Route注解進(jìn)行標(biāo)注,在注解中需要定義path來表示uri的路徑,忘記從哪個版本開始,必須至少使用兩級目錄,第一級目錄代表group,group的概念后面會闡述下,在ARouter中對所有發(fā)布的服務(wù)做了懶加載,只有g(shù)roup中的任意一個服務(wù)第一次被調(diào)用的時候才會去一次行把該group下的服務(wù)統(tǒng)一加載到內(nèi)存,這樣可以避免啟動的時候初始化過多的可能不會用到的組件服務(wù);

3)ARouter初始化

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

官方是建議將ARouter的初始化放在自定義的Application中初始化,這樣可以避免在某個頁面中或者service中初始化,后期資源被回收的問題,導(dǎo)致所有的組件服務(wù)全部失效,放在Application中就可以保證ARouter事例在內(nèi)存中的生命周期和APP保持一致,不會存在資源被誤回收的可能;在我后續(xù)的例子中為了方便我將初始化放在了demo主Activity中,這只是為了演示,實際項目使用過程中大家一定要放到Application中去初始化,防止app在使用過程中出現(xiàn)一些莫名其妙的問題;

4)頁面路由

//組件無參數(shù)跳轉(zhuǎn)
ARouter.getInstance()
   .build("/modulea/activity1")
   .navigation();
//組件攜帶參數(shù)跳轉(zhuǎn)
 ARouter.getInstance()
   .build("/modulea/activity1")
   .withString("name", "老王")
   .withInt("age", 18)
   .navigation();

定義的activity1是和上面調(diào)用的activity不在一個module中,我們這里將activity1定義在了moduleA下面,activity1獲取參數(shù),可以像spring一樣定義Autowired注解,但是這里的Autowired注解可不是spring類庫下的自動綁定注解類,而是arouter庫下的Autowired類,在activity定義了參數(shù)的同名局部變量后就可以在activity中通過ARouter.getInstance().inject(this); 來自動獲取到傳遞的參數(shù),arouter會自動注入到變量中,這樣整個過程是不是看起來很簡單,很清晰。

/**
 * https://m.shrb.com/modulea/activity1?name=老王&age=23
 */
@Route(path = "/modulea/activity1")
public class Activity1 extends Activity {

    @Autowired
    public String name;
    @Autowired
    public int age;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_1);
        //傳遞參數(shù)注入
        ARouter.getInstance().inject(this);

        Log.d("param", name + age);
    }
}

4. 深入學(xué)習(xí)ARouter

上面我們介紹了如果簡單的引入ARouter并且進(jìn)行不同組件間的頁面路由,下面我們再介紹下ARouter一些高級技能。

1)解析URI中的參數(shù)

// 為每一個參數(shù)聲明一個字段,并使用 @Autowired 標(biāo)注
// URL中不能傳遞Parcelable類型數(shù)據(jù),通過ARouter api可以傳遞Parcelable對象
@Route(path = "/modulea/activity1")
public class Activity1 extends Activity {
    @Autowired
    public String name;
    @Autowired
    int age;
    @Autowired(name = "girl") // 通過name來映射URL中的不同參數(shù)
    boolean boy;
    @Autowired
    TestObj obj;    // 支持解析自定義對象,URL中使用json傳遞

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ARouter.getInstance().inject(this);

    // ARouter會自動對字段進(jìn)行賦值,無需主動獲取
    Log.d("param", name + age + boy);
    }
}
  1. 定義攔截器

這里所指的攔截器和j2ee中里常說的攔截器是一個概念,就是在處理這個服務(wù)前需要處理的動作,也類似jsp中的filter,和okhttp中的攔截器也是一樣的概念,可以在攔截器中實現(xiàn)切面公共的功能,這樣這些切面公共功能就不會和業(yè)務(wù)服務(wù)代碼耦合在一起,是一種比較好的AOP實現(xiàn),ARouter實現(xiàn)的攔截器也可以對攔截器設(shè)置優(yōu)先級,這樣可以對攔截器的處理優(yōu)先順序進(jìn)行處理;但是這個攔截器整體功能還是比較弱,目前的版本實現(xiàn)的是全服務(wù)攔截,沒有參數(shù)可以定義pointcut攔截點,所以如果要對指定頁面進(jìn)行處理只能在

// 比較經(jīng)典的應(yīng)用就是在跳轉(zhuǎn)過程中處理登陸事件,這樣就不需要在目標(biāo)頁重復(fù)做登陸檢查
// 攔截器會在跳轉(zhuǎn)之間執(zhí)行,多個攔截器會按優(yōu)先級順序依次執(zhí)行
@Interceptor(priority = 8, name = "測試用攔截器")
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
    Log.d("Interceptor","攔截器測試");
    callback.onContinue(postcard);  // 處理完成,交還控制權(quán)
    // callback.onInterrupt(new RuntimeException("我覺得有點異常"));      
      // 覺得有問題,中斷路由流程

    // 以上兩種至少需要調(diào)用其中一種,否則不會繼續(xù)路由
    }

    @Override
    public void init(Context context) {
    // 攔截器的初始化,會在sdk初始化的時候調(diào)用該方法,僅會調(diào)用一次
    }
}
  1. 外部通過URL跳轉(zhuǎn)APP的內(nèi)部頁面

定義SchemaFilterActitity,所有外部來調(diào)用本APP的請求,都會先到該SchemeFilterActivity,由該Activity獲取uri后再通過Arouter進(jìn)行轉(zhuǎn)發(fā),這樣就可以實現(xiàn)幾種效果,1.同一部手機(jī)上可以通過自定義url來訪問我們app對外暴露的頁面并接收外部應(yīng)用傳遞過來的值,這樣對于集團(tuán)內(nèi)應(yīng)用交叉營銷非常方便;2.對外分享的二維碼直接掃碼后打開app,對于在推廣的app,可以實現(xiàn)掃碼識別url后直接從外部手機(jī)瀏覽器跳轉(zhuǎn)到我們的app某個頁面,支付寶、微信支付都是這樣實現(xiàn)在支付的時候喚起原生頁面的;

public class SchameFilterActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        Uri uri = getIntent().getData();
        ARouter.getInstance().build(uri).navigation();
        finish();
    }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.commonlib">

    <application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:supportsRtl="true">
        <activity android:name=".SchameFilterActivity">

            <!-- Schame -->
            <intent-filter>
              <!--自定義host和scheme -->
                <data
                    android:host="m.hop.com"
                    android:scheme="shrb" />

                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
            </intent-filter>
        </activity>
        <activity android:name=".HopWebview"></activity>
    </application>

</manifest>

4)定義全局降級頁面

相信我們之前遇到過這樣的問題,如果跳轉(zhuǎn)的頁面不存在,或者調(diào)用的組件服務(wù)不存在就會報錯甚至crash,這里ARouter為我們提供了默認(rèn)降級服務(wù),一旦路由找不到頁面或者服務(wù)就會調(diào)用該降級service,我們可以在繼承自DegradeService類的onLost方法中實現(xiàn)降級需要實現(xiàn)的動作;

// 實現(xiàn)DegradeService接口,并加上一個Path內(nèi)容任意的注解即可
@Route(path = "/xxx/xxx")
public class DegradeServiceImpl implements DegradeService {
    @Override
    public void onLost(Context context, Postcard postcard) {
        Log.d("DegradeService","降級服務(wù)啟動!");
    }

    @Override
    public void init(Context context) {

    }
}

5)組件服務(wù)注冊與調(diào)用

前面我們說了組件間的互相調(diào)用都是通過暴露接口,或者服務(wù)來實現(xiàn),筆者原來公司服務(wù)之間的互相調(diào)用都是通過直接調(diào)用依賴接口,所以調(diào)用方需要依賴被調(diào)用組件的接口,ARouter是將組件的服務(wù)對外暴露,調(diào)用方直接使用組件暴露的服務(wù)uri就可以了,使用起來和spring調(diào)用遠(yuǎn)程服務(wù)接口很像,使用起來很簡潔;

服務(wù)注冊

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

// 實現(xiàn)接口
@Route(path = "/service/hello", name = "測試服務(wù)")
public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String name) {
    return "hello, " + name;
    }

    @Override
    public void init(Context context) {

    }
}

服務(wù)調(diào)用:

public class Test {
    @Autowired
    HelloService helloService;

    @Autowired(name = "/service/hello")
    HelloService helloService2;

    HelloService helloService3;

    HelloService helloService4;

    public Test() {
    ARouter.getInstance().inject(this);
    }

    public void testService() {
     // 1. (推薦)使用依賴注入的方式發(fā)現(xiàn)服務(wù),通過注解標(biāo)注字段,即可使用,無需主動獲取
     // Autowired注解中標(biāo)注name之后,將會使用byName的方式注入對應(yīng)的字段,不設(shè)置name屬性,會默認(rèn)使用byType的方式發(fā)現(xiàn)服務(wù)(當(dāng)同一接口有多個實現(xiàn)的時候,必須使用byName的方式發(fā)現(xiàn)服務(wù))
    helloService.sayHello("monkey0l");
    helloService2.sayHello("monkey01");

    // 2. 使用依賴查找的方式發(fā)現(xiàn)服務(wù),主動去發(fā)現(xiàn)服務(wù)并使用,下面兩種方式分別是byName和byType
    helloService3 = ARouter.getInstance().navigation(HelloService.class);
    helloService4 = (HelloService) ARouter.getInstance().build("/service/hello").navigation();
    helloService3.sayHello("monkey01");
    helloService4.sayHello("monkey01");
    }
}

5. 現(xiàn)有項目ARouter改造實踐

上面介紹了組件化的一些方案和ARouter組件化方案的使用,下面我們就拿筆者真實的項目例子去講解下實施的一些經(jīng)驗。

對于手上有項目需要進(jìn)行組件化的小伙伴,下面的這些內(nèi)容應(yīng)該會對你有所幫助,會給大家實施的一個思路,其實組件化的思路很重要,具體使用什么框架去實施其實是次要的。

1)對現(xiàn)有項目進(jìn)行業(yè)務(wù)功能拆分;

項目實施組件化一般都是因為項目太復(fù)雜或者代碼耦合問題比較嚴(yán)重,一修改代碼就出現(xiàn)問題,所以首先我們要做的就是先對整個APP的功能進(jìn)行拆分,對各個組件功能進(jìn)行邊界劃分,這個工作其實是整個組件化過程的核心,組件化好不好全看組件功能拆分的好不好;當(dāng)時我們拆分的過程主要分為幾部分:

Screenshot 2017-11-21 17.04.13.png
1.殼工程
殼工程主要用于將各個組件組合起來和做一些工程初始化,初始化包含了后續(xù)各個組件會用到的一些庫的初始化,也包括ApplicationContext的初始化工作;
2.基礎(chǔ)類庫
基礎(chǔ)類庫主要還是將各個組件中都會用到的一些基礎(chǔ)庫統(tǒng)一進(jìn)行封裝,例如網(wǎng)絡(luò)請求、圖片緩存、sqllite操作、數(shù)據(jù)加密等基礎(chǔ)類庫,這樣可以避免各個組件都在自己的組件中單獨(dú)引用,而且引用的版本可能都不一樣,導(dǎo)致整個工程外部庫混亂,統(tǒng)一了基礎(chǔ)類庫后,基礎(chǔ)類庫保持相對的穩(wěn)定,這樣各個組件對外部庫的使用是相對可控的,防止出現(xiàn)一些外部庫引出的極端問題,而且這樣的話對于庫的升級也比較好管理;
3.基礎(chǔ)工程
對于每個組件都有一些是公共的抽象,例如我們工程中自己定義的BaseActivity、BaseFragment、自定義控件等,這些對于每個組件都是一樣的,每個組件都基于一樣的基礎(chǔ)工程開發(fā),一方面可以減少開發(fā)工作,另一方面也可以讓各個組件的開發(fā)人員能夠統(tǒng)一架構(gòu)框架,這樣每個組件的技術(shù)代碼框架看起來都是一樣的,也便于后期代碼維護(hù)和人員互備;
4.業(yè)務(wù)模塊
最大的一塊體力工作就是業(yè)務(wù)模塊的實現(xiàn)了,上面的幾部分都實現(xiàn)以后,剩余的主要體力工作就是實現(xiàn)各個拆分出來的業(yè)務(wù)模塊;

2)基礎(chǔ)類庫抽離;

對于原先工程中,由于開發(fā)人員技術(shù)參差不齊、開發(fā)趕進(jìn)度、沒有代碼復(fù)查等原因,導(dǎo)致代碼基礎(chǔ)類庫管理混亂,一個網(wǎng)絡(luò)請求居然都有好幾個外部庫,在組件化過程中我們對常用的幾個公共類庫進(jìn)行了統(tǒng)一,并且都封裝在了一個基礎(chǔ)類庫中。

組件化:ARouter
MVVM:google Databinding
圖片:picasso
網(wǎng)絡(luò):Retrofit
異步框架:RxJava
數(shù)據(jù)庫:greenorm
Hybrid:jsBridge
日志管理:Logger、timber
消息Event:EventBus
消息推送:JPush
APM埋點:友盟
熱更新:Tinker

3)基礎(chǔ)工程抽離;

對于基礎(chǔ)工程抽離其實比較簡單,因為我們原先的工程對于公共類這已經(jīng)抽象的比較好了,主要是在基礎(chǔ)工程中抽象出了BaseActiviry、BaseFragment、BaseApplication、BaseViewModel等公共類,在每個Base類中都已經(jīng)定義了一些必須要實現(xiàn)的抽象類和已經(jīng)定義好的基礎(chǔ)函數(shù)功能,這樣以后每個定義的Activity或者Fragment都繼承了這些Base類的基礎(chǔ)功能,能夠減少很多公共代碼的開發(fā)工作,也可以在基礎(chǔ)類中實現(xiàn)統(tǒng)一異常處理,統(tǒng)一消息捕獲,切面攔截等等,只要是組件中公共的功能都可以在這里實現(xiàn);

4)殼工程開發(fā);

殼工程其實比較簡單,一般殼工程中主要承擔(dān)了項目初始化的工作,在殼的Application中需要對后續(xù)其它組件用到的全局庫進(jìn)行統(tǒng)一初始化,也要承擔(dān)一些例如版本檢測、配置文件加載、刷新、組件加載、熱更新等工作。殼工程主要做的都是些基礎(chǔ)的臟活累活,殼工程開發(fā)好后,就可以進(jìn)行業(yè)務(wù)功能開發(fā)了,每次開發(fā)單模塊功能的時候,可以在殼工程中只引用自己模塊需要的功能進(jìn)行開發(fā)編譯調(diào)試了,不需要全部組件引用,這樣可以大大縮減每次編譯的時間,如果打開instance run那么編譯的速度就更快了;

5)業(yè)務(wù)功能開發(fā);

每個組件的業(yè)務(wù)功能開發(fā)這里就沒什么好說的了,主要就是堆人去實施;

6)全回歸測試;

在整個組件化過程中,最大的問題就是怎么保證組件化后原功能沒有問題,這就要求我們有良好的CI來保障,持續(xù)集成是個很大的話題,后面我們單獨(dú)寫幾篇文章來說下持續(xù)集成的實施經(jīng)驗,這里就不詳細(xì)說了,通過CI不斷的迭代提交代碼,不斷的自動化回歸來保證代碼的質(zhì)量,如果沒有好的CI,實施這么大的一個組件化重構(gòu),風(fēng)險還是非常大的!這里沒有危言聳聽,我們的組件化和CI基本是同步實施的,沒有CI那就意味著,所有組件化后的測試都要求手工回歸去測試,那么測試的工作量是巨大的,而且不能很好的保證重構(gòu)后一定沒有問題,因為是一個線上版本,一旦出了問題那就意味著大量用戶的差評和流失,后果不堪設(shè)想,所以如果開發(fā)團(tuán)隊對于現(xiàn)有產(chǎn)品只是為了趕新潮而去實施組件化,那就算了不要去組件化,如果遇到上面我提到的那些問題了,那就去組件化,但是要切記通過CI來保證質(zhì)量,不然會死的很慘。

小結(jié):

筆者目前一共實施過3個項目的組件化,第一個是傳統(tǒng)國有銀行APP的組件化重構(gòu),使用的是自研的簡單deeplink URL組件化方案,組件化核心代碼量非常小,幾個核心類加起來大概也就1000多行代碼,整個實施過程有個比較好的前提就是,團(tuán)隊已經(jīng)具備比較好的CI能力,而且公司預(yù)留了充足的人力和時間(3個月)讓我們?nèi)嵤┙M件化,期間沒有新需求,估計大部分公司都不會有這樣的待遇,當(dāng)時因為是第一次實施,期間也遇到了很多問題,當(dāng)時還沒有那么多資料可以參考,現(xiàn)在組件化的資料真的是鋪天蓋地,最后經(jīng)過十幾名同事的努力終于完成了整體項目的組件化實施并順利上線,組件化以后再增加新功能那叫一個舒服。。

第二個組件化項目是電商項目,這個項目和上一個銀行的組件化項目最大區(qū)別就在于,互聯(lián)網(wǎng)電商項目是不可能給你3個月完全沒有新需求的,這就要求我們要給在高速飛行的飛機(jī)換零件,一旦出問題那就是機(jī)毀人亡,所以在這個項目的實施過程中,我們主要解決了如何在現(xiàn)有的項目上實施增量的組件化,實施增量組件化的核心思路就是在開發(fā)出殼工程后,將原工程作為一個big組件,一步步的將里面的功能拆分出來,等big組件消失了,那也就拆完了,這個項目用的組件化方案也是自研的一套URL方案,和上一個銀行的組件化方案非常像,只是在其中還實現(xiàn)了組件功能的服務(wù)化發(fā)布,但是沒有實現(xiàn)ARouter的服務(wù)注冊、治理整套機(jī)制,這個項目實施下來就比第一個項目組件化順利的多;

第三個組件化的項目是一個O2O的項目,這個項目是新項目使用組件化實施,完全沒有舊項目的負(fù)擔(dān),而且那個時候ARouter也出來了,我們在項目中也首次使用了ARouter,有了前面兩次的磕磕盼盼,整體實施起來可謂是順風(fēng)順?biāo)疫€是新項目,很快就組件化實施上線。

組件化還是個比較龐大的工程,尤其對于線上項目進(jìn)行組件化重構(gòu),還是存在很大的風(fēng)險,這篇文章對于想實施組件化的同學(xué)應(yīng)該有一定的參考意義,大家看完了如果覺得好就點個贊吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,665評論 25 708
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,785評論 18 139
  • 1 上周日,帶女兒去某音樂中心上課。 兩歲半課程的卡通人物“噗噗嚕”非常可愛,還有關(guān)于“噗噗嚕”的歌,讓小妞一見傾...
    曉達(dá)親子情商療愈閱讀 464評論 0 3
  • 每逢佳節(jié)倍思親,千古不變。又是一年的端午節(jié),又有很多人在外鄉(xiāng)月下盼團(tuán)圓了。 猶記得小時候在每年的五月五...
    差不多先生的123閱讀 307評論 0 1
  • 失去了感知世界的能力 失去掌握身體平衡的能力 心隨風(fēng)飄 腳步沉重 容顏不展 冰冷自心底漫漫的滲透 沁入四肢和骨髓 ...
    楚留飍閱讀 262評論 0 0