首先粘貼源碼地址,歡迎frok,歡迎start
源碼地址
目前,Android 組件化普遍使用于移動(dòng)開發(fā),但是組件化的初衷是為了解耦代碼,并行開發(fā)效率;小型app似乎會(huì)care不到,完全解耦的組件化會(huì)在app越來越臃腫的時(shí)候帶來很大的提升;
1.組件化介紹
ok,那么我們需要知道完全解耦的組件化框架應(yīng)該注意哪些點(diǎn):
- 主app只加載業(yè)務(wù)組件,不可調(diào)用組件;組件與組件之間不存在調(diào)用關(guān)系;這樣無論是主app和業(yè)務(wù)組件都是完全獨(dú)立,完全解耦的;
- 主app和組件都依賴common組件,通過common的注冊(cè)和分發(fā)實(shí)現(xiàn)組件之間的交互,這個(gè)common我們姑且叫做業(yè)務(wù)主線
- android中page使用common下層接口和路由進(jìn)行實(shí)現(xiàn)(在本框架中,ARouter實(shí)現(xiàn)Activity跳轉(zhuǎn),ARouter-Interceptor實(shí)現(xiàn)Activity跳轉(zhuǎn)的攔截;Fragment通過common下沉注冊(cè)分發(fā)實(shí)現(xiàn)Fragment的填充)
- 每一個(gè)組件應(yīng)當(dāng)是一個(gè)app可單獨(dú)編譯:Library和Application之間轉(zhuǎn)化使用gradle配置相應(yīng)的Manifest和applicationId
2.單獨(dú)編譯組件化配置(gradle)
依賴關(guān)系
- App 依賴common
- Home/Login/News 依賴common
- common 依賴component-base
2.1. 首先在整個(gè)工程的gradle.properties中配置組件 Library/Application切換的開關(guān):
isRunLogin = false //login組件
isRunHome = false //home組件
isRunNews = false //news組件
2.2. 由于android中Library(組件)/Application切換時(shí)的差異,需要單獨(dú)配置主見
以home組件為例:
首先開build.gradle:
//注釋1: 配置切換application/Library的打包
if (isRunHome.toBoolean()){
apply plugin: 'com.android.application'
}else{
apply plugin: 'com.android.library'
}
android {
.......
sourceSets {
//注釋2: Library/Application切換 AndroidManifest
main {
if (isRunLogin.toBoolean()){
manifest.srcFile 'src/main/AndroidManifest.xml'
}else{
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
}
}
}
......
}
......
- 注釋1: 切換application/Libaray的打包配置
-
注釋2: Application為單獨(dú)編譯,需要有applicationId,并且主Activity需要配置main屬性;
Libaray為集成編譯,組件不能有applicationId,且不可以設(shè)置啟動(dòng)的main Activity
下面看集成編譯(Library)和單獨(dú)編譯(Application)的Manifest配置:
1.png
//集成編譯,打包為Library
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="component.android.com.home">
<application android:theme="@style/home_AppTheme">
<activity android:name=".view.activity.HomeActivity"/>
</application>
</manifest>
//單獨(dú)編譯,打包為單獨(dú)Application 可單獨(dú)編譯
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="component.android.com.home">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".global.HomeApp"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/home_AppTheme">
<activity android:name=".view.activity.HomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3.組件之間activity跳轉(zhuǎn)(Actiivity跳轉(zhuǎn))
3.1. 組件之間的activity跳轉(zhuǎn),這里使用ARouter
ARouter是阿里開源的一種頁面跳轉(zhuǎn)task
首先看ARouter在build.gralde的配置:
//主app build.gradle
......
dependencies {
......
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
//home/login/news 組件 build.gradle
dependencies {
........
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
}
//common build.gradle
dependencies {
......
api 'com.alibaba:arouter-api:1.3.1'
// arouter-compiler 的注解依賴需要所有使用 ARouter 的 module 都添加依賴
annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
}
3.2 在app和各組件中進(jìn)行page跳轉(zhuǎn)
- 首先是ARouter的初始化
public class MainApplication extends BaseApp {
@Override
public void onCreate() {
super.onCreate();
//在主app中初始化ARouter
initRouter();
initMoudleApp(this);
initMoudleData(this);
}
private void initRouter() {
if (BuildConfig.DEBUG){
//打印日志
ARouter.openLog();
ARouter.openDebug();
}
ARouter.init(this);
}
@Override
public void initMoudleApp(Application application) {
for (String moduleApp : AppConfig.moduleApps) {
try {
Class clazz = Class.forName(moduleApp);
BaseApp baseApp = (BaseApp) clazz.newInstance();
baseApp.initMoudleApp(this);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
@Override
public void initMoudleData(Application application) {
for (String moduleApp : AppConfig.moduleApps) {
try {
Class clazz = Class.forName(moduleApp);
BaseApp baseApp = (BaseApp) clazz.newInstance();
baseApp.initMoudleData(this);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}
//組件中 home--HomeApplication
public class HomeApp extends BaseApp {
@Override
public void initMoudleApp(Application application) {
if (BuildConfig.DEBUG){
//打印日志
ARouter.openLog();
ARouter.openDebug();
}
ARouter.init(this);
}
@Override
public void initMoudleData(Application application) {
}
}
這里需要注意一下 當(dāng)集成編譯時(shí)候 ,組件僅僅是一個(gè)組件,不會(huì)單獨(dú)具備Applicagtion入口,所以需要在主app的MainApplication中利用反射的方式 initMoudleData/initMoudleData進(jìn)行ARouter等初始化的配置;
下面看ARouter的跳轉(zhuǎn)實(shí)例:
//app/MainActivity
....
private void initClick() {
findViewById(R.id.btn_nav_home).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ARouter.getInstance().build("/home/homeActivity").navigation()
}
}
在app中實(shí)現(xiàn)跳轉(zhuǎn),但是這個(gè) path/home/homeActivity
需要在home組件目標(biāo)位置添加注解才能實(shí)現(xiàn)activity的跳轉(zhuǎn):
@Route(path = "/myhome/homeActivity")
public class HomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
}
}
這樣 就成功實(shí)現(xiàn)類組件之間的activity的跳轉(zhuǎn);
4.組件之間的邏輯交互
App點(diǎn)擊跳轉(zhuǎn)home,須判斷登錄邏輯:
- 1.登錄則跳轉(zhuǎn)home組件的homeActivity
-
2.未登錄則跳轉(zhuǎn)login組件的loginActivity,點(diǎn)擊登錄,再重復(fù)以上邏輯
這樣 主app,home和login就實(shí)現(xiàn)了一個(gè)簡單的交互邏輯
2.png
首先開component:
//ILoginService
public interface ILoginService {
boolean getLoginStatus();
int getLoginUserId();
}
//DefultLoginService
public class DefultLoginService implements ILoginService {
@Override
public boolean getLoginStatus() {
return false;
}
@Override
public int getLoginUserId() {
return 0;
}
}
//ComponentServiceFactory
public class ComponentServiceFactory {
......
public static ComponentServiceFactory getInstance(Context context){
if (instance == null){
synchronized (ComponentServiceFactory.class){
if (instance == null){
instance = new ComponentServiceFactory();
}
}
}
return instance;
}
private ILoginService loginService;
public void setLoginService(ILoginService iloginService){
loginService = iloginService;
}
public ILoginService getLoginService(){
if (loginService == null){
loginService = new DefultLoginService();
}
return loginService;
}
}
然后在login中通過common的ComponentServiceFactory注冊(cè)對(duì)應(yīng)的loginService
//LoginService
public class LoginService implements ILoginService {
@Override
public boolean getLoginStatus() {
return AccountUtils.getInstance().isAccountStatus();
}
@Override
public int getLoginUserId() {
return 0;
}
}
//Login組件LoginApp注冊(cè)service
........
public class LoginApp extends BaseApp {
@Override
public void initMoudleApp(Application application) {
Log.i("LoginApp","initMoudleApp");
if (BuildConfig.DEBUG){
//打印日志
ARouter.openLog();
ARouter.openDebug();
}
ARouter.init(this);
ComponentServiceFactory.getInstance(this).setLoginService(new LoginService());
}
@Override
public void initMoudleData(Application application) {
}
}
在App-MainActivity中跳轉(zhuǎn)homeActivity,在home組件中使用ARouter的攔截器:
//app-mainActivity
private void initClick() {
findViewById(R.id.btn_nav_home).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ARouter.getInstance().build("/home/homeActivity").navigation(MainActivity.this, new NavCallback() {
//ARouter攔截器的監(jiān)聽
@Override
public void onArrival(Postcard postcard) {
LogUtils.LogI("loginInterceptor","done");
}
@Override
public void onFound(Postcard postcard) {
//super.onFound(postcard);
LogUtils.LogI("loginInterceptor","found");
}
@Override
public void onLost(Postcard postcard) {
//super.onLost(postcard);
LogUtils.LogI("loginInterceptor","lost");
}
@Override
public void onInterrupt(Postcard postcard) {
//super.onInterrupt(postcard);
LogUtils.LogI("loginInterceptor","interrupt");
}
});
}
});
}
//home-HomeInterceptor
@Interceptor(priority = 1,name = "homeInterceptor")
public class HomeInterceptor implements IInterceptor {
private Context context;
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
switch (postcard.getPath()){
case "/myhome/homeActivity":
//通過component進(jìn)行邏輯交互
if (ComponentServiceFactory.getInstance(context).getLoginService().getLoginStatus()){
callback.onContinue(postcard);
}else {
ARouter.getInstance().build("/login/loginActivity").navigation();
//callback.onInterrupt(new RuntimeException("請(qǐng)登錄"));
//callback.onContinue(postcard);
}
break;
default:
callback.onContinue(postcard);
break;
}
}
@Override
public void init(Context context) {
this.context = context;
}
}
//login-loginInterceptor
@Interceptor(priority = 2,name = "loginInterceptor")
public class LoginInterceptor implements IInterceptor {
private Context context;
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
switch (postcard.getPath()){
case "/login/loginActivity":
LogUtils.LogI("loginInterceptor","請(qǐng)點(diǎn)擊登錄按鈕");
callback.onContinue(postcard);
break;
default:
callback.onContinue(postcard);
//在每一個(gè)組件中添加一個(gè)navi的攔截器 邏輯在
}
}
@Override
public void init(Context context) {
this.context = context;
}
}
- 1.在跳轉(zhuǎn)homeActivity時(shí),跳轉(zhuǎn)到home組件的homeInterceptor攔截器
- 2.在homeInterceptor中通過component獲取login注冊(cè)的lohginservice來獲取登錄狀態(tài),實(shí)現(xiàn)下一步跳轉(zhuǎn)
可以看到 app 通過ARouter跳home home通過component的注冊(cè)分發(fā),判斷登錄邏輯 進(jìn)行下一步跳轉(zhuǎn);這樣就實(shí)現(xiàn)了不依賴其他組件的邏輯交互
5.組件化fragment解耦
在android中我們使用最多的就是fragment,一般情況下 我們會(huì)實(shí)例化fragment再進(jìn)行下一步邏輯;為了解耦我們?cè)赾omponent中注冊(cè)fragment接口,在相應(yīng)組件中注冊(cè)fragmentservice,在其他組件中實(shí)現(xiàn)分發(fā):
//component-LoginFragmentService
public class LoginFragmentService implements IFragmentService {
@Override
public Fragment getFragment(String tag) {
return new LginHomeFragment();
}
@Override
public void newFragment(Activity activity, int resId, FragmentManager fragmentManager, Bundle bundle, String tag) {
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(resId,new LginHomeFragment(),tag);
transaction.commit();
}
}
//component-ComponentServiceFactory
public class ComponentServiceFactory {
private static volatile ComponentServiceFactory instance;
private IFragmentService newsFragmentService;
private IFragmentService homeFragmentService;
private IFragmentService loginFragmentService;
public static ComponentServiceFactory getInstance(Context context){
if (instance == null){
synchronized (ComponentServiceFactory.class){
if (instance == null){
instance = new ComponentServiceFactory();
}
}
}
return instance;
}
........
//主冊(cè)fragmentservice入口
public void setHomeFragmentService(IFragmentService iFragmentService){
homeFragmentService = iFragmentService;
}
public void setLoginFragmentService(IFragmentService iFragmentService){
loginFragmentService = iFragmentService;
}
public void setNewsFragmentService(IFragmentService iFragmentService){
newsFragmentService = iFragmentService;
}
public IFragmentService getNewsFragmentService() {
return newsFragmentService;
}
public IFragmentService getHomeFragmentService() {
return homeFragmentService;
}
public IFragmentService getLoginFragmentService() {
return loginFragmentService;
}
}
在home組件中進(jìn)行fragmentservice的注冊(cè)工作:
//home-HomeApp
public class HomeApp extends BaseApp {
@Override
public void initMoudleApp(Application application) {
if (BuildConfig.DEBUG){
//打印日志
ARouter.openLog();
ARouter.openDebug();
}
ARouter.init(this);
ComponentServiceFactory.getInstance(this).setHomeFragmentService(new HomeFragmentService());
}
@Override
public void initMoudleData(Application application) {
}
}
在App中調(diào)用:
app-MainActivity:
private void initBaseView() {
FragmentManager supportFragmentManager = getSupportFragmentManager();
ComponentServiceFactory.getInstance(this)
.getHomeFragmentService().newFragment(this,R.id.content,supportFragmentManager,null,null);
}
這樣一個(gè)home組件中的homeFragment就加載到主app的xml中
同理組件之間的fragment引用亦如此
注意框架中component-IFragmentService實(shí)現(xiàn)了兩個(gè)方法:
//獲取目標(biāo)的fragment來進(jìn)行操作
Fragment getFragment(String tag);
//用于固定的區(qū)域來填充相應(yīng)fragment
void newFragment(Activity activity, int resId, FragmentManager fragmentManager, Bundle bundle, String tag);
-
getFragment(String tag);
為獲取目標(biāo)fragment接口,獲取到實(shí)例之后開發(fā)者自己實(shí)現(xiàn)fragment相關(guān)邏輯 -
newFragment(...);
適用于在布局中靜態(tài)添加fragment,一步到位
當(dāng)然框架中還有部分限制組件資源的gradle配置,有興趣可以在github下載demo
源碼地址
感謝閱讀 歡迎github star