计算机技术实战

纸上得来终觉浅,绝知此事要躬行。

Download this project as a .zip file Download this project as a tar.gz file

Android组件化研究实践

目录

项目依赖树

说明:

  1. 设置变量assembleMode;
  2. 当assembleMode=true时,App依赖ModuleA、B、C;
  3. 当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");