組件化與模塊化
組件
組件指的是單一的功能組件绍填,如視頻組件(VideoSDK)歌逢、支付組件(PaySDK)签杈、路由組件(Router)等,每個組件都能單獨(dú)抽出來制作成SDK
模塊
模塊指的是獨(dú)立的業(yè)務(wù)模塊沽一,如直播模塊(LiveModule)盖溺、首頁模塊(HomeModule)、即時通信模塊(IMModule)等锯玛。模塊相對于組件來說粒度更大,模塊可能包含多種不用的組件。
組件化開發(fā)的好處
(1)避免重復(fù)造輪子攘残,可以節(jié)省開發(fā)和維護(hù)的成本拙友。
(2)可以通過組件和模塊為業(yè)務(wù)基準(zhǔn)合理地安排人力,提高開發(fā)效率歼郭。
(3)不同的項(xiàng)目可以共用一個組件或模塊遗契,確保整體技術(shù)方案的統(tǒng)一性。
(4)為未來插件化共同用一套底層模型做準(zhǔn)備
模塊化開發(fā)的好處
(1)業(yè)務(wù)模塊解耦病曾,業(yè)務(wù)移植更加簡單牍蜂。
(2)多團(tuán)隊(duì)根據(jù)業(yè)務(wù)內(nèi)容進(jìn)行并行開發(fā)和測試。
(3)單個業(yè)務(wù)可以單獨(dú)編譯打包泰涂,加快編譯速度鲫竞。
(4)多個App共用模塊,降低研發(fā)和維護(hù)成本逼蒙。
多Module劃分業(yè)務(wù)和基礎(chǔ)功能从绘,這個概念將作為組件化的基礎(chǔ)。
組件化和模塊化的本質(zhì)思想是一樣的是牢,都是為了代碼重用和業(yè)務(wù)解耦僵井,區(qū)別在于模塊化是業(yè)務(wù)導(dǎo)向,組件化是功能導(dǎo)向驳棱。組件化和模塊化的劃分將更好地為項(xiàng)目插件化開路批什。插件化的模塊發(fā)布和正常發(fā)布有著非常大的差異,已經(jīng)脫離了組件化和模塊化的構(gòu)建機(jī)制社搅,插件化擁有更高效的業(yè)務(wù)解耦驻债。
項(xiàng)目搭建
1.首先我們新建一個項(xiàng)目然后新建一個library(componentlib)和兩個組件(logincomponent、minecomponent)罚渐,如下圖:
(1)app:應(yīng)用層用于統(tǒng)籌全部組件却汉,并輸出生成App
(2)logincomponent和minecomponent作為組件來使用,包含一些簡單的業(yè)務(wù)荷并、比如登錄合砂、我的。
(3)componentlib:包含一些基礎(chǔ)庫和對基礎(chǔ)庫的封裝源织,包含圖片加載翩伪、網(wǎng)絡(luò)加載,數(shù)據(jù)存儲(正常是再抽離一層出來谈息,作為Base層)缘屹;還有作為通訊層,來進(jìn)行各個組件的數(shù)據(jù)交互侠仇。
引用關(guān)系是app引用logincomponent和mincomponent轻姿,然后logincomponent和mincomponent引用componentlib犁珠;
2、項(xiàng)目中的配置
(1)在gradle.properties配置全局gradle環(huán)境
(2)app build.gradle的配置
logincomponent和minecomponent配置一樣互亮,下面是logincomponent build.gradle的配置
注意這里必須要配置兩個AndroidManifest.xml犁享,一個用于獨(dú)立運(yùn)行,一個用做組件
獨(dú)立運(yùn)行下的AndroidManifest如下:
作為組件的AndroidManifest.xml如下
minecomponent組件的配置一樣豹休,這里就不貼圖了炊昆。
代碼實(shí)現(xiàn)
配置完成后,我們就可以開始重頭戲威根,擼代碼凤巨。
組件化的時候我們首先要解決兩個問題
1、回頭看下項(xiàng)目結(jié)構(gòu)圖洛搀,由于App引用了logincomponent和minecomponent這個兩個組件敢茁,而他們兩個作為組件要怎么拿到App的Application這個項(xiàng)目唯一的全局上下文。
2姥卢、組件間如何通訊卷要。
組件獲取到上下文
對于問題1,我們可以在App層独榴,通過反射將Application設(shè)置到兩個組建中僧叉,這樣組件就可以使用到App層的Application。
(1)首先在componentlib中定義接口IAppComponent:
public interface IAppComponent {
void initializa(Application application);
}
(2)然后組件的Application繼承該接口:
public class LoginApp extends Application implements IAppComponent {
private static Application application;
public static Application getApplicatoin(){
return application;
}
//獨(dú)立運(yùn)行的時候棺榔,這里是入口
@Override
public void onCreate() {
super.onCreate();
initializa(this);
}
//這里獲取到了全局的Application
@Override
public void initializa(Application application) {
this.application = application;
}
}
注意上面的onCreate()方法瓶堕,作為組件的時候是不會被調(diào)用的,但是獨(dú)立運(yùn)行的時候onCreate就是入口了症歇。
(3)App的Application通過反射進(jìn)行組件的實(shí)例化
public class AppConfig {
public static final String[] COMPONENTS = {
"com.kangjj.logincomponent.LoginApp",
"com.kangjj.manecomponent.MineApp"
};
}
public class MainApp extends Application implements IAppComponent {
private MainApp application;
public MainApp getApplication() {
return application;
}
@Override
public void onCreate() {
super.onCreate();
initializa(this);
}
//組件實(shí)例化
@Override
public void initializa(Application application) {
this.application = (MainApp) application;
for (String component : AppConfig.COMPONENTS) {
try {
//通過反射獲取到需要實(shí)例化的組件
Class<?> clazz = Class.forName(component);
Object object = clazz.newInstance();
//將實(shí)例化的對象強(qiáng)轉(zhuǎn)為IAppComponent郎笆,然后進(jìn)行組件的實(shí)例化
if(object instanceof IAppComponent){
((IAppComponent)object).initializa(application);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
這樣組件就獲取到了全局的Application
組件通訊
(1)創(chuàng)建組件的通訊工廠管理類
public class ServiceFactory {
private static final ServiceFactory instance = new ServiceFactory();
public static ServiceFactory getInstance(){
return instance;
}
private ServiceFactory(){}
private ILoginService mLoginService;
private IMineService mMineService;
public ILoginService getLoginService() {
//如果將登陸組件移除,App進(jìn)行無登陸組件的業(yè)務(wù)
if(mLoginService == null){
mLoginService = new EmptyLoginService();
}
return mLoginService;
}
//設(shè)置logincomponent的通訊接口忘晤,在logincomponent的初始化的地方實(shí)現(xiàn)
public void setLoginService(ILoginService loginService) {
this.mLoginService = loginService;
}
public IMineService getMineService() {
if(mMineService == null){
mMineService = new EmptyMineService();
}
return mMineService;
}
public void setMineService(IMineService mineService) {
this.mMineService = mineService;
}
}
(2)定義ILoginService接口,該接口主要是用于兩個類的實(shí)現(xiàn):
public interface ILoginService {
void launch(Context context, String targetClass);
Fragment newUserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle);
}
logincomponent里的LoginService,這里進(jìn)行啟動組件的Actiivty宛蚓,或者獲取Fragment等操作。
public class LoginService implements ILoginService {
@Override
public void launch(Context context, String targetClass) {
Intent intent = new Intent(context,LoginActivity.class);
intent.putExtra(LoginActivity.EXTRA_TARGET_CLASS,targetClass);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
@Override
public Fragment newUserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
UserInfoFragment fragment = new UserInfoFragment();
fragment.setArguments(bundle);
fragmentManager.beginTransaction().add(viewId,fragment).commit();
return fragment;
}
}
componentlib的EmptyLoginService
public class EmptyLoginService implements ILoginService {
@Override
public void launch(Context context, String targetClass) {
}
@Override
public Fragment newUserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
return null;
}
}
(3)接下來是初始化LoginService
public class LoginApp extends Application implements IAppComponent {
···
//省略一些代碼
@Override
public void initializa(Application application) {
this.application = application;
ServiceFactory.getInstance().setLoginService(new LoginService());
}
}
(4)調(diào)用设塔,進(jìn)行通訊
下面是LoginActivity和UserInfoFragment凄吏,前者在App進(jìn)行啟動調(diào)用,后者用來獲取Fragment在App進(jìn)行顯示闰蛔。
public class LoginActivity extends AppCompatActivity {
public static final String EXTRA_TARGET_CLASS = "EXTRA_TARGET_CLASS";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
}
public class UserInfoFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.userinfo_fragment,null);
}
}
對應(yīng)的布局文件是很簡單的TextView痕钢,這里就不顯示了。
App里MainActivity的使用:
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//跳轉(zhuǎn)到登錄頁面
public void login(View view){
ServiceFactory.getInstance().getLoginService().launch(this,"");
}
public void goMine(View view){
ServiceFactory.getInstance().getMineService().launch(this,2 ,REQUEST_CODE);
}
//獲取組件的Userinfo頁面進(jìn)行顯示
public void getFragment(View view){
ServiceFactory.getInstance().getLoginService().newUserInfoFragment(getSupportFragmentManager(),R.id.container,new Bundle());
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==REQUEST_CODE && resultCode==RESULT_OK){
Toast.makeText(this,data.getStringExtra("fromMine"),Toast.LENGTH_LONG).show();
}
}
}
對應(yīng)xml布局文件如下:
<?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"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="login"
android:onClick="login"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="go mine"
android:onClick="goMine"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="get UserInfoFragment"
android:onClick="getFragment"/>
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
(5)運(yùn)行序六,運(yùn)行前記得修改gradle.properties
#login組件獨(dú)立運(yùn)行的標(biāo)志
loginRunAlone = false
#mine組件獨(dú)立運(yùn)行的標(biāo)志
mineRunAlone = false
然后編譯任连、運(yùn)行。
總結(jié)
組件化并不復(fù)雜例诀,本質(zhì)上也是gradle配置的變化而已随抠,組件化核心思想是面向接口變成裁着,定義公公組件,所有組件都依賴公共組件拱她。
文章有何不妥之處跨算,歡迎指正,與討論椭懊,謝謝觀看。