Android组件化研究实践
目录
项目依赖树
说明:
- 设置变量assembleMode;
- 当assembleMode=true时,App依赖ModuleA、B、C;
- 当assembleMode=false时,App不依赖ModuleA、B、C,这三者可以独立运行;
组件化实现
配置根目录gradle
ext {
// assembleMode=true: 发布模式,子模块作为library,assembleMode=false: 调试模式,子模块作为独立的应用,
assembleMode=true
}
配置模块gradle
if (assembleMode) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
android {
defaultConfig {
// other configs
if (!assembleMode) {
applicationId "com.github.mwping.androidguides"
versionCode 1
versionName "1.0.0"
} else {
// do nothing
}
}
sourceSets {
main {
// 独立app模式下使用debug下的AndroidManifest.xml
if (assembleMode) {
manifest.srcFile 'src/main/AndroidManifest.xml'
java {
exclude '**/test/**'
}
} else {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
}
}
}
}
dependencies {
// other dependencies
implementation project(':base')
annotationProcessor project(':processor')
}
添加注解
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface RoutePath {
String value();
}
添加注解处理器
@AutoService(Processor.class)
public class RouteProcessor extends AbstractProcessor {
private Filer mFiler;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnv.getFiler();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
doProcess(set, roundEnvironment);
return true;
}
private void doProcess(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> routePathSet = roundEnvironment.getElementsAnnotatedWith(RoutePath.class);
if (routePathSet.isEmpty()) {
return;
}
buildRoutePaths(routePathSet);
}
private void buildRoutePaths(Set<? extends Element> routePathSet) {
for (Element element : routePathSet) {
if (element instanceof TypeElement) {
buildClassPath((TypeElement) element);
}
}
}
private void buildClassPath(TypeElement element) {
String path = element.getAnnotation(RoutePath.class).value();
String moduleName = Utils.generateModuleName(path);
FieldSpec.Builder fieldBuilder = FieldSpec.builder(Class.class, "TARGET_CLASS",
Modifier.FINAL, Modifier.PUBLIC, Modifier.STATIC).initializer("$T.class", ClassName.get(element));
TypeSpec module = TypeSpec.classBuilder(moduleName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addField(fieldBuilder.build())
.addJavadoc("Auto generated by {@link com.github.mwping.module.processor.RouteProcessor}, do not modify this class!\n" +
"see doc https://github.com/square/javapoet\n")
.build();
JavaFile javaFile = JavaFile.builder("com.github.mwping.module", module)
.build();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = new LinkedHashSet<>();
set.add(RoutePath.class.getName());
return set;
}
}
配置路径
@RoutePath("/module/login")
public class LoginActivity extends AppCompatActivity implements LoaderCallbacks<Cursor> {
// ...
}
运行项目,会自动生成类:
/**
* Auto generated by {@link com.github.mwping.module.processor.RouteProcessor}, do not modify this class!
* see doc https://github.com/square/javapoet
*/
public final class Module$Login {
public static final Class TARGET_CLASS = LoginActivity.class;
}
添加路由分发逻辑
public class Router {
private static volatile Router INSTANCE;
private HashMap<String, Class> pathMap;
private Router() {
pathMap = new HashMap<>();
}
public static Router getInstance() {
if (INSTANCE == null) {
synchronized (Router.class) {
if (INSTANCE == null) {
INSTANCE = new Router();
}
}
}
return INSTANCE;
}
@MwpLog
public boolean jump(Context context, String url) {
Uri uri = Uri.parse(url);
String host = uri.getHost();
String path = uri.getPath();
if (TextUtils.isEmpty(path)) {
return false;
}
if (pathMap.get(path) == null) {
try {
Class moduleClass = Class.forName("com.github.mwping.module." + Utils.generateModuleName(path));
Field field = moduleClass.getField("TARGET_CLASS");
Class targetClass = (Class) field.get(null);
pathMap.put(path, targetClass);
} catch (Exception e) {
e.printStackTrace();
}
}
Class targetClass = pathMap.get(path);
if (targetClass == null) {
return false;
}
Intent intent = new Intent(context, targetClass);
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
return true;
}
}
使用
Router.getInstance().jump(context, "/module/login");