Android組件化二【跨Module調用方法】
經過前一篇的文章,我們已經可以將Module單獨運行了,這一篇的話,我們繼續探索下組件化(其實是模塊化)的內容。
我們這次使用的是AppJoint的組件化方案,仍舊使用上篇文章的代碼來做演示。
Appjoint的詳細說明文檔在 迴歸初心:極簡 Android 組件化方案 — AppJoint !
一、目標
這次我們的目標是在app模塊的MainActivity中啓動food模塊的FoodActivity,然後在food模塊的FoodActivity中啓動movie模塊中的MovieActivity。其實但是啓動Activity的話我們可以直接採取Arouter的路由方案來進行,但是這裏的話我們不使用Arouter,而是使用AppJoint提供的方法來演示。
二、準備工作
在food模塊中創建FoodActivity,在movie模塊中創建MovieActivity,app模塊中有默認的MainActivity。創建好必備的Activity後我們就需要準備跨模塊來啓動Activity了。
三、方案
其實當兩個模塊之間想要通訊的話,我們一般需要暴漏出來一個接口,然後給另一個模塊調用,然而這樣的話可能就會發生一個模塊依賴另一個模塊的問題了,所以我們需要將這個接口單獨暴漏出來,這時有兩種方案:
- 添加一個基礎路由模塊例如叫router,統一聲明所有對外暴漏的接口。所有需要實現或者調用其他模塊的都要依賴該router模塊。
- 每個需要對外暴漏方法的模塊添加一個專門對外暴漏接口的模塊。例如movie模塊想暴露出來一個方法,那麼添加一個movieApi模塊,該模塊只聲明對外暴漏的接口,movie模塊需要依賴movieApi模塊並實現相應的方法,假如food模塊需要調用該方法,那麼food模塊也需要依賴movieApi模塊。
方案就是這樣,我們按照第二種來進行嘗試:
3.1、新建movieApi模塊
在該模塊中,只創建對外暴漏的接口IMovieRouter,不包含任何業務邏輯,業務只交由movie模塊來實現。該接口包含一個啓動MovieActivity的方法,代碼如下:
package com.cooloongwu.movieapi;
import android.content.Context;
public interface IMovieRouter {
void startMovieActivity(Context context);
}
3.2、依賴movieApi模塊
3.2.1、movie模塊依賴
movie模塊依賴movieApi模塊後,需要實現IMovieRouter接口,代碼如下:
package com.cooloongwu.movie;
import android.content.Context;
import com.cooloongwu.movieapi.IMovieRouter;
import io.github.prototypez.appjoint.core.ServiceProvider;
@ServiceProvider
public class MovieRouterImpl implements IMovieRouter {
@Override
public void startMovieActivity(Context context) {
Intent intent = new Intent(context, MovieActivity.class);
context.startActivity(intent);
}
}
注意: MovieRouterImpl 類需要使用 ServiceProvider 註解!
注意: MovieRouterImpl 類需要使用 ServiceProvider 註解!
注意: MovieRouterImpl 類需要使用 ServiceProvider 註解!
3.2.2、food模塊依賴
food模塊依賴movieApi模塊後,直接可以在FoodActivity中調用IMovieRouter 中的方法了。代碼如下:
IMovieRouter movieRouter = AppJoint.service(IMovieRouter.class);
movieRouter.startMovieActivity(this);
3.3、其他流程
因爲app模塊直接依賴了food和movie模塊,所以在app模塊中可以直接啓動food模塊中的FoodActivity了,但是正常開發中不建議在app模塊中做過多的操作,app只作爲一個殼即可。然後在FoodActivity中可以通過AppJoint提供的**AppJoint.service()**來調用其他模塊的方法。
四、單個模塊運行的問題
以上所有步驟當你運行app模塊的時候是沒有問題的,但是當你單獨運行food模塊的時候,此時問題就來了,因爲我們在food模塊中調用了movie模塊中的方法。而單獨food運行的時候,movie模塊是完全不參與的,所以就沒了IMovieRouter 的實現,當我們調用AppJoint.service()就會獲取到null,這時也會產生各種問題。
怎麼解決呢?
還記得我們第一篇文章的殼模塊麼,我們可以在food模塊的殼模塊(比如我們新建可模塊叫runfood模塊)中做些操作,因爲這個模塊在正式打包的時候是不會參與打包的,所以我們可以隨意在其中添加代碼。既然沒有IMovieRouter 的實現,那我們就在可模塊中去實現,當然正常邏輯是走不通的,我們就在實現裏面進行打印或者進行Toast操作吧。
4.1、food模塊中的依賴修改
在原來的food模塊中,可能我們直接依賴movieapi的方法是使用的implementation 。
dependencies {
...
implementation project(path: ':movieapi')
}
這樣當runfood模塊依賴food模塊的時候,runfood模塊就會訪問不到movieapi模塊,也就是無法訪問到IMovieRouter 接口,此時我們需要把 implementation
修改爲api
,這樣將movieapi模塊也暴漏給runfood模塊。
4.2、runfood模塊中實現IMovieRouter
我們給runfood模塊添加IMovieRouter 的Mock類,只打印日誌:
package com.cooloongwu.foodrun;
import android.content.Context;
import android.util.Log;
import com.cooloongwu.movieapi.IMovieRouter;
public class MovieRouterMockImpl implements IMovieRouter {
@Override
public void startMovieActivity(Context context) {
Log.e("MovieRouterMockImpl", "startMovieActivity");
}
}
4.3、調用IMovieRouter
其實在使用IMovieRouter的等其他暴漏的接口的時候可以直接將這些接口實例存放在類(例如Router類,演示用)中,代碼如下:
package com.cooloongwu.food;
import com.cooloongwu.movieapi.IMovieRouter;
import io.github.prototypez.appjoint.AppJoint;
public class Router {
public static IMovieRouter movieRouter = AppJoint.service(IMovieRouter.class);
}
使用的時候直接使用 Router.movieRouter.startMovieActivity(this); 。
這樣的話當然也可以對其進行統一賦值了,也就是我們可以在runfood模塊中添加Application,然後在Application的onCreate()方法中對Router.movieRouter進行賦值,如下:
Router.movieRouter = new MovieRouterMockImpl();
這樣,當我們在單獨運行的時候就不會出現AppJoint.service()獲取的是null的問題了,而是我們Mock的這麼一個實現。