Android组件化,完全解耦

目前,Android 组件化普遍使用于移动开发,但是组件化的初衷是为了解耦代码,并行开发效率;小型app似乎会care不到,完全解耦的组件化会在app越来越臃肿的时候带来很大的提升;

1.组件化介绍

ok,那么我们需要知道完全解耦的组件化框架应该注意哪些点:

  1. 主app只加载业务组件,不可调用组件;组件与组件之间不存在调用关系;这样无论是主app和业务组件都是完全独立,完全解耦的;
  2. 主app和组件都依赖common组件,通过common的注册和分发实现组件之间的交互,这个common我们姑且叫做业务主线
  3. android中page使用common下层接口和路由进行实现(在本框架中,ARouter实现Activity跳转,ARouter-Interceptor实现Activity跳转的拦截;Fragment通过common下沉注册分发实现Fragment的填充)
  4. 每一个组件应当是一个app可单独编译:Library和Application之间转化使用gradle配置相应的Manifest和applicationId

2.单独编译组件化配置(gradle)


依赖关系

  • App 依赖common
  • Home/Login/News 依赖common
  • common 依赖component-base

2.1. 首先在整个工程的gradle.properties中配置组件 Library/Application切换的开关:

1
2
3
isRunLogin = false  //login组件
isRunHome = false //home组件
isRunNews = false //news组件

2.2. 由于android中Library(组件)/Application切换时的差异,需要单独配置主见
以home组件为例:
首先开build.gradle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//注释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为单独编译,需要有applicationId,并且主Activity需要配置main属性;
    Libaray为集成编译,组件不能有applicationId,且不可以设置启动的main Activity
    下面看集成编译(Library)和单独编译(Application)的Manifest配置:
1
2
3
4
5
6
7
8
9
//集成编译,打包为Library
<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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//单独编译,打包为单独Application 可单独编译
<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跳转(Actiivity跳转)

3.1. 组件之间的activity跳转,这里使用ARouter

ARouter是阿里开源的一种页面跳转task

首先看ARouter在build.gralde的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//主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和各组件中进行page跳转

  • 首先是ARouter的初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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) {

}
}

这里需要注意一下 当集成编译时候 ,组件仅仅是一个组件,不会单独具备Applicagtion入口,所以需要在主app的MainApplication中利用反射的方式 initMoudleData/initMoudleData进行ARouter等初始化的配置; 下面看ARouter的跳转实例:

1
2
3
4
5
6
7
8
9
//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中实现跳转,但是这个 path/home/homeActivity需要在home组件目标位置添加注解才能实现activity的跳转:

1
2
3
4
5
6
7
8
9
@Route(path = "/myhome/homeActivity")
public class HomeActivity extends AppCompatActivity {

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

这样 就成功实现类组件之间的activity的跳转;

4.组件之间的逻辑交互

App点击跳转home,须判断登录逻辑:

  • 1.登录则跳转home组件的homeActivity
  • 2.未登录则跳转login组件的loginActivity,点击登录,再重复以上逻辑
    这样 主app,home和login就实现了一个简单的交互逻辑

    首先开component:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//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注册对应的loginService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//LoginService
public class LoginService implements ILoginService {
@Override
public boolean getLoginStatus() {
return AccountUtils.getInstance().isAccountStatus();
}

@Override
public int getLoginUserId() {
return 0;
}
}

//Login组件LoginApp注册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中跳转homeActivity,在home组件中使用ARouter的拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//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拦截器的监听
@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进行逻辑交互
if (ComponentServiceFactory.getInstance(context).getLoginService().getLoginStatus()){
callback.onContinue(postcard);
}else {
ARouter.getInstance().build("/login/loginActivity").navigation();
//callback.onInterrupt(new RuntimeException("请登录"));
//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","请点击登录按钮");
callback.onContinue(postcard);
break;
default:
callback.onContinue(postcard);
//在每一个组件中添加一个navi的拦截器 逻辑在

}
}

@Override
public void init(Context context) {
this.context = context;
}
}
  • 1.在跳转homeActivity时,跳转到home组件的homeInterceptor拦截器
  • 2.在homeInterceptor中通过component获取login注册的lohginservice来获取登录状态,实现下一步跳转

可以看到 app 通过ARouter跳home home通过component的注册分发,判断登录逻辑 进行下一步跳转;这样就实现了不依赖其他组件的逻辑交互

5.组件化fragment解耦

在android中我们使用最多的就是fragment,一般情况下 我们会实例化fragment再进行下一步逻辑;为了解耦我们在component中注册fragment接口,在相应组件中注册fragmentservice,在其他组件中实现分发:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//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;
}

........
//主册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组件中进行fragmentservice的注册工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//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中调用:

1
2
3
4
5
6
7
// app-MainActivity:
private void initBaseView() {
FragmentManager supportFragmentManager = getSupportFragmentManager();
ComponentServiceFactory.getInstance(this)
.getHomeFragmentService().newFragment(this,R.id.content,supportFragmentManager,null,null);

}

这样一个home组件中的homeFragment就加载到主app的xml中
同理组件之间的fragment引用亦如此
注意框架中component-IFragmentService实现了两个方法:

1
2
3
4
5
//获取目标的fragment来进行操作
Fragment getFragment(String tag);

//用于固定的区域来填充相应fragment
void newFragment(Activity activity, int resId, FragmentManager fragmentManager, Bundle bundle, String tag);
  • getFragment(String tag);为获取目标fragment接口,获取到实例之后开发者自己实现fragment相关逻辑
  • newFragment(...);适用于在布局中静态添加fragment,一步到位

当然框架中还有部分限制组件资源的gradle配置,有兴趣可以在github下载demo

坚持原创技术分享,您的支持将鼓励我继续创作!