ABI 拆分在 NativeScript 2.3.0 中失败

ABI Split failed in NativeScript 2.3.0

我已经在普通组中寻求帮助,但大多数人也没有得到帮助。 我的简单应用程序有一个荒谬的 apk 大小的大问题。我使用@markosko nativescript 过滤器将应用程序从 17.2MB 减少到 14MB,即使 nativescript-snapshot 也无法帮助发布版本仍然是 17MB。

我尝试使用 nativescipt 文档中的 ABI 拆分示例,但我注意到它正在尝试拆分,但 glade 对所有 apk 使用相同的名称,所以我在 app.glade 中想到了这个

def tnsBuildMultipleApks=true;

android {  
    defaultConfig {  
        generatedDensities = []
        applicationId = "com.maliyo.oneclick"  
        versionCode Integer.parseInt("" + "1" + "0")
    }  
    aaptOptions {  
        additionalParameters "--no-version-vectors"  
    }
    if (Boolean.valueOf(tnsBuildMultipleApks)) {
        splits {
            abi {
                enable true
                reset()
                include  'armeabi', 'armeabi-v7a', 'x86', 'mips'
                universalApk true
            }
        }
    }
} 

def getDate() {
  def date = new Date()
  def formattedDate = date.format('yyyyMMdd')
  return formattedDate
}

// map for the version code that gives each ABI a value
ext.versionCodes = [
    'F0F1F2X86Debug':1, 'F0F1F2ArmeabiDebug':2, 'F0F1F2Armeabi-v7aDebug':3, 'F0F1F2MipsDebug':4,
    'F0F1F2X86Release':1, 'F0F1F2ArmeabiRelease':2, 'F0F1F2Armeabi-v7aRelease':3, 'F0F1F2MipsRelease':4
    ]

// For each APK output variant, override versionCode with a combination of
// ABI APK value * 100 + defaultConfig.versionCode
android.applicationVariants.all { variant ->
    // assign different version code for each output
    variant.outputs.each { output ->
        if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
            println("******************************************************")
            println(output);
            println(output.getVariantOutputData().getFullName())

            if (Boolean.valueOf(tnsBuildMultipleApks)) {
                def file = output.outputFile
                // version at the end of each built apk
                //output.outputFile = new File(file.parent, file.name.replace(".apk", "-" + android.defaultConfig.versionName + "-" + getDate() + ".apk"))
                output.outputFile = new File(file.parent, file.name.replace(".apk", "-" + output.getVariantOutputData().getFullName() + "-" + getDate() + ".apk"))
                output.versionCodeOverride =
                    //(assd++) * 100
                    project.ext.versionCodes.get(output.getVariantOutputData().getFullName(), 0) * 100
                    + android.defaultConfig.versionCode
            }
        }
    }
}
/**/

好吧,它拆分了,但我认为因为我在输出中破解了文件名,adb 由于命名模式找不到将 apk 推送到设备或模拟器的方法,也许,只是说 apk not found .

我尝试通过 USB 手动将适当的 apk 发送到设备,它的应用程序已成功安装,但在闪屏显示 metadata/treeNodeStream.dat could not be loaded

后崩溃

更新

@plamen-petkov 非常感谢您的贡献,我同意您的看法,当您一个接一个地构建并更改 abi 过滤器时,它工作正常。但是在我的 app.gradle 中,我成功地构建了多个 apk 并进行了测试,一切正常。

但就像 tns 仅将 appname-debug.apkappname-release.apk 推送到 adb。我可以用 tnsBuildMultipleApks 关闭这个拆分,也许当我还在测试时,我可以关闭它并使用 tns run android ,当我想进行最终构建时,它会再次打开它,因为它与 tns build android --release ....

// Add your native dependencies here:

// Uncomment to add recyclerview-v7 dependency
//dependencies {
//  compile 'com.android.support:recyclerview-v7:+'
//}
import groovy.json.JsonSlurper //used to parse package.json

def tnsBuildMultipleApks=true;
String content = new File("$projectDir/../../app/package.json").getText("UTF-8")
def jsonSlurper = new JsonSlurper()
def appPackageJson = jsonSlurper.parseText(content)

android {  
    defaultConfig {  
        generatedDensities = []
        applicationId = appPackageJson.nativescript.id  
        versionCode = appPackageJson.version_code ?: 1
    }

    aaptOptions {  
        additionalParameters "--no-version-vectors"  
    }
    if (Boolean.valueOf(tnsBuildMultipleApks)) {
        splits {
            abi {
                enable true
                reset()
                include  'x86', 'armeabi-v7a', 'arm64-v8a'
                universalApk true
            }
        }
    }
}

// map for the version code that gives each ABI a value
ext.versionCodes = [
    'x86':1, 'armeabi-v7a':2, 'arm64-v8a':3
]

