Android 插件化框架replugin replugin-host-gradle源码解析

replugin-host-gradle,针对宿主应用执行的构建任务:生成带 RePlugin 插件坑位的 AndroidManifest.xml(允许自定义数量);生成 RepluginHostConfig 类,方便插件框架读取并自定义其属性;生成 plugins-builtin.json,json中含有插件应用的信息,包名,插件名,插件路径等。

1、生成有坑位的组件的AndroidManifest.xml

gradle 插件入口 Replugin.groovy

 

    @Override
    public void apply(Project project) {
        println "${TAG} Welcome to replugin world ! "

        this.project = project

        /* Extensions */
        project.extensions.create(AppConstant.USER_CONFIG, RepluginConfig)

        if (project.plugins.hasPlugin(AppPlugin)) {

            def android = project.extensions.getByType(AppExtension)
            android.applicationVariants.all { variant ->

                addShowPluginTask(variant)

                if (config == null) {
                    config = project.extensions.getByName(AppConstant.USER_CONFIG)
                    checkUserConfig(config)
                }

                def generateBuildConfigTask = VariantCompat.getGenerateBuildConfigTask(variant)
                def appID = generateBuildConfigTask.appPackageName
                def newManifest = ComponentsGenerator.generateComponent(appID, config)  //宿主中AndroidManifest.xml中的组件坑位生成
                println "${TAG} countTask=${config.countTask}"

                def variantData = variant.variantData
                def scope = variantData.scope

                //host generate task
                def generateHostConfigTaskName = scope.getTaskName(AppConstant.TASK_GENERATE, "HostConfig")
                def generateHostConfigTask = project.task(generateHostConfigTaskName)

                generateHostConfigTask.doLast {
                    FileCreators.createHostConfig(project, variant, config)
                }
                generateHostConfigTask.group = AppConstant.TASKS_GROUP

                //depends on build config task
                if (generateBuildConfigTask) {
                    generateHostConfigTask.dependsOn generateBuildConfigTask
                    generateBuildConfigTask.finalizedBy generateHostConfigTask
                }

                //json generate task
                def generateBuiltinJsonTaskName = scope.getTaskName(AppConstant.TASK_GENERATE, "BuiltinJson")
                def generateBuiltinJsonTask = project.task(generateBuiltinJsonTaskName)

                generateBuiltinJsonTask.doLast {
                    FileCreators.createBuiltinJson(project, variant, config)
                }
                generateBuiltinJsonTask.group = AppConstant.TASKS_GROUP

                //depends on mergeAssets Task
                def mergeAssetsTask = VariantCompat.getMergeAssetsTask(variant)
                if (mergeAssetsTask) {
                    generateBuiltinJsonTask.dependsOn mergeAssetsTask
                    mergeAssetsTask.finalizedBy generateBuiltinJsonTask
                }

                variant.outputs.each { output ->
                    VariantCompat.getProcessManifestTask(output).doLast {
                        println "${AppConstant.TAG} processManifest: ${it.outputs.files}"
                        it.outputs.files.each { File file ->
                            updateManifest(file, newManifest)
                        }
                    }
                }
            }
        }
    }

def newManifest = ComponentsGenerator.generateComponent(appID, config)

    def static generateComponent(def applicationID, def config) {
        // 是否使用 AppCompat 库(涉及到默认主题)
        if (config.useAppCompat) {
            themeNTS = THEME_NTS_USE_APP_COMPAT
        } else {
            themeNTS = THEME_NTS_NOT_USE_APP_COMPAT
        }

        def writer = new StringWriter()
        def xml = new MarkupBuilder(writer)

        /* UI 进程 */
        xml.application {

            /* 需要编译期动态修改进程名的组件*/

            String pluginMgrProcessName = config.persistentEnable ? config.persistentName : applicationID

            // 常驻进程Provider
            provider(
                    "${name}":"com.qihoo360.replugin.component.process.ProcessPitProviderPersist",
                    "${authorities}":"${applicationID}.loader.p.main",
                    "${exp}":"false",
                    "${process}":"${pluginMgrProcessName}")

            provider(
                    "${name}":"com.qihoo360.replugin.component.provider.PluginPitProviderPersist",
                    "${authorities}":"${applicationID}.Plugin.NP.PSP",
                    "${exp}":"false",
                    "${process}":"${pluginMgrProcessName}")

            // ServiceManager 服务框架
            provider(
                    "${name}":"com.qihoo360.mobilesafe.svcmanager.ServiceProvider",
                    "${authorities}":"${applicationID}.svcmanager",
                    "${exp}":"false",
                    "${multiprocess}":"false",
                    "${process}":"${pluginMgrProcessName}")

            service(
                    "${name}":"com.qihoo360.replugin.component.service.server.PluginPitServiceGuard",
                    "${process}":"${pluginMgrProcessName}")

            /* 透明坑 */
            config.countTranslucentStandard.times {
                activity(
                        "${name}": "${applicationID}.${infix}N1NRTS${it}",
                        "${cfg}": "${cfgV}",
                        "${exp}": "${expV}",
                        "${ori}": "${oriV}",
                        "${theme}": "${themeTS}")
            }
            config.countTranslucentSingleTop.times {
                activity(
                        "${name}": "${applicationID}.${infix}N1STPTS${it}",
                        "${cfg}": "${cfgV}",
                        "${exp}": "${expV}",
                        "${ori}": "${oriV}",
                        "${theme}": "${themeTS}",
                        "${launchMode}": "singleTop")
            }
			......
		}
			
}

2、生成 RepluginHostConfig

Replugin.groovy apply(Project project)

                //host generate task
                def generateHostConfigTaskName = scope.getTaskName(AppConstant.TASK_GENERATE, "HostConfig")
                def generateHostConfigTask = project.task(generateHostConfigTaskName)

                generateHostConfigTask.doLast {
                    FileCreators.createHostConfig(project, variant, config)
                }
                generateHostConfigTask.group = AppConstant.TASKS_GROUP


FileCreators.groovy

    static def createHostConfig(project, variant, config) {
        def creator = new RePluginHostConfigCreator(project, variant, config)
        create(creator)
    }

RePluginHostConfigCreator.groovy

@Override
    String getFileContent() {
        return """
package com.qihoo360.replugin.gen;

/**
 * 注意:此文件由插件化框架自动生成,请不要手动修改。
 */
public class RePluginHostConfig {

    // 常驻进程名字
    public static String PERSISTENT_NAME = "${config.persistentName}";

    // 是否使用“常驻进程”(见PERSISTENT_NAME)作为插件的管理进程。若为False,则会使用默认进程
    public static boolean PERSISTENT_ENABLE = ${config.persistentEnable};

    // 背景透明的坑的数量(每种 launchMode 不同)
    public static int ACTIVITY_PIT_COUNT_TS_STANDARD = ${config.countTranslucentStandard};
    public static int ACTIVITY_PIT_COUNT_TS_SINGLE_TOP = ${config.countTranslucentSingleTop};
    public static int ACTIVITY_PIT_COUNT_TS_SINGLE_TASK = ${config.countTranslucentSingleTask};
    public static int ACTIVITY_PIT_COUNT_TS_SINGLE_INSTANCE = ${
            config.countTranslucentSingleInstance
        };

    // 背景不透明的坑的数量(每种 launchMode 不同)
    public static int ACTIVITY_PIT_COUNT_NTS_STANDARD = ${config.countNotTranslucentStandard};
    public static int ACTIVITY_PIT_COUNT_NTS_SINGLE_TOP = ${config.countNotTranslucentSingleTop};
    public static int ACTIVITY_PIT_COUNT_NTS_SINGLE_TASK = ${config.countNotTranslucentSingleTask};
    public static int ACTIVITY_PIT_COUNT_NTS_SINGLE_INSTANCE = ${
            config.countNotTranslucentSingleInstance
        };

    // TaskAffinity 组数
    public static int ACTIVITY_PIT_COUNT_TASK = ${config.countTask};

    // 是否使用 AppCompat 库
    public static boolean ACTIVITY_PIT_USE_APPCOMPAT = ${config.useAppCompat};

    //------------------------------------------------------------
    // 主程序支持的插件版本范围
    //------------------------------------------------------------

    // HOST 向下兼容的插件版本
    public static int ADAPTER_COMPATIBLE_VERSION = ${config.compatibleVersion};

    // HOST 插件版本
    public static int ADAPTER_CURRENT_VERSION = ${config.currentVersion};
}"""
    }

在这里插入图片描述

3、生成 plugins-builtin.json

Replugin.groovy apply(Project project)

                //json generate task
                def generateBuiltinJsonTaskName = scope.getTaskName(AppConstant.TASK_GENERATE, "BuiltinJson")
                def generateBuiltinJsonTask = project.task(generateBuiltinJsonTaskName)

                generateBuiltinJsonTask.doLast {
                    FileCreators.createBuiltinJson(project, variant, config)
                }
                generateBuiltinJsonTask.group = AppConstant.TASKS_GROUP

PluginBuiltinJsonCreator.groovy

    @Override
    String getFileContent() {
        //查找插件文件并抽取信息,如果没有就直接返回null
        File pluginDirFile = new File(fileDir?.getAbsolutePath() + File.separator + config.pluginDir)
        if (!pluginDirFile.exists()) {
            println "${AppConstant.TAG} The ${pluginDirFile.absolutePath} does not exist "
            println "${AppConstant.TAG} pluginsInfo=null"
            return null
        }

        new File(fileDir.getAbsolutePath() + File.separator + config.pluginDir)
                .traverse(type: FileType.FILES, nameFilter: ~/.*\${config.pluginFilePostfix}/) {

            PluginInfoParser parser = null
            try {
                parser = new PluginInfoParser(it.absoluteFile, config)
            } catch (Exception e) {
                if (config.enablePluginFileIllegalStopBuild) {
                    System.err.println "${AppConstant.TAG} the plugin(${it.absoluteFile.absolutePath}) is illegal !!!"
                    throw new Exception(e)
                }
            }

            if (null != parser) {
                pluginInfos << parser.pluginInfo
            }
        }

        //插件为0个
        if (pluginInfos.isEmpty()) {
            println "${AppConstant.TAG} pluginsSize=0"
            println "${AppConstant.TAG} pluginsInfo=null"
            return null
        }

        //构建插件们的json信息
        def jsonOutput = new JsonOutput()
        String pluginInfosJson = jsonOutput.toJson(pluginInfos)
        //格式化打印插件们的json信息
        println "${AppConstant.TAG} pluginsSize=${pluginInfos.size()}"
        println "${AppConstant.TAG} pluginsInfo=${jsonOutput.prettyPrint(pluginInfosJson)}"

        return pluginInfosJson
    }

从manifest的xml中抽取PluginInfo信息
PluginInfoParser.groovy

public PluginInfoParser(File pluginFile, def config) {

        pluginInfo = new PluginInfo()

        ApkFile apkFile = new ApkFile(pluginFile)

        String manifestXmlStr = apkFile.getManifestXml()
        ByteArrayInputStream inputStream = new ByteArrayInputStream(manifestXmlStr.getBytes("UTF-8"))

        SAXParserFactory factory = SAXParserFactory.newInstance()
        SAXParser parser = factory.newSAXParser()
        parser.parse(inputStream, this)

        String fullName = pluginFile.name
        pluginInfo.path = config.pluginDir + "/" + fullName

        String postfix = config.pluginFilePostfix
        pluginInfo.name = fullName.substring(0, fullName.length() - postfix.length())
    }


    public PluginInfo getPluginInfo() {
        return pluginInfo;
    }


    @Override
    public void startDocument() throws SAXException {
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

        if ("meta-data" == qName) {
            switch (attributes.getValue(ANDROID_NAME)) {
                case TAG_NAME:
                    pluginInfo.name = attributes.getValue(ANDROID_VALUE)
                    break;
                case TAG_VERSION_LOW:
                    pluginInfo.low = new Long(attributes.getValue(ANDROID_VALUE))
                    break;
                case TAG_VERSION_HIGH:
                    pluginInfo.high = new Long(attributes.getValue(ANDROID_VALUE))
                    break;
                case TAG_VERSION_VER:
                    pluginInfo.ver = new Long(attributes.getValue(ANDROID_VALUE))
                    break
                case TAG_FRAMEWORK_VER:
                    pluginInfo.frm = new Long(attributes.getValue(ANDROID_VALUE))
                    break
                default:
                    break
            }
        } else if ("manifest" == qName) {
            pluginInfo.pkg = attributes.getValue("package")
            pluginInfo.ver = new Long(attributes.getValue("android:versionCode"))
        }
    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值