Giter Site home page Giter Site logo

iqiyi / qigsaw Goto Github PK

View Code? Open in Web Editor NEW
1.6K 51.0 261.0 66.7 MB

🔥🔥Qigsaw ['tʃɪɡsɔ] is a dynamic modularization library which is based on Android App Bundles(Do not need Google Play Service). It supports dynamic delivery for split APKs without reinstalling the base one.

License: Other

Java 79.40% Groovy 20.29% CMake 0.02% C 0.12% AIDL 0.17%
android java dynamic iqiyi android-app-bundle plugin-framework split-apk play play-core

qigsaw's People

Contributors

donalddu avatar jwu26 avatar kissonchan avatar rubitree avatar ziyang0116 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qigsaw's Issues

Lambda 表达式导致的NoClassDefFoundError 错误

在使用qigsaw 插件化项目的时候,遇到了split中使用lambda表达式在打release包混淆时,打包可以成功,但在运行lambda表达式处会crash,其中R8和Proguard混淆都会报NoClassDefFoundError错误,请问这可能是什么原因导致的,应该怎么解决呢?

我尝试了以下方法都无效:

  1. 添加 lambda表达式 的混淆;
  2. android.enableR8=false 关闭R8,使用Proguard也会crash;

com.android.tools.build:gradle:3.6.2 报错

Caused by: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'property(interface org.gradle.api.file.Directory, property(interface org.gradle.api.file.Directory, fixed(class org.gradle.api.internal.file.DefaultFilePropertyFactory$FixedDirectory, C:\datas\QigsawTest\app\build\outputs\apk\debug)))' with class 'org.gradle.api.internal.file.DefaultFilePropertyFactory$DefaultDirectoryVar' to class 'java.io.File'

安装包大小

在安智市场看爱奇艺2019以后几年的包也没有减小,爱奇艺app没有用这个动态化组件吗

请问使用Qigsaw之后,上层业务对于插件的签名校验应该如何做,是否有最佳实践供参考

dynamic-feature的apk包,用apkSigner做了签名之后,在宿主通过PackageManager.getPackageArchiveInfo获取签名信息的时候,获取不到packageInfo信息,取到的值为null,因此无法像之前传统插件化框架那样对插件做签名校验,我们用demo测试了一下,貌似bundle方式打出来的包,用PackageManager.getPackageArchiveInfo就是无法获取packageInfo的。请问如果需要做插件签名校验,这块有什么最佳实践可以参考的吗?

Sample 启动崩溃

java.lang.RuntimeException: Unable to get provider com.iqiyi.qigsaw.sample.java.JavaContentProvider: java.lang.ClassNotFoundException: Didn't find class "com.iqiyi.qigsaw.sample.java.JavaContentProvider" on path: DexPathList[[zip file "/data/app/com.iqiyi.qigsaw.sample-u8wRcwFsQ0wfnQl1TzqxGA==/base.apk"],nativeLibraryDirectories=[/data/app/com.iqiyi.qigsaw.sample-u8wRcwFsQ0wfnQl1TzqxGA==/lib/arm64, /system/lib64, /vendor/lib64]]
at android.app.ActivityThread.installProvider(ActivityThread.java:6717)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:6215)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6132)
at android.app.ActivityThread.-wrap1(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1839)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:197)
at android.app.ActivityThread.main(ActivityThread.java:7022)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:515)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:837)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.iqiyi.qigsaw.sample.java.JavaContentProvider" on path: DexPathList[[zip file "/data/app/com.iqiyi.qigsaw.sample-u8wRcwFsQ0wfnQl1TzqxGA==/base.apk"],nativeLibraryDirectories=[/data/app/com.iqiyi.qigsaw.sample-u8wRcwFsQ0wfnQl1TzqxGA==/lib/arm64, /system/lib64, /vendor/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:125)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.app.ActivityThread.installProvider(ActivityThread.java:6702)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:6215)?
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6132)?
at android.app.ActivityThread.-wrap1(Unknown Source:0)?
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1839)?
at android.os.Handler.dispatchMessage(Handler.java:106)?
at android.os.Looper.loop(Looper.java:197)?
at android.app.ActivityThread.main(ActivityThread.java:7022)?
at java.lang.reflect.Method.invoke(Native Method)?
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:515)?
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:837)?

splitcore类库里面并没有使用androidx.appcompat:appcompat里的代码,是否可以去掉

另外是否可以做成AndroidX和非AndroidX通用的类库
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "androidx.annotation:annotation:1.0.0" implementation 'androidx.appcompat:appcompat:1.0.0' api project(':splitcommon') implementation project(':splitextension') implementation project(':splitrequester') implementation project(':splitinstaller') api project(':splitloader') api project(':splitdownloader') api project(':playcorelibrary') api project(':splitreporter') testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' }

Qigsaw兼容Gradle 插件 4.1.0吗?

