Code Monkey home page Code Monkey logo

walle's Introduction

Walle

Release Version Build Status PRs Welcome License

Walle(瓦力):Android Signature V2 Scheme签名下的新一代渠道包打包神器

瓦力通过在Apk中的APK Signature Block区块添加自定义的渠道信息来生成渠道包,从而提高了渠道包生成效率,可以作为单机工具来使用,也可以部署在HTTP服务器上来实时处理渠道包Apk的升级网络请求。

Quick Start

为了方便大家的使用,我们提供了2种使用方式:

  • Gradle插件方式,方便快速集成
  • 命令行方式,最大化满足各种自定义需求

Gradle插件使用方式

配置build.gradle

在位于项目的根目录 build.gradle 文件中添加Walle Gradle插件的依赖, 如下:

buildscript {
    dependencies {
        classpath 'com.meituan.android.walle:plugin:1.1.7'
    }
}

并在当前App的 build.gradle 文件中apply这个插件,并添加上用于读取渠道号的AAR

apply plugin: 'walle'

dependencies {
    compile 'com.meituan.android.walle:library:1.1.7'
}

配置插件

walle {
    // 指定渠道包的输出路径
    apkOutputFolder = new File("${project.buildDir}/outputs/channels");
    // 定制渠道包的APK的文件名称
    apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
    // 渠道配置文件
    channelFile = new File("${project.getProjectDir()}/channel")
}

配置项具体解释:

  • apkOutputFolder:指定渠道包的输出路径, 默认值为new File("${project.buildDir}/outputs/apk")

  • apkFileNameFormat:定制渠道包的APK的文件名称, 默认值为'${appName}-${buildType}-${channel}.apk'
    可使用以下变量:

         projectName - 项目名字
         appName - App模块名字
         packageName - applicationId (App包名packageName)
         buildType - buildType (release/debug等)
         channel - channel名称 (对应渠道打包中的渠道名字)
         versionName - versionName (显示用的版本号)
         versionCode - versionCode (内部版本号)
         buildTime - buildTime (编译构建日期时间)
         fileSHA1 - fileSHA1 (最终APK文件的SHA1哈希值)
         flavorName - 编译构建 productFlavors 名
    
  • channelFile:包含渠道配置信息的文件路径。 具体内容格式详见:渠道配置文件示例,支持使用#号添加注释。

如何获取渠道信息

在需要渠道等信息时可以通过下面代码进行获取

String channel = WalleChannelReader.getChannel(this.getApplicationContext());

如何生成渠道包

生成渠道包的方式是和assemble${variantName}Channels指令结合,渠道包的生成目录默认存放在 build/outputs/apk/,也可以通过walle闭包中的apkOutputFolder参数来指定输出目录

用法示例:

  • 生成渠道包 ./gradlew clean assembleReleaseChannels
  • 支持 productFlavors ./gradlew clean assembleMeituanReleaseChannels

更多用法

插入额外信息

channelFile只支持渠道写入,如果想插入除渠道以外的其他信息,请在walle配置中使用configFile

walle {
    // 渠道&额外信息配置文件,与channelFile互斥
	configFile = new File("${project.getProjectDir()}/config.json")
}

configFile是包含渠道信息和额外信息的配置文件路径。
配置文件采用json格式,支持为每个channel单独配置额外的写入信息。具体内容格式详见:渠道&额外信息配置文件示例

注意:

  • 此配置项与channelFile功能互斥,开发者在使用时选择其一即可,两者都存在时configFile优先执行。
  • extraInfo 不要出现以channel为key的情况

而对应的渠道信息获取方式如下:

ChannelInfo channelInfo= WalleChannelReader.getChannelInfo(this.getApplicationContext());
if (channelInfo != null) {
   String channel = channelInfo.getChannel();
   Map<String, String> extraInfo = channelInfo.getExtraInfo();
}
// 或者也可以直接根据key获取
String value = WalleChannelReader.get(context, "buildtime");
临时生成某渠道包

我们推荐使用channelFile/configFile配置来生成渠道包,但有时也可能有临时生成渠道包需求,这时可以使用:

  • 生成单个渠道包: ./gradlew clean assembleReleaseChannels -PchannelList=meituan

  • 生成多个渠道包: ./gradlew clean assembleReleaseChannels -PchannelList=meituan,dianping

  • 生成渠道包&写入额外信息:

    ./gradlew clean assembleReleaseChannels -PchannelList=meituan -PextraInfo=buildtime:20161212,hash:xxxxxxx

    注意: 这里的extraInfo以key:value形式提供,多个以,分隔。

  • 使用临时channelFile生成渠道包: ./gradlew clean assembleReleaseChannels -PchannelFile=/Users/xx/Documents/channel

  • 使用临时configFile生成渠道包: ./gradlew clean assembleReleaseChannels -PconfigFile=/Users/xx/Documents/config.json

