AA插件化

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jielundewode/article/details/83269777

最近发现的一个相当不错的插件化方案!链接

1.AA的设置

类库的 defaultConfig模块语句中加入如下代码:

       javaCompileOptions {
           annotationProcessorOptions {
               arguments = ["library": "true"]
           }
       }

dependencies和app写法一致:

  kapt "org.androidannotations:androidannotations:$AAVersion"
  compileOnly "org.androidannotations:androidannotations-api:$AAVersion"

AA的资源id写法与app中不同:

@EActivity(resName = "view_actionsheet")
open class KotlinActitivy : AppCompatActivity() {
    @Click(resName = ["txt_cancel"])
    open fun clickCancel(){
        finish()
    }
}

2.模块分层

projectRoot
  +--app
  +--module1
  +--module2
  +--standalone
  |  +--module1Standalone
  |  +--module2Standalone   

在项目根目录下的 settings.gradle 里的代码是这样的:

// main app
include ':app'
// library modules
include ':module1'
include ':module2'
// for standalone modules
include ':standalone:module1Standalone'
include ':standalone:module2Standalone'

app 模块的 build.gradle 中的依赖部分代码如下:

dependencies {
    implementation project(':module1')
    implementation project(':module1')
}

以 standalone/module1Standalone 为例,它对应的 build.gradle 中的依赖为:

dependencies {
    implementation project(':module1')
}

在 Android Studio 中创建模块,默认模块是位于项目根目录之下的,如果希望把模块移动到某个文件夹下面,需要对模块右键,选择 “Refactor – Move” 移动到指定目录之下。

2.1为每个模块准备 Application

在组件化之前,我们常常把项目中需要在启动时完成的初始化行为,放在自定义的 Application 中,根据本人的项目经验,初始化行为可以分为以下两类:

  1. 业务相关的初始化。例如服务器推送长连接建立,数据库的准备,从服务器拉取 CMS 配置信息等。
  2. 与业务无关的技术组件的初始化。例如日志工具、统计工具、性能监控、崩溃收集、兼容性方案等。

针对问题1:

@ModuleSpec
public class Module1Application extends Application {

   @Override
   public void onCreate() {
       super.onCreate();
       // do module1 initialization
       Log.i("module1", "module1 init is called");
   }
}

针对2,因为可能每个模块都需要初始化,这段初始化逻辑可以放在Standalone模块中:

public class Module1StandaloneApplication extends Module1Application {
//注意继承自上面类库中的Module1Application 
    @Override
    public void onCreate() {
        // module1 init inside super.onCreate()
        super.onCreate();
        // initialization only used for running module1 standalone
        Log.i("module1Standalone", "module1Standalone init is called");
    }
}

注意上面代码中的@ModuleSpec注解,告知 AppJoint 框架,我们需要确保当前模块该 Application 中的初始化行为,能够在最终全量编译时,被主 App 的 Application 类调用到。所以对应的,我们的主 App 模块(app 模块)的自定义 Application 类也需要被一个注解 – AppSpec 标记:

@AppSpec
public class App extends Application {
    ...
}

2.2 全局获取ApplicationContext的变化

@ModuleSpec
public class Module1Application extends Application {
    
    public static Application INSTANCE;//注意是Application 非ApplicationContext

    @Override
    public void onCreate() {
        super.onCreate();
        INSTANCE = (Application)getApplicationContext();
        // do module1 initialization
        Log.i("module1", "module1 init is called");
    }
}

3 模块间调用

理想情况下是不同模块做不同业务,不存在相互调用,但项目改造初期不太可能实现,用router模块来解决,我们在这个模块内定义 所有业务模块希望暴露给其它模块调用的方法,如下图:

projectRoot
  +--app
  +--module1
  +--module2
  +--standalone
  +--router
  |  +--main
  |  |  +--java
  |  |  |  +--com.yourPackage
  |  |  |  |  +--AppRouter.java
  |  |  |  |  +--Module1Router.java
  |  |  |  |  +--Module2Router.java

在上面的项目结构层次中,我们在新建的 router 模块下定义了 3 个 接口:

AppRouter 接口声明了 app 模块暴露给 module1、module2 的方法的定义。
Module1Router 接口声明了 module1 模块暴露给 app、module2 的方法的定义。
Module2Router 接口声明了 module2 模块暴露给 module1、app 的方法的定义。
以 AppRouter 接口文件为例,这个接口的定义如下:

public interface AppRouter {
    /**
     * 普通的同步方法调用
     */
    String syncMethodOfApp();

    /**
     * 以 RxJava 形式封装的异步方法
     */
    Observable<String> asyncMethod1OfApp();

    /**
     * 以 Callback 形式封装的异步方法
     */
    void asyncMethod2OfApp(Callback<String> callback);
}

这些方法是 app 模块需要暴露给 module1 、 module2 的方法,同时 app 模块自身也需要提供这个接口的实现,所以首先我们需要在 app 、module1 、module2 这三个模块的 build.gradle 文件中依赖 router 这个模块:

dependencies {
    // Other dependencies
    ...
    api project(":router")
}