最近版本升级到gradle 4.1.0:classpath 'com.android.tools.build:gradle:4.1.0'
出现如下问题:
Caused by: org.gradle.api.GradleException: com.android.build.gradle.tasks.ProcessMultiApkApplicationManifest_Decorated
at com.iqiyi.qigsaw.buildtool.gradle.internal.tool.AGPCompat.getBundleManifestDirCompat(AGPCompat.groovy:106)
at com.iqiyi.qigsaw.buildtool.gradle.internal.tool.AGPCompat$getBundleManifestDirCompat$8.call(Unknown Source)
at com.iqiyi.qigsaw.buildtool.gradle.QigsawAppBasePlugin$_apply_closure1$_closure5.doCall(QigsawAppBasePlugin.groovy:110)
at org.gradle.util.ClosureBackedAction.execute(ClosureBackedAction.java:71)
at org.gradle.util.ConfigureUtil.configureTarget(ConfigureUtil.java:154)
at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:105)
at org.gradle.util.ConfigureUtil$WrappedConfigureAction.execute(ConfigureUtil.java:166)
at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1$1.run(DefaultCollectionCallbackActionDecorator.java:100)
at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.reapply(DefaultUserCodeApplicationContext.java:60)
at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction$1.run(DefaultCollectionCallbackActionDecorator.java:97)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:395)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:387)
at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:157)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:242)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:150)
at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:84)
at org.gradle.api.internal.DefaultCollectionCallbackActionDecorator$BuildOperationEmittingAction.execute(DefaultCollectionCallbackActionDecorator.java:94)
at org.gradle.api.internal.DefaultDomainObjectCollection.all(DefaultDomainObjectCollection.java:163)
at org.gradle.api.internal.DefaultDomainObjectCollection.all(DefaultDomainObjectCollection.java:198)
at org.gradle.api.DomainObjectCollection$all.call(Unknown Source)
at com.iqiyi.qigsaw.buildtool.gradle.QigsawAppBasePlugin$_apply_closure1.doCall(QigsawAppBasePlugin.groovy:92)

不支持ARouter

项目中用了ARouter,集成Qigsaw之后module之间无法跳转,应该是ARouter不支持app bundle,不知道有没有其他解决办法。

夜神模拟器无法运行

日志如下:
08-11 15:14:47.058 2853-2853/? D/SplitAABInfoProvider: App has no fused modules.
08-11 15:14:47.059 2853-2853/? D/SplitAABInfoProvider: No splits are found or app cannot be found in package manager.
08-11 15:14:47.061 2853-2853/? I/Split:MultiDex: VM with version 2.1.0 has multidex support
08-11 15:14:47.062 2853-2853/? D/SplitInstallSupervisor: No splits need to uninstall!
08-11 15:14:47.062 2853-2853/? I/SplitInfoManagerImpl: currentVersion : 37.0.0_1.0.0 defaultVersion : 37.0.0_1.0.0
08-11 15:14:47.062 2853-2853/? I/SplitInfoManagerImpl: Default split file name: qigsaw/qigsaw_37.0.0_1.0.0.json
08-11 15:14:47.065 2853-2853/? I/SplitInfoManagerImpl: Cost 3 mil-second to parse default split info
08-11 15:14:47.069 2853-2886/? I/Split:AbiUtil: Succeed to get primaryCpuAbi null from ApplicationInfo.
08-11 15:14:47.069 2853-2886/? I/Split:AbiUtil: Succeed to get primaryCpuAbi x86 from CurrentInstructionSet.
08-11 15:14:47.150 2853-2853/? D/SplitInstallManagerImpl: App has no fused modules.
08-11 15:14:47.150 2853-2853/? D/SplitInstallManagerImpl: No splits are found or app cannot be found in package manager.
08-11 15:14:47.151 2853-2853/? D/SplitInstallManagerImpl: App has no fused modules.
08-11 15:14:47.152 2853-2853/? D/SplitInstallManagerImpl: No splits are found or app cannot be found in package manager.
08-11 15:14:47.152 2853-2853/? I/PlayCore: UID: [10044] PID: [2853] SplitInstallService : startInstall([game_feature])
08-11 15:14:47.155 2853-2889/? I/PlayCore: UID: [10044] PID: [2853] SplitInstallService : Initiate binding to the service.
08-11 15:14:47.175 2853-2895/? D/OpenGLRenderer: Use EGL_SWAP_BEHAVIOR_PRESERVED: true
08-11 15:14:47.189 2853-2853/? D/Atlas: Validating map...
08-11 15:14:47.202 1763-2391/? V/WindowManager: Adding window Window{29d8936b u0 com.monstresinvincibles.fr/com.huxwgledazpt.lofrwups.KhUOHktgecKlQqa} at 2 of 6 (before Window{167d54ac u0 Starting com.monstresinvincibles.fr})
08-11 15:14:47.215 2853-2895/? I/OpenGLRenderer: Initialized EGL, version 1.4
08-11 15:14:47.233 2853-2895/? D/OpenGLRenderer: Enabling debug mode 0
08-11 15:14:47.360 2853-2853/? I/PlayCore: UID: [10044] PID: [2853] SplitInstallService : ServiceConnectionImpl.onServiceConnected(ComponentInfo{com.monstresinvincibles.fr/com.iqiyi.android.qigsaw.core.splitinstall.remote.SplitInstallService})
08-11 15:14:47.365 2853-2889/? I/PlayCore: UID: [10044] PID: [2853] SplitInstallService : linkToDeath
08-11 15:14:47.367 2853-2886/? I/PlayCore: UID: [10044] PID: [2853] SplitInstallService : onError(-2)