使用上述-P参数后,本次打包channelFile/configFile配置将会失效,其他配置仍然有效。 -PchannelList,-PchannelFile, -PconfigFile三者不可同时使用。

命令行工具使用方式

可以使用命令行工具来支持各类自定义的需求,具体使用方式详见:Walle CLI 使用说明

其他使用方式

为了更好的满足大家的各类自定义需求,我们把对APK Signing Block区块进行读写操作的模块进行了封装。

读写模块的使用说明详见:

Q&A

原理介绍

对该工具的原理感兴趣的同学,可以移步美团Android新一代渠道包生成工具进行了解。

注意事项

  • 使用apksigner重新对Apk签名会导致渠道信息丢失,需要再次写入渠道信息
  • 1.1.3版本起,walle支持对含有comment的apk进行渠道写入, 详见issue 52

技术支持

  • Read The Fucking Source Code
  • 通过提交issue来寻求帮助
  • 联系我们寻求帮助

贡献代码

  • 欢迎提交issue
  • 欢迎提交PR

参考

License

Copyright 2017 Meituan-Dianping

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

walle's People

Contributors

achellies avatar kc910521 avatar sailor-deng avatar yrom 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  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

walle's Issues

使用java -jar walle-cli.jar -h报NullPointerException错误

hyhedeMacBook-Air:ajkwalle hyhe$ java -jar walle-cli.jar -h
Exception in thread "main" java.lang.NullPointerException
	at com.beust.jcommander.JCommander.usage(JCommander.java:1134)
	at com.beust.jcommander.JCommander.usage(JCommander.java:1049)
	at com.beust.jcommander.JCommander.usage(JCommander.java:1041)
	at com.meituan.android.walle.WalleCommandLine.parse(WalleCommandLine.java:28)
	at com.meituan.android.walle.Main.main(Main.java:46)

因为我们公司的渠道命名规则是前缀加数字,如b00,b01,b999 这类有规则的命名方式,渠道号配置文件是b_00_999,所以对源码中WriteChannelsCommand.java类的parse方法做了修改

      if (channelFile != null) {
            try {
                final List lines = IOUtils.readLines(new FileInputStream(channelFile), "UTF-8");
                for (String line : lines) {
                    final String lineTrim = line.trim();
                    if (lineTrim.matches("^[a-zA-Z]_\\d*_\\d*")) {
                        String[] values = lineTrim.split("_");
                        String preStr = values[0];
                        int start = Integer.parseInt(values[1]);
                        int end = Integer.parseInt(values[2]);
                        for (int i = start; i <= end; i++) {
                            String channel = String.format("%s%s%d", preStr, i < 10 ? "0" : "",i);
                            generateChannelApk(inputFile, outputDir, channel);
                        }
                    } else {
                        continue;
                    }

                }
                System.out.println("finished!");
            } catch (IOException e) {
                System.out.println(e.getMessage());
            }
        }

并且为了打“胖”jar包,在 build.gradle中jar中加了

jar {
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    manifest {
        attributes 'Main-Class': 'com.meituan.android.walle.Main'
        attributes 'Walle-Version': VERSION_NAME
    }
}

见我的fork分支https://github.com/hyhe/walle
做了上述修改后,打出来的jar包 -h就报错了,其它命令参数可正常使用。

productFlavors 配置 buildConfigField

您好,我想问下,我根据不同的渠道,我们需要设置不同的属性,想下面这种怎么实现?
productFlavors {
dev {
buildConfigField 'boolean', 'DIDA_FLAVOR', "true"
}
yingyongbao {
buildConfigField 'boolean', 'DIDA_FLAVOR', "false"
}
}

walle-cli如何制定 新APK的Name

指定渠道配置文件 java -jar walle-cli-all.jar batch -f /Users/Meituan/walle/app/channel 用这个命令进行批量打包,如何规范新的apk name

请教,新的centralDirStartOffset为什么要额外加上8,这是block是指哪一个?

            fIn.seek(fileChannel.size() - 6);
            // 6 = 2(Comment length) + 4 (Offset of start of central directory, relative to start of archive)
            final ByteBuffer temp = ByteBuffer.allocate(4);
            temp.order(ByteOrder.LITTLE_ENDIAN);
            temp.putInt((int) (centralDirStartOffset + length +
                    8 - (centralDirStartOffset - apkSigningBlockOffset)));
            // 8 = size of block in bytes (excluding this field) (uint64)
            temp.flip();
            fIn.write(temp.array());

