Android组件化Application合并冲突解决方案

1,398 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

问题

随着项目代码量越来越大,每次编译的时候都会等上很长时间,极大降低了开发效率,而且随着功能的复杂度上升,代码耦合程度也越来越高,为了解决这些问题,很多项目都引入了组件化,将不同业务模块单独抽离成组件,单独编译测试,不同业务模块之间解耦,但是有的组件需要在Application中初始化,如果我们为每个组件都提供一个Application,虽然能单独编译运行,但是在最后所有组件一起打包的时候会遇到Manifest merger failed的问题,那么如何解决这个问题呢,我们可以参考谷歌App Startup库的做法。

实现

1. 定义ApplicationDelegate

首先,我们定义一个ApplicationDelegate接口,并定义attachBaseContext()onCreate()方法,用来代理真正Application中对应的方法。

public interface ApplicationDelegate {
    void attachBaseContext(Application application,Context context);
    void onCreate(Application application);
}

2. 使用ApplicationDelegate

在需要使用Application的组件中实现ApplicationDelegate,例如common模块初始化ARouter

public class CommonApplicationDelegate implements ApplicationDelegate {
    @Override
    public void attachBaseContext(Application application, Context context) {
    }
    @Override
    public void onCreate(Application application) {
        if(BuildConfig.DEBUG){
            ARouter.openDebug();
            ARouter.openLog();
        }
        ARouter.init(application);
    }
}

3. 代理Application

在base库中创建真正的BaseApplication并继承自Application,随后将ApplicationattachBaseContext()onCreate()方法交给ApplicationDelegate处理,由于我们项目有多个组件,所以使用List将ApplicationDelegate保存。

public class BaseApplication extends Application {
    List<ApplicationDelegate> delegates;
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        delegates = findApplicationDelegate(base);
        if (delegates != null) {
            for (ApplicationDelegate delegate : delegates) {
                delegate.attachBaseContext(this,base);
            }
        }
    }
    @Override
    public void onCreate() {
        super.onCreate();
        if (delegates != null) {
            for (ApplicationDelegate delegate : delegates) {
                delegate.onCreate(this);
            }
        }
    }
}

4. 注册ApplicationDelegate

在上一步处理的时候我们发现需要先找到ApplicationDelegate。也就是定义findApplicationDelegate()方法,不过在查找之前,需要先对ApplicationDelegate进行注册,开头的时候说了我们参考App Startup的做法,它是在<provider>节点中添加meta-data,所以我们可以仿照在application节点中添加meta-data,如下:

//首页组件
<application>
    <meta-data android:value="ApplicationDelegate" android:name=".HomeApplicationDelegate"/>
</application>

//登录组件
<application>
    <meta-data android:value="ApplicationDelegate" android:name=".LoginApplicationDelegate"/>
</application>

5. 查找ApplicationDelegate

首先我们通过PackageManager获取到ApplicationInfo,然后获取metaData,找到我们需要的ApplicationDelegate

private static final String MODULE_META_KEY = "ApplicationDelegate";
public List<ApplicationDelegate> findApplicationDelegate(Context context) {
    List<ApplicationDelegate> delegates = new ArrayList<>();
    try {
        PackageManager pm = context.getPackageManager();
        String packageName = context.getPackageName();
        ApplicationInfo info = pm.getApplicationInfo(packageName, GET_META_DATA);
        if (info != null && info.metaData != null) {
            for (String key : info.metaData.keySet()) {
                Object value = info.metaData.get(key);
                if (MODULE_META_KEY.equals(value)) {
                    ApplicationDelegate delegate = initApplicationDelegate(key);
                    if (delegate != null) {
                        delegates.add(delegate);
                    }
                }
            }
        }
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    return delegates;
}

6. 实例化ApplicationDelegate

获取到ApplicationDelegate之后,只需要通过反射创建其对象就可以了

private ApplicationDelegate initApplicationDelegate(String className) {
    Class<?> clazz;
    Object instance = null;
    try {
        clazz = Class.forName(className);
    } catch (ClassNotFoundException e) {
        throw new IllegalArgumentException("找不到" + className, e);
    }
    try {
        instance = clazz.newInstance();
    } catch (Exception e) {
        throw new RuntimeException("不能获取 " + className + " 的实例", e);
    }
    if (!(instance instanceof ApplicationDelegate)) {
        throw new RuntimeException("不能获取 " + ApplicationDelegate.class.getName() + " 的实例 " + instance);
    }
    return (ApplicationDelegate) instance;
}

最后别忘了将BaseApplication注册到base模块AndroidManifest.xml