请问Qigsaw目前是无法支持模拟器吗?

此插件令下游Transform崩溃 “Unexpected scopes found in folder”

直接原因

尝试创建新的SubStream

abstract class SimpleClassCreatorTransform extends Transform {

    String prepareToCreateClass(TransformInvocation transformInvocation) {
        ...
        return transformInvocation.outputProvider.getContentLocation("main",
                getOutputTypes(), getScopes(),
                Format.DIRECTORY)
    }
}

Scope集合作为TransformOutputProvider.getContentLocation()有问题

class SplitComponentTransform extends SimpleClassCreatorTransform {

    @Override
    Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT
    }
}

详见build目录下的__content__.json,基本没有混合ScopeSubStream

[
    {
        "scopes": [
            "EXTERNAL_LIBRARIES"
        ],
        "format": "JAR",
        "types": [ "DEX_ARCHIVE" ],
        "name": "...",
        "index": 0,
        "present": true
    },
    {
        "scopes": [ "SUB_PROJECTS" ],
        "format": "JAR",
        "types": [ "DEX_ARCHIVE" ],
        "name": "...",
        "index": 352,
        "present": true
    },
    {
        "scopes": [ "PROJECT" ],
        "format": "DIRECTORY",
        "types": [ "DEX_ARCHIVE" ],
        "name": "...",
        "index": 354,
        "present": true
    },
]

根本原因

  • 每个Transform都会"承上启下"地对应一个IntermediateStream
  • 每个IntermediateStream对应多个SubStream
  • 上下游的SubStreamScope匹配,大部分情况下游是上游的超集,但如果是部分包含则抛异常

影响范围

所有使用TransformScope不包含EXTERNAL_LIBRARIES的插件。

解决方法

  • 修正TransformOutputProvider.getContentLocation()参数
    建议改为ImmutableSet.of(QualifiedContent.Scope.PROJECT)

  • 改用代码生成实现此流程
    SplitComponentTransform的业务与R.java生成类似,但后者没有侵入到字节码编辑流程。

重现代码

新建一个Android工程,然后把下面代码粘贴到主工程的build.gradle最后。

import com.android.annotations.NonNull
import com.android.build.api.transform.DirectoryInput
import com.android.build.api.transform.Format
import com.android.build.api.transform.JarInput
import com.android.build.api.transform.Status
import com.android.build.api.transform.TransformException
import com.android.build.api.transform.TransformInput
import com.android.build.api.transform.TransformInvocation
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.build.api.transform.Transform
import com.android.build.api.transform.QualifiedContent
import com.google.common.collect.ImmutableSet
import org.apache.commons.io.FileUtils
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes


abstract class TransformBase extends Transform {

    @Override
    boolean isIncremental() {
        return false
    }

    @Override
    Set<QualifiedContent.ContentType> getInputTypes() {
        return ImmutableSet.of(QualifiedContent.DefaultContentType.CLASSES)
    }

    private static File getOutputFile(TransformInvocation invocation, QualifiedContent content, Format format) {
        def provider = invocation.outputProvider
        return provider.getContentLocation(content.name, content.contentTypes, content.scopes, format)
    }

    private static void transformJar(TransformInvocation invocation, JarInput jar) {
        if (jar.status == Status.REMOVED) {
            return
        }

        def outFile = getOutputFile(invocation, jar, Format.JAR)
        FileUtils.copyFile(jar.file, outFile)
    }

    private static void transformDirectory(TransformInvocation invocation, DirectoryInput dir) {
        if (!dir.file.exists()) {
            return
        }

        def statusMap = dir.getChangedFiles()
        def outDir = getOutputFile(invocation, dir, Format.DIRECTORY)
        FileUtils.copyDirectory(dir.file, outDir, { statusMap.getOrDefault(it, Status.NOTCHANGED) != Status.REMOVED })
    }

    @Override
    void transform(@NonNull TransformInvocation transformInvocation)
            throws TransformException, InterruptedException, IOException {
        transformInvocation.inputs.each { TransformInput input ->
            input.jarInputs.each { transformJar(transformInvocation, it) }
            input.directoryInputs.each { transformDirectory(transformInvocation, it) }
        }
    }
}

class CopyTransform extends TransformBase {

    @Override
    String getName() {
        return "copy"
    }

    @Override
    Set<? super QualifiedContent.Scope> getScopes() {
        return ImmutableSet.of(
                QualifiedContent.Scope.PROJECT,
                QualifiedContent.Scope.SUB_PROJECTS
        )
    }
}

class GenClassTransform extends TransformBase {

    private static final String FOO_CLASS_NAME = "Foo"

    private static final String FOO_CLASS_PACKAGE = "com.fish47.foo"