加固后失效

使用第三方加固后,需要重新签名的,所以这种多渠道打包方式失效了。只能使用常规的gradle配置多渠道,加固后再签名

从代码上看,channelFile和channelList是并且的关系

        if (channelListProperty != null && channelListProperty.trim().length() > 0) {
            channelList.addAll(channelListProperty.split(",").collect { it.trim() })
        }
        if (channelFileProperty != null && channelFileProperty.trim().length() > 0) {
            channelList.addAll(getChannelListFromFile(targetProject, channelFileProperty))
        }

gradle3.3中没有getAndroidGradlePluginVersion()

com.android.tools.build:gradle:2.3.0-beta2 gradle3.3中getAndroidGradlePluginVersion 被com.android.builder.Version.ANDROID_GRADLE_PLUGIN_VERSION替代,导致插件出错
可以使用getGradle().getGradleVersion()或com.android.builder.Version.ANDROID_GRADLE_PLUGIN_VERSION

重新签名后再修改渠道失败

使用jarsigner对apk重新签名后,再执行写入渠道号失败,异常:
com.meituan.android.walle.SignatureNotFoundException: No APK Signing Block before ZIP Central Directory
at com.meituan.android.walle.ApkUtil.findApkSigningBlock(ApkUtil.java:125)
at com.meituan.android.walle.PayloadWriter.handleApkSigningBlock(PayloadWriter.java:77)
at com.meituan.android.walle.PayloadWriter.putAll(PayloadWriter.java:43)
at com.meituan.android.walle.PayloadWriter.put(PayloadWriter.java:31)
at com.meituan.android.walle.ChannelWriter.putRaw(ChannelWriter.java:73)
at com.meituan.android.walle.ChannelWriter.put(ChannelWriter.java:55)
at com.meituan.android.walle.commands.WriteChannelCommand.parse(WriteChannelCommand.java:51)
at com.meituan.android.walle.Main.main(Main.java:49)

无法修改打包生成的apk名字

productFlavors {
ceshi {}
}
通过这种方式指定渠道名
再结合这个命令 gradlew clean assembleRelease
问题:
可以打出包,但是获取不到渠道名,为空

channel file does not exist

walle 项目直接checkout下来,在AS的Terminal执行 ./gradlew clean assembleRelease -PchannelFile=channel 渠道包没有打出来
image

channel文件
image

简化配置插件的脚本

使用这个替换

plugins {
  id "com.meituan.android.walle:plugin" version "1.0.5"
}

这个

buildscript {
    dependencies {
        classpath 'com.meituan.android.walle:plugin:1.0.5'
    }
}

apply plugin: 'walle'

build error

build error: Plugin requires 'APK Signature Scheme v2 Enabled' for splits

多渠道附加信息的需求

有这么个需求,App内部会有评价这个功能,跳转到应用市场评价,并且需要优先跳转到所发渠道的应用商城。

例如:当前发版的渠道是360,但是手机上如果装了360和应用宝则直接跳转到360,而不是让用户去选择。如果手机上没有渠道对应的应用市场,则让用户选择或者提示未安装市场。

因为每个渠道的附加信息是不一样的,所以建议在渠道里面可以配置每个渠道的附加信息,并且也可以配置全局的公用的附加信息。

channel文件中可能类似下面的:

#360渠道
360cn:package=com.qihoo.appstore,arg1=xxx
#应用宝渠道
myapp:package=com.tencent.android.qqdownloader,arg1=xxx

或者

#360渠道
360cn package:com.qihoo.appstore,arg1:xxx
#应用宝渠道
myapp package:com.tencent.android.qqdownloader,arg1:xxx

@achellies

加固后手动进行v2签名的问题

选择360加固后需要重签名,360自带工具是v1签名,手动用apksigner进行签名的话报错,如下:

D:\develop\git\keystore>apksigner sign --ks D:\develop\git\keystore\simple_test1
.    jks D:\develop\git\keystore\app-official-release-360cn.encrypted.apk
Keystore password for signer #1:
Failed to load signer "signer #1"
java.io.IOException: Failed to obtain key with alias "test1" from D:\develop\git
\keystore\simple_test1.jks. Wrong password?
        at com.android.apksigner.ApkSignerTool$SignerParams.loadPrivateKeyAndCer
tsFromKeyStore(ApkSignerTool.java:690)
        at com.android.apksigner.ApkSignerTool$SignerParams.loadPrivateKeyAndCer