// For each APK output variant, override versionCode with a combination of
// ABI APK value * 100 + android.defaultConfig.versionCode
// getAbiFilter() not working for me so I extracted it from getFullname()
if (Boolean.valueOf(tnsBuildMultipleApks)) {
    android.applicationVariants.all { variant ->
        println(appPackageJson)
        println(android.defaultConfig.versionCode)
        println(android.defaultConfig.applicationId)

        def name
        def flavorNamesConcat = ""

        variant.productFlavors.each() { flavor ->
            flavorNamesConcat += flavor.name
        }
        flavorNamesConcat = flavorNamesConcat.toLowerCase()
        println(flavorNamesConcat)

        variant.outputs.each { output ->
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                //You may look for this path in your console to see what the values are
                println("******************************************************")
                println(output); println(output.getVariantOutputData().getFullName())

                def abiName = output.getVariantOutputData().getFullName().toLowerCase().replace(flavorNamesConcat, "").replace(project.ext.selectedBuildType, "")
                println(abiName)


                def file = output.outputFile
                output.versionCodeOverride =
                    project.ext.versionCodes.get(abiName, 0) * 100
                    + android.defaultConfig.versionCode

                def apkDirectory = output.packageApplication.outputFile.parentFile
                def apkNamePrefix = output.outputFile.name.replace(".apk", "-" + abiName)

                if (output.zipAlign) {
                    name = apkNamePrefix + ".apk"
                    output.outputFile = new File(apkDirectory, name);
                }

                name = apkNamePrefix + "-unaligned.apk"
                output.packageApplication.outputFile = new File(apkDirectory, name);
            }
        }
    }
}

如果您一次使用 ABI 拆分,它们会很有用。这是一个例子:

android {  
...
    splits {
        abi {
            enable true
            reset()
            include  'armeabi-v7a'
        }
    }
...
}

生成的 .apk 文件将只包含 armeabi-v7a 设备所需的库,因为它是上面 ABI 拆分配置中提到的唯一架构。正如 documentation 中指出的那样,可用的 ABI 是 'arm64-v8a'、'armeabi-v7a'、'x86',因此您不能使用 'armeabi' 或 'mips' 架构.

此外,您不需要这一行:'universalApk true',因为它所做的是忽略拆分并制作一个包含所有提供的体系结构的 .apk 文件,而您想要的恰恰相反。

您还可以关注 this 问题的进展,因为它会进一步减小 .apk 的大小。

希望对您有所帮助!

这对我来说效果很好,生成 apks 和 tns run android 现在运行良好,谢谢。

    // Add your native dependencies here:

// Uncomment to add recyclerview-v7 dependency
//dependencies {
//  compile 'com.android.support:recyclerview-v7:+'
//}
import groovy.json.JsonSlurper //used to parse package.json

def tnsBuildMultipleApks=true;
String content = new File("$projectDir/../../app/package.json").getText("UTF-8")
def jsonSlurper = new JsonSlurper()
def appPackageJson = jsonSlurper.parseText(content)

android {  
    defaultConfig {  
        generatedDensities = []
        applicationId = appPackageJson.nativescript.id  
        versionCode = appPackageJson.version_code ?: 1
    }

    aaptOptions {  
        additionalParameters "--no-version-vectors"  
    }
    if (Boolean.valueOf(tnsBuildMultipleApks)) {
        splits {
            abi {
                enable true
                reset()
                include  'x86', 'armeabi-v7a', 'arm64-v8a'
                universalApk true
            }
        }
    }
}

// map for the version code that gives each ABI a value
ext.versionCodes = [
    'x86':1, 'armeabi-v7a':2, 'arm64-v8a':3
]

// For each APK output variant, override versionCode with a combination of
// ABI APK value * 100 + android.defaultConfig.versionCode
// getAbiFilter() not working for me so I extracted it from getFullname()
if (Boolean.valueOf(tnsBuildMultipleApks)) {
    android.applicationVariants.all { variant ->
        println(appPackageJson)
        println(android.defaultConfig.versionCode)
        println(android.defaultConfig.applicationId)

        def name
        def flavorNamesConcat = ""

        variant.productFlavors.each() { flavor ->
            flavorNamesConcat += flavor.name
        }
        flavorNamesConcat = flavorNamesConcat.toLowerCase()
        println(flavorNamesConcat)

        variant.outputs.each { output ->
            if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
                //You may look for this path in your console to see what the values are
                println("******************************************************")
                println(output); println(output.getVariantOutputData().getFullName())

                def abiName = output.getVariantOutputData().getFullName().toLowerCase().replace(flavorNamesConcat, "").replace(project.ext.selectedBuildType, "")
                println(abiName)


                def file = output.outputFile
                def versionCode = project.ext.versionCodes.get(abiName, 0);
                output.versionCodeOverride =
                    project.ext.versionCodes.get(abiName, 0) * 100
                    + android.defaultConfig.versionCode

                def apkDirectory = output.packageApplication.outputFile.parentFile
                println(output.outputFile.name)
                def apkNamePrefix = ""
                if(versionCode){
                    apkNamePrefix = output.outputFile.name.replace(".apk", "-" + abiName)
                }
                else {
                    apkNamePrefix = output.outputFile.name.replace(".apk", "")
                }

                if (output.zipAlign) {
                    name = apkNamePrefix + ".apk"
                    output.outputFile = new File(apkDirectory, name);
                }

                name = apkNamePrefix + "-unaligned.apk"
                output.packageApplication.outputFile = new File(apkDirectory, name);
            }
        }
    }
}