然后我们回到 app 模块,为刚刚在 router 定义的 AppRouter 接口提供一个实现:

@ServiceProvider//注意这个annotation
public class AppRouterImpl implements AppRouter {

    @Override
    public String syncMethodOfApp() {
        return "syncMethodResult";
    }

    @Override
    public Observable<String> asyncMethod1OfApp() {
        return Observable.just("asyncMethod1Result");
    }

    @Override
    public void asyncMethod2OfApp(final Callback<String> callback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                callback.onResult("asyncMethod2Result");
            }
        }).start();
    }
}

假设现在 module1 中需要调用 app 模块中的 asyncMethod1OfApp 方法:

AppRouter appRouter = AppJoint.service(AppRouter.class);

// 获得同步调用的结果        
String syncResult = appRouter.syncMethodOfApp();
// 发起异步调用
appRouter.asyncMethod1OfApp()
        .subscribe((result) -> {
            // handle asyncResult
        });
// 发起异步调用
appRouter.asyncMethod2OfApp(new Callback<String>() {
    @Override
    public void onResult(String data) {
        // handle asyncResult
    }
});

也就是说,如果一个模块需要提供方法供其他模块调用,需要做以下步骤:

1.把接口声明在 router 模块中
2.在自己模块内部实现上一步中声明的接口,同时在实现类上标记 @ServiceProvider 注解

3.1 模块独立编译运行模式下跨模块方法的调用

在模块单独编译运行期间,其它的模块是不参与编译的,它们的代码也不会打包进用于模块独立运行的 standalaone 模块,我们如何解决在模块单独编译运行模式下,跨模块调用的代码依然有效呢?
以 module1 为例,首先为了便于在 module1 内部任何地方都可以调用其它模块的方法,我们创建一个 RouterServices 类用于存放其它模块的接口的实例:

public class RouterServices {
    // app 模块对外暴露的接口
    public static AppRouter sAppRouter = AppJoint.service(AppRouter.class);
    // module2 模块对外暴露的接口
    public static Module2Router sModule2Router = AppJoint.service(Module2Router.class);
}

这时需要创建 Mock 了 AppRouter 和 Module2Router 功能的类。这些类由于只对 module1 的独立编译运行有意义,所以这些类最合适的位置是放在 module1Standalone 这个模块内,以 AppRouter 的 Mock 类 AppRouterMock 为例:

public class AppRouterMock implements AppRouter {
    @Override
    public String syncMethodOfApp() {
        return "mockSyncMethodOfApp";
    }

    @Override
    public Observable<String> asyncMethod1OfApp() {
        return Observable.just("mockAsyncMethod1OfApp");
    }

    @Override
    public void asyncMethod2OfApp(final Callback<String> callback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                callback.onResult("mockAsyncMethod2Result");
            }
        }).start();
    }
}

在Application中改变引用:

public class Module1StandaloneApplication extends Module1Application {

    @Override
    public void onCreate() {
        // module1 init inside super.onCreate()
        super.onCreate();
        // initialization only used for running module1 standalone
        Log.i("module1Standalone", "module1Standalone init is called");

        // Replace instances inside RouterServices
        RouterServices.sAppRouter = new AppRouterMock();
        RouterServices.sModule2Router = new Module2RouterMock();
    }
}

3.2 跨模块启动 Activity 和 Fragment

首先,在 router 模块的 Module1Router 内声明启动 Module1Activity 的方法:

public interface Module1Router {
    ...
    // 启动 Module1Activity
    void startModule1Activity(Context context);
}

然后在 module1 模块里 Module1Router 对应的实现类 Module1RouterImpl 中实现刚刚定义的方法:

@ServiceProvider
public class Module1RouterImpl implements Module1Router {

    ...

    @Override
    public void startModule1Activity(Context context) {
        Intent intent = new Intent(context, Module1Activity.class);
        context.startActivity(intent);
    }
}

这样, module2 中就可以通过下面的方式启动 module1 中的 Module1Activity 了。

RouterServices.sModule1Router.startModule1Activity(context);

跨模块获取 Fragment 实例也是类似的方法,我们在 Module1Router 里继续声明方法:

public interface Module1Router {
    ...
    // 启动 Module1Activity
    void startModule1Activity(Context context);

    // 获取 Module1Fragment
    Fragment obtainModule1Fragment();
}

差不多的写法,我们只要在 Module1RouterImpl 里接着实现方法即可:

@ServiceProvider
public class Module1RouterImpl implements Module1Router {
    @Override
    public void startModule1Activity(Context context) {
        Intent intent = new Intent(context, Module1Activity.class);
        context.startActivity(intent);
    }

    @Override
    public Fragment obtainModule1Fragment() {
        Fragment fragment = new Module1Fragment();
        Bundle bundle = new Bundle();
        bundle.putString("param1", "value1");
        bundle.putString("param2", "value2");
        fragment.setArguments(bundle);
        return fragment;
    }
}

猜你喜欢

转载自blog.csdn.net/jielundewode/article/details/83269777
AA