ts(ApkSignerTool.java:555)
        at com.android.apksigner.ApkSignerTool$SignerParams.access$200(ApkSigner
Tool.java:509)
        at com.android.apksigner.ApkSignerTool.sign(ApkSignerTool.java:215)
        at com.android.apksigner.ApkSignerTool.main(ApkSignerTool.java:89)
Caused by: java.security.UnrecoverableKeyException: Cannot recover key
        at sun.security.provider.KeyProtector.recover(KeyProtector.java:328)
        at sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:146
)
        at sun.security.provider.JavaKeyStore$JKS.engineGetKey(JavaKeyStore.java
:56)
        at sun.security.provider.KeyStoreDelegator.engineGetKey(KeyStoreDelegato
r.java:96)
        at sun.security.provider.JavaKeyStore$DualFormatJKS.engineGetKey(JavaKey
Store.java:70)
        at java.security.KeyStore.getKey(KeyStore.java:1023)
        at com.android.apksigner.ApkSignerTool$SignerParams.loadPrivateKeyAndCer
tsFromKeyStore(ApkSignerTool.java:663)
        ... 4 more

但是使用调试签名进行v2签名是可以的,获取v2签名信息如下:

D:\develop\git\keystore>java -jar D:\develop\git\keystore\CheckAndroidV2Signatur
e.jar D:\develop\git\keystore\app-official-release-360cn.encrypted.apk
{"ret":0,"msg":"ok","isV2":true,"isV2OK":true} 

通过Keytool命令,分析两个签名的内容,只找到一处不同:

  • 可以通过的debug的签名用的是签名算法名称: SHA1withRSA 版本: 3
  • 无法签名,提示错误的签名用的是签名算法是:签名算法名称: SHA256withRSA 版本: 3

另:报错的签名是AS进行创建的。

附两个keystore的keytool信息:


D:\develop\git\keystore>keytool -list -v -keystore D:\develop\git\keystore\simpl
e_test1.jks
输入密钥库口令:

密钥库类型: JKS
密钥库提供方: SUN

您的密钥库包含 1 个条目

别名: test1
创建日期: 2017-2-8
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
所有者: CN=t, OU=t, O=t, L=t, ST=t, C=86
发布者: CN=t, OU=t, O=t, L=t, ST=t, C=86
序列号: 1b12f74d
有效期开始日期: Wed Feb 08 10:35:46 CST 2017, 截止日期: Sun Feb 02 10:35:46 CST
2042
证书指纹:
         MD5: 76:C0:09:30:FC:7B:A2:6A:57:8F:5B:E0:4F:A4:83:A5
         SHA1: 29:8D:00:CC:3A:74:AE:F8:4C:3F:87:C4:04:86:A0:60:65:A8:1B:37
         SHA256: FC:2A:5A:CC:E3:6E:9F:70:15:DD:01:99:0A:EB:16:D2:86:E5:E4:7F:A1:
92:56:32:A4:9B:F5:1D:24:A1:46:3D
         签名算法名称: SHA256withRSA
         版本: 3

扩展:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 42 9D DE 94 66 61 B1 17   0F D6 1B 54 B8 C7 38 D9  B...fa.....T..8.
0010: 3B E7 56 20                                        ;.V
]
]



*******************************************
*******************************************

D:\develop\git\keystore>keytool -list -v -keystore C:\Users\Administrator\.andro
id\debug.keystore
输入密钥库口令:

密钥库类型: JKS
密钥库提供方: SUN

您的密钥库包含 1 个条目

别名: androiddebugkey
创建日期: 2016-9-5
条目类型: PrivateKeyEntry
证书链长度: 1
证书[1]:
所有者: C=US, O=Android, CN=Android Debug
发布者: C=US, O=Android, CN=Android Debug
序列号: 1
有效期开始日期: Mon Sep 05 14:05:29 CST 2016, 截止日期: Wed Aug 29 14:05:29 CST
2046
证书指纹:
         MD5: 3F:2A:E7:EA:71:D2:EB:DF:EE:BE:75:60:1F:3C:72:18
         SHA1: 5F:78:1B:6C:94:DB:0C:9C:1A:8D:C0:EE:B6:23:A8:F1:82:FB:71:8C
         SHA256: 01:5B:88:57:01:71:6C:E9:3A:24:39:DF:C2:ED:DB:BC:3C:03:B7:FD:73:
C5:09:59:F0:3F:2C:4D:32:72:4C:82
         签名算法名称: SHA1withRSA
         版本: 1


*******************************************
*******************************************

Could not find method walle() for arguments

Error:(95, 0) Could not find method walle() for arguments [build_1xpnhc3ozz0wtrcpdgdc37t1j$_run_closure2@28c2c774] on project ':app' of type org.gradle.api.Project.

walle plugin:1.0.3
gradle version:3.3
Android Studio:2.2.3
OS:WIndows 10 X64

proposal: 将 payload 标准化

通过阅读代码发现, 是在 APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
ID = 0x7109871a; 处写了一个 json 字符串

{
  "channel": "channel_name",
   ...extrainfo
}

这个 ID 也算 magic number, 这样 Reader 才能读到.
标准化之后, 就不局限与打包了, 我需要读DB, 写 APK, 算MD5, 写DB, 上传 CDN 等等, 需要使用其他语言来写...

打包命令bug

我在terminal执行
./gradlew clean assembleReleaseChannels -PchannelList=200001000021
想要打单个渠道包,但是打包的结果是工程目录下channle文件夹里面的50个渠道包,并没有打出渠道号是200001000021的渠道包

省去手动app依赖配置

用这个代替

@Override 
void apply(Project project) {
    project.dependencies {
        compile 'com.meituan.android.walle:library:1.0.5'
    }
}
``
这个
```java
dependencies {
    compile 'com.meituan.android.walle:library:1.0.5'
}

请教zip comment的字数为什么一定为4?

    /**
     * check zip comment
     *
     * @param fileChannel fileChannel
     * @return true if has comment
     * @throws IOException
     */
    public static boolean checkComment(final FileChannel fileChannel) throws IOException {
        // End of central directory record (EOCD)
        // Offset     Bytes     Description[23]
        // 0            4       End of central directory signature = 0x06054b50
        // 4            2       Number of this disk
        // 6            2       Disk where central directory starts
        // 8            2       Number of central directory records on this disk
        // 10           2       Total number of central directory records
        // 12           4       Size of central directory (bytes)
        // 16           4       Offset of start of central directory, relative to start of archive
        // 20           2       Comment length (n)
        // 22           n       Comment
        // For a zip with no archive comment, the
        // end-of-central-directory record will be 22 bytes long, so
        // we expect to find the EOCD marker 22 bytes from the end.
        final ByteBuffer byteBuffer = ByteBuffer.allocate(4);
        fileChannel.position(fileChannel.size() - 22);
        fileChannel.read(byteBuffer);

        if (byteBuffer.get(0) != 0x50 ||
                byteBuffer.get(1) != 0x4b ||
                byteBuffer.get(2) != 0x05 ||
                byteBuffer.get(3) != 0x06) {
            return true;
        }
        return false;
    }

360加固后,重新打渠道问题

你好!
对于360开放平台的流氓式强制加固,后需要重新签名,重新打渠道问题,美团是怎么解决的呢?
我使用了“jarsigner -verbose -keystore ${KEYSTORE_PATH} -storepass ${STOREPASS} -signedjar ${SIGN_APK_PATH} -digestalg SHA1 -sigalg MD5withRSA ${APK_PATH} ${ALIAS_NAME} -tsa https://timestamp.geotrust.com/tsa”这个命令重新签名后(这个签名命令是旧版本的签名命令,找不到v2版的签名命令),使用 java -jar walle-cli-all.jar put -c qihu360 CloudReader-qihu360_signed.apk 打渠道,会直接抛异常。

channel file does not exist 无法打多渠道包

使用最新的1.0.3 ,无法打多渠道包
输出包提示如下信息:
Removed unused resources: Binary resource data reduced from 4323KB to 4323KB: Removed 0%
:app:mergeReleaseJniLibFolders
:app:transformNative_libsWithMergeJniLibsForRelease
:app:packageRelease
:app:assembleRelease
:app:assembleReleaseV2SignatureSchemeChannel
channel file does not exist
APK Signature Scheme v2 Channel Maker takes about 0 milliseconds

BUILD SUCCESSFUL

Total time: 5 mins 17.994 secs
在build\outputs\apk目录下只有一个release包,无配置文件中多渠道的包
channel文件在android\app下

请问是什么原因导致channel文件找不到的?

能给个demo吗?

能给个gradle 配置demo吗? 现在还没看懂怎么用!水平有限,希望能照顾下我们这样的猿友!

Could not find method walle()

我根据文档配置后,Sync项目后,报下面的错误:
Error:(239, 0) Could not find method walle() for arguments [build_8e710nlujanxx3jeuass6znpr$_run_closure3@76a8173f] on project ':app' of type org.gradle.api.Project.
我已经加上apply plugin: 'walle'了。
希望能解答一下,谢谢~

集成失败

Error:Plugin requires 'APK Signature Scheme v2 Enabled' for release.

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.