    @Override
    String getName() {
        return "genClass"
    }

    @Override
    Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT
    }

    private static getGenerateClassFile(File dir) {
        def subDir = FOO_CLASS_PACKAGE.replace(".", File.separator)
        def subPath = String.format("%s%c%s.class", subDir, File.separatorChar, FOO_CLASS_NAME)
        return new File(dir, subPath)
    }

    private static byte[] getGenerateClassBytes() {
        def fullName = String.format("%s/%s", FOO_CLASS_PACKAGE.replace(".", "/"), FOO_CLASS_NAME)
        def clzAcc = Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER
        def cw = new ClassWriter(0)
        cw.visit(49, clzAcc, fullName, null, "java/lang/Object", null)

        def mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null)
        mv.visitVarInsn(Opcodes.ALOAD, 0)
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
        mv.visitInsn(Opcodes.RETURN)
        mv.visitMaxs(1, 1)
        mv.visitEnd()

        cw.visitEnd()
        return cw.toByteArray()
    }

    private static generateFooClass(TransformInvocation invocation) {
        def provider = invocation.outputProvider
        def types = ImmutableSet.of(QualifiedContent.DefaultContentType.CLASSES)
        def scopes = TransformManager.SCOPE_FULL_PROJECT
        def outDir = provider.getContentLocation("main", types, scopes, Format.DIRECTORY)

        def classFile = getGenerateClassFile(outDir)
        FileUtils.writeByteArrayToFile(classFile, getGenerateClassBytes())
    }

    @Override
    void transform(@NonNull TransformInvocation transformInvocation)
            throws TransformException, InterruptedException, IOException {
        super.transform(transformInvocation)
        generateFooClass(transformInvocation)
    }
}

def androidExtension = project.extensions.findByName("android")
if (androidExtension instanceof BaseExtension) {
    // ok
//    androidExtension.registerTransform(new CopyTransform())
//    androidExtension.registerTransform(new GenClassTransform())

    // crash
    androidExtension.registerTransform(new GenClassTransform())
    androidExtension.registerTransform(new CopyTransform())
}

编译过后无法正常清理项目

Execution failed for task ':dynamicfeature:transformClassesWithSplitResourcesLoaderForDebug'.

Unable to delete directory 'D:\Donald\QigsawTest\dynamicfeature\build\intermediates\transforms\splitResourcesLoader\debug\25\com\dhy\dynamicfeature' after 10 attempts

请教下上传插件的问题

error
把smaple改成true的时候上传插件也要自己去实现吗?
实现上传插件后project级别的build.gradle文件里的classpath "com.iqiyi.android.qigsaw:gradle-plugin:${VERSION_NAME}"要不要换成自己的插件地址?

国内应用商店现在还不支持上传aab格式文件吧

如题,国内不支持,所以现在爱奇艺还是发给应用商店的还是一个apk,这个apk和之前的并没什么不同啊,资源,语言,so库并不能做区分啊,只是多了一个可以远程下载安装插件功能,最核心的还是依赖应用商店啊。

请问是否支持公共资源插件

你好,请问是否支持类似于Small那样的公共资源插件?
场景是:一些公共的资源定义,比如色值、间距、公共布局等资源定义都放在这个公共资源插件中,然后其他的业务插件依赖这个公共资源插件,这样升级公共资源插件后,所有业务插件中的公共资源定义都会跟随生效。

插件的processDebugResources任务失败,Dependent features configured but no package ID was set

报错信息

Execution failed for task ':processDebugResources'.

AAPT2 aapt2-3.4.2-5326820-osx Daemon #0: Unexpected error during link, attempting to stop daemon.
This should not happen under normal circumstances, please file an issue if it does.

  • Try:
    Run with --info or --debug option to get more log output. Run with --scan to get full insights.

  • Exception is:
    org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':processDebugResources'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.accept(ExecuteActionsTaskExecuter.java:148)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.accept(ExecuteActionsTaskExecuter.java:145)
    at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:191)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:138)
    at org.gradle.api.internal.tasks.execution.ResolveBeforeExecutionStateTaskExecuter.execute(ResolveBeforeExecutionStateTaskExecuter.java:75)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:62)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:108)
    at org.gradle.api.internal.tasks.execution.ResolveBeforeExecutionOutputsTaskExecuter.execute(ResolveBeforeExecutionOutputsTaskExecuter.java:67)
    at org.gradle.api.internal.tasks.execution.StartSnapshotTaskInputsBuildOperationTaskExecuter.execute(StartSnapshotTaskInputsBuildOperationTaskExecuter.java:62)
    at org.gradle.api.internal.tasks.execution.ResolveAfterPreviousExecutionStateTaskExecuter.execute(ResolveAfterPreviousExecutionStateTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:94)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:95)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:73)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:49)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:49)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:43)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:134)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:129)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:202)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:193)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:129)
    at org.gradle.execution.plan.DefaultPlanExecutor.process(DefaultPlanExecutor.java:74)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.executeWithServices(DefaultTaskExecutionGraph.java:178)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.execute(DefaultTaskExecutionGraph.java:154)
    at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:41)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:40)
    at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:24)
    at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:46)
    at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:49)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:40)
    at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:33)
    at org.gradle.execution.IncludedBuildLifecycleBuildExecuter.execute(IncludedBuildLifecycleBuildExecuter.java:36)
    at org.gradle.execution.NotifyingBuildExecuter$ExecuteTasks.run(NotifyingBuildExecuter.java:56)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.execution.NotifyingBuildExecuter.execute(NotifyingBuildExecuter.java:41)
    at org.gradle.initialization.DefaultGradleLauncher.runTasks(DefaultGradleLauncher.java:235)
    at org.gradle.initialization.DefaultGradleLauncher.doClassicBuildStages(DefaultGradleLauncher.java:146)
    at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:125)
    at org.gradle.initialization.DefaultGradleLauncher.executeTasks(DefaultGradleLauncher.java:105)
    at org.gradle.internal.invocation.GradleBuildController$1.execute(GradleBuildController.java:58)
    at org.gradle.internal.invocation.GradleBuildController$1.execute(GradleBuildController.java:55)
    at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:82)
    at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:75)
    at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:183)
    at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
    at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:75)
    at org.gradle.internal.invocation.GradleBuildController.run(GradleBuildController.java:55)
    at org.gradle.tooling.internal.provider.runner.BuildModelActionRunner.run(BuildModelActionRunner.java:54)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
    at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:58)
    at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
    at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:39)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:51)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:45)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:45)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:49)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:46)
    at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:78)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:46)
    at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:31)
    at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:42)
    at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:28)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78)
    at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:52)
    at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:59)
    at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:36)
    at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:68)
    at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:38)
    at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:37)
    at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:26)
    at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43)
    at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:60)
    at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55)
    at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:48)
    at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:32)
    at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:37)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
    at org.gradle.util.Swapper.swap(Swapper.java:38)
    at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:62)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:81)
    at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
    at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
    at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
    at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:295)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    Caused by: com.android.builder.internal.aapt.v2.Aapt2InternalException: AAPT2 aapt2-3.4.2-5326820-osx Daemon #0: Unexpected error during link, attempting to stop daemon.
    This should not happen under normal circumstances, please file an issue if it does.
    at com.android.builder.internal.aapt.v2.Aapt2Daemon.handleError(Aapt2Daemon.kt:148)
    at com.android.builder.internal.aapt.v2.Aapt2Daemon.link(Aapt2Daemon.kt:110)
    at com.android.builder.internal.aapt.v2.Aapt2DaemonManager$LeasedAaptDaemon.link(Aapt2DaemonManager.kt:176)
    at com.android.builder.core.AndroidBuilder.processResources(AndroidBuilder.java:858)
    at com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask$AaptSplitInvoker.invokeAaptForSplit(LinkApplicationAndroidResourcesTask.kt:797)
    at com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask$AaptSplitInvoker.run(LinkApplicationAndroidResourcesTask.kt:669)
    at com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask.doFullTaskAction(LinkApplicationAndroidResourcesTask.kt:262)
    at com.android.build.gradle.internal.tasks.IncrementalTask.taskAction(IncrementalTask.java:106)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:103)
    at org.gradle.api.internal.project.taskfactory.IncrementalTaskInputsTaskAction.doExecute(IncrementalTaskInputsTaskAction.java:46)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:41)
    at org.gradle.api.internal.project.taskfactory.AbstractIncrementalTaskAction.execute(AbstractIncrementalTaskAction.java:25)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$5.run(ExecuteActionsTaskExecuter.java:401)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:390)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:373)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$200(ExecuteActionsTaskExecuter.java:79)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:210)
    at org.gradle.internal.execution.steps.ExecuteStep.lambda$execute$0(ExecuteStep.java:32)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:32)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:26)
    at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:58)
    at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:35)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:48)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:33)
    at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:39)
    at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:73)
    at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:54)
    at org.gradle.internal.execution.steps.CatchExceptionStep.execute(CatchExceptionStep.java:35)
    at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
    at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:45)
    at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:31)
    at org.gradle.internal.execution.steps.CacheStep.executeWithoutCache(CacheStep.java:201)
    at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:70)
    at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:45)
    at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:49)
    at org.gradle.internal.execution.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:43)
    at org.gradle.internal.execution.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:32)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:38)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:24)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:96)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:89)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:54)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:38)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:77)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:37)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:36)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:26)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:90)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:48)
    at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:33)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:117)
    ... 131 more
    Caused by: java.io.IOException: Unable to make AAPT link command.
    at com.android.builder.internal.aapt.v2.Aapt2DaemonUtil.requestLink(Aapt2DaemonUtil.java:43)
    at com.android.builder.internal.aapt.v2.Aapt2DaemonImpl.doLink(Aapt2DaemonImpl.kt:177)
    at com.android.builder.internal.aapt.v2.Aapt2Daemon.link(Aapt2Daemon.kt:103)
    ... 188 more
    Caused by: com.android.builder.internal.aapt.AaptException: Dependent features configured but no package ID was set.
    at com.android.builder.internal.aapt.v2.AaptV2CommandBuilder.makeLinkCommand(AaptV2CommandBuilder.kt:304)
    at com.android.builder.internal.aapt.v2.Aapt2DaemonUtil.requestLink(Aapt2DaemonUtil.java:41)
    ... 190 more


提示packageId没有设置,也就是插件的packageId,这个看了代码应该是系统设置的

if (config.packageId != null) {
if (config.allowReservedPackageId) {
builder.add("--allow-reserved-package-id")
}
builder.add("--package-id", "0x" + Integer.toHexString(config.packageId))
for (dependentFeature in config.dependentFeatures) {
builder.add("-I", dependentFeature.absolutePath)
}
} else if (!config.dependentFeatures.isEmpty()) {
throw AaptException("Dependent features configured but no package ID was set.")
}

config.packageId应该是7e

从技术角度看,有点意思,实际应用角度意义不大

从携程 360 腾讯 阿里开源的一些插件化 热加载等等框架,工程开发过程中还是有点复杂度的,小项目用不上,大项目直接拆分多个产品App,加上展示类功能H5化,基本能减少大部分的APK大小。
感觉国内开源都是把快淘汰的技术放出来,纯技术讨论用。哈哈哈

内置插件修改代码覆盖安装,第一次startInstall必然失败

失败log:
2020-09-12 17:11:58.261 21686-21711/com.iqiyi.qigsaw.sample W/SplitDownloadPreprocessor: Failed to rename /data/user/0/com.iqiyi.qigsaw.sample/app_qigsaw/1.0.0_f623dcc/tmp/tmp-java2533143994139208769.apk to /data/user/0/com.iqiyi.qigsaw.sample/app_qigsaw/1.0.0_f623dcc/java/1.1@1/java-master.apk
2020-09-12 17:11:58.261 21686-21711/com.iqiyi.qigsaw.sample I/SplitDownloadPreprocessor: Copy built-in split failed '/data/user/0/com.iqiyi.qigsaw.sample/app_qigsaw/1.0.0_f623dcc/java/1.1@1/java-master.apk': length 0

搜索源码SplitDownloadPreprocessor.copyBuiltInSplit的 tmp.renameTo(splitApk)执行失败了,不知道为啥这里还搞了重试三次的逻辑,是本身就有这个bug吗?

请问如果dynamic-feature依赖的宿主中的资源id如果变化了,同一个dynamic-feature包如何同时支持新版本宿主和老版本宿主?

问题的具体场景如下:
1、dynamic-feature包依赖了宿主中的一个资源,例如一个layout或者string等(原理类似)

2、宿主因为增加功能,例如新增了布局资源,导致这个被依赖的layout的资源id发生了变化

3、那么同一个hynamic-feature包如何同时支持现网老版本的宿主app,以及增加新功能的新版本的宿主app?
因为dynamic-feature包中引用的宿主的那个layout的资源id是跟随宿主编译时,对应的宿主版本layout的id,这个和现网老版本的layout的id是不同的

我们业务使用插件化的一个很重要的目的是:通过插件方式为老版本的宿主APP增加功能。
如果这种方式不支持,业务使用插件化的价值就有所降低了。目前看宿主资源id变化的可能性还是很大的,而且不只是自定义资源,android x的资源id,有可能因为宿主修改,也会导致id的变化。

有几个疑问

1 不使用tinker的情况下,加载插件的数量就是固定的?
2 主App 远程下载完插件到本地后,安装插件过程是不是会弹出安装界面让用户操作?
3 看到sample里 各个feature 都引用了 :app模块,是不是可这么理解:公共库可以放到 app模块内,插件在编译时会把app里的代码也一并编译到插件内生成插件apk? 如果不是,请问,各个featrue都使用到的公共库应该怎么引入?
4 请问这个库目前能在Android 10上正常运行吗?

谢谢

自定义实现动态Activity,提示Resources$NotFoundException: Resource ID #0x7e020002

宿主中添加了代理Activity,然后代理实现插件里未注册的Activity。有些手机就正常可以启动看到界面。
另一些则提示Resources$NotFoundException: Resource ID #0x7e020002。页面载入失败,
用的是dynamic-load-apk这个方案。
插件中的资源不是安装加载后都可以直接用么?
Resources resources = null;
try {
context = ctx.createPackageContext(ctx.getPackageName(), 0);
resources = context.getResources();
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
资源加载一直不成功。请问有没有什么办法,或者该插件是否以后会提供这样的功能。这个功能的实现,难点或者问题在哪里呢?

Unable to get provider com.iqiyi.qigsaw.sample.java.JavaContentProvider

android 11demo 启动失败
2021-05-21 21:08:39.553 30745-30745/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.iqiyi.qigsaw.sample, PID: 30745
java.lang.RuntimeException: Unable to get provider com.iqiyi.qigsaw.sample.java.JavaContentProvider: java.lang.ClassNotFoundException: Didn't find class "com.iqiyi.qigsaw.sample.java.JavaContentProvider" on path: DexPathList[[zip file "/data/app/~~jt2UDqoG_kzUh5O2ypnuuA==/com.iqiyi.qigsaw.sample-oFjDdIHcuKNiFOeCd4gxkQ==/base.apk"],nativeLibraryDirectories=[/data/app/~~jt2UDqoG_kzUh5O2ypnuuA==/com.iqiyi.qigsaw.sample-oFjDdIHcuKNiFOeCd4gxkQ==/lib/arm64, /system/lib64, /system_ext/lib64, /vendor/lib64, /odm/lib64]]
at android.app.ActivityThread.installProvider(ActivityThread.java:7799)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:7335)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7221)
at android.app.ActivityThread.access$1500(ActivityThread.java:293)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2135)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:260)
at android.app.ActivityThread.main(ActivityThread.java:8241)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1006)
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.iqiyi.qigsaw.sample.java.JavaContentProvider" on path: DexPathList[[zip file "/data/app/~~jt2UDqoG_kzUh5O2ypnuuA==/com.iqiyi.qigsaw.sample-oFjDdIHcuKNiFOeCd4gxkQ==/base.apk"],nativeLibraryDirectories=[/data/app/~~jt2UDqoG_kzUh5O2ypnuuA==/com.iqiyi.qigsaw.sample-oFjDdIHcuKNiFOeCd4gxkQ==/lib/arm64, /system/lib64, /system_ext/lib64, /vendor/lib64, /odm/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:207)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.app.AppComponentFactory.instantiateProvider(AppComponentFactory.java:147)
at androidx.core.app.CoreComponentFactory.instantiateProvider(CoreComponentFactory.java:62)
at android.app.ActivityThread.installProvider(ActivityThread.java:7783)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:7335) 
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7221) 
at android.app.ActivityThread.access$1500(ActivityThread.java:293) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2135) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loop(Looper.java:260) 
at android.app.ActivityThread.main(ActivityThread.java:8241) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1006) 

qigsaw-android-sample compile error:

Ubuntu 18.04
branch:master
last commit: 12936b4

$cd /Qigsaw/qigsaw-android-sample
$./gradlew qigsawAssembleDebug

Task :app:validateSigningDebug FAILED

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':app:validateSigningDebug'.

Keystore file '/home/XXXX/workspace/Qigsaw/qigsaw-android-sample/keystore/debug.jks' not found for signing config 'debug'.

the keystore file search fail?

I print the value:
in qigsaw-android-sample,:
project is ":app"
project:rootProject is "qigsaw-android-sample" not "qigsaw"

I think there is solution:
change:
storeFile new File(project.rootProject.projectDir.absolutePath + "/keystore/release.jks")
to
storeFile new File("../keystore/release.jks")

and

change:
storeFile new File(project.rootProject.projectDir.absolutePath + "/keystore/debug.jks")
to
storeFile new File("../keystore/debug.jks")

is ok.

packageInstaller安装时需要root权限?

技术文章里说到可以用packageInstaller安装split apk,但是我自己demo是没不成功的,原因是设备需要开root权限,第三方app才可以安装split apk么?

Conflict between Qigsaw and Fabric gradle plugin.

Hi Qigsaw team,

There is a circular dependency issue between Qigsaw gradle plugin and gradle plugin of Fabric Crashlytics crash reporting framework.

When executing release build type
./gradlew qigsawAssembleMyFlavorRelease

The error log is like:

Circular dependency between the following tasks:
    :app:bundleMyFlavorReleaseClasses
    +--- :app:compileMyFlavorReleaseJavaWithJavac
    |    +--- :app:compileMyFlavorReleaseKotlin
    |    |    \--- :app:processMyFlavorReleaseResources
    |    |         \--- :app:mergeMyFlavorReleaseResources
    |    |              \--- :app:fabricGenerateResourcesMyFlavorRelease
    |    |                   \--- :app:mergeMyFlavorReleaseAssets
    |    |                        \--- :app:qigsawAssembleMyFlavorRelease
    |    |                             \--- :myDynamicFeature:assembleMyFlavorRelease
    |    |                                  +--- :myDynamicFeature:compileMyFlavorReleaseSources
    |    |                                  |    \--- :myDynamicFeature:compileMyFlavorReleaseJavaWithJavac
    |    |                                  |         +--- :app:bundleMyFlavorReleaseClasses (*)
    |    |                                  |         +--- :myDynamicFeature:javaPreCompileMyFlavorRelease
    |    |                                  |         |    \--- :app:bundleMyFlavorReleaseClasses (*)
    |    |                                  |         \--- :myDynamicFeature:processMyFlavorReleaseResources
    |    |                                  |              \--- :app:processMyFlavorReleaseResources (*)
    |    |                                  +--- :myDynamicFeature:lintVitalMyFlavorRelease
    |    |                                  |    \--- :myDynamicFeature:compileMyFlavorReleaseJavaWithJavac (*)
    |    |                                  \--- :myDynamicFeature:packageMyFlavorRelease
    |    |                                       +--- :app:transformDexWithDexSplitterForMyFlavorRelease
    |    |                                       |    +--- :app:transformClassesAndResourcesWithProguardForMyFlavorRelease
    |    |                                       |    |    +--- :app:mergeMyFlavorReleaseAaptProguardFiles
    |    |                                       |    |    |    +--- :app:processMyFlavorReleaseResources (*)
    |    |                                       |    |    |    \--- :myDynamicFeature:processMyFlavorReleaseResources (*)
    |    |                                       |    |    +--- :app:transformClassesWithFirebasePerformancePluginForMyFlavorRelease
    |    |                                       |    |    |    \--- :app:transformClassesWithCreateComponentInfoTransformForMyFlavorRelease
    |    |                                       |    |    |         +--- :app:compileMyFlavorReleaseJavaWithJavac (*)
    |    |                                       |    |    |         \--- :app:compileMyFlavorReleaseKotlin (*)
    |    |                                       |    |    +--- :app:transformResourcesWithMergeJavaResForMyFlavorRelease
    |    |                                       |    |    |    +--- :app:compileMyFlavorReleaseJavaWithJavac (*)
    |    |                                       |    |    |    +--- :app:compileMyFlavorReleaseKotlin (*)
    |    |                                       |    |    |    \--- :myDynamicFeature:transformResourcesWithMergeJavaResForMyFlavorRelease
    |    |                                       |    |    |         \--- :myDynamicFeature:compileMyFlavorReleaseJavaWithJavac (*)
    |    |                                       |    |    \--- :myDynamicFeature:transformClassesWithMergeClassesForMyFlavorRelease
    |    |                                       |    |         \--- :myDynamicFeature:transformClassesWithSplitComponentTransformForMyFlavorRelease
    |    |                                       |    |              \--- :myDynamicFeature:compileMyFlavorReleaseJavaWithJavac (*)
    |    |                                       |    +--- :app:transformClassesWithMergeClassesForMyFlavorRelease
    |    |                                       |    |    \--- :app:transformClassesWithFirebasePerformancePluginForMyFlavorRelease (*)
    |    |                                       |    +--- :app:transformDexArchiveWithDexMergerForMyFlavorRelease
    |    |                                       |    |    \--- :app:transformClassesWithDexBuilderForMyFlavorRelease
    |    |                                       |    |         \--- :app:transformClassesAndResourcesWithProguardForMyFlavorRelease (*)
    |    |                                       |    \--- :myDynamicFeature:transformClassesWithMergeClassesForMyFlavorRelease (*)
    |    |                                       +--- :myDynamicFeature:compileMyFlavorReleaseJavaWithJavac (*)
    |    |                                       +--- :myDynamicFeature:processMyFlavorReleaseResources (*)
    |    |                                       +--- :myDynamicFeature:transformDexArchiveWithDexMergerForMyFlavorRelease
    |    |                                       |    \--- :myDynamicFeature:transformClassesWithDexBuilderForMyFlavorRelease
    |    |                                       |         +--- :app:bundleMyFlavorReleaseClasses (*)
    |    |                                       |         \--- :myDynamicFeature:transformClassesWithSplitComponentTransformForMyFlavorRelease (*)
    |    |                                       \--- :myDynamicFeature:transformResourcesWithMergeJavaResForMyFlavorRelease (*)
    |    +--- :app:generateMyFlavorReleaseSources
    |    |    \--- :app:fabricGenerateResourcesMyFlavorRelease (*)
    |    +--- :app:javaPreCompileMyFlavorRelease
    |    |    \--- :app:compileMyFlavorReleaseKotlin (*)
    |    \--- :app:processMyFlavorReleaseResources (*)
    \--- :app:compileMyFlavorReleaseKotlin (*)

The project contains simple "myDynamicFeature" dynamic feature module and simple product flavor "MyFlavor".

For debug build type it's fine since the fabricGenerateResources gradle task of Fabric would not be added to the dependsOn list. But not the case for the release build.

Please help check if Qigsaw framework can work with Fabric Crashlytics.

THX.

split proguard

split 包可以混淆吗?
下发的split包是CreateSplitDetailsFileTask中的,应该是没有混淆的吧
如果split包的混淆相关 有什么思路可以提供吗?

建议增加『新增动态模块』的功能

建议增加『新增动态模块』的功能,应用场景例如:

  • 1、临时性的业务模块(比如电商季节性的活动?或者是某些临时营销活动的宣传、广告之类);
  • 2、需要持续新增的业务模块(比如用于新增小游戏模块?);

不需要支持新增四大组件(虽然技术上也无法实现),但新增的模块可以通过已有的(base已注册)代理activity加载fragment等形式去实现,因此还是有一定的意义所在的。

Qigsaw与viewBinding冲突

A problem occurred evaluating project ':app'.

Could not find method buildFeatures() for arguments [build_ac29nl13sp1j228wqm2rza2op$_run_closure1$_closure9@43334137] on extension 'android' of type com.android.build.gradle.internal.dsl.BaseAppModuleExtension.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.