android插件自定义之多渠道打包插件(支持微信资源混淆andResGuard)

前言

  自定义android studio插件?想想就觉得是一件令人兴奋的事。最近闲来无事决定完善之前的一些代码操作,然后就想到了之前的apk多渠道打包工具,觉得还是太麻烦,何不用插件的形式引入工程自动打包呢。说做就做...

资料整理

  1.插件入门:http://blog.csdn.net/sbsujjbcy/article/details/50782830
  2.插件进阶:AndroidComponent组件化方案,AndResGuard资源混淆库的插件源码
  3.渠道包原理:将渠道打入assets里的文件中,我之前的文章有介绍过此原理的打包工具

简单流程图

BDB1A4CD-6D45-4470-BE5B-56F6A891BC69.png

ejApkRelease task

  1.初始化task

创建EjApkTask继承gradle api的DefaultTask来创建自己的task


public class EjApkTask extends DefaultTask{

    def android
    def buildConfigs = []

    EjApkTask(){
        description = 'Assemble ej APK'
        group = 'andChannelApk'
        outputs.upToDateWhen { false }
        android = project.extensions.android
        ...
    }

    @TaskAction
    run(){
        ...
    }

}

其中:
group表示在android studio 右侧的gradle任务列表为此task创建一个自己的目录:


DB39201E-B335-4473-AD38-FD531407425B.png

会自动将大写转为小写,写法参照AndResGuard原码写的

run方法表示此task运行的方法

创建,并初始化ejApkRelease task

        project.afterEvaluate {
            System.out.println("ejApkRelease is start")
            def taskName = "resguardRelease"
            def ejTask = "ejApkRelease"
            def task = project.task(ejTask, type: EjApkTask)
            //判断是否存在resguardRelease task
            if(project.tasks.findByPath(taskName) != null){
                System.out.println("ejApkRelease is exit")
                task.dependsOn "resguardRelease"
            } else {
                task.dependsOn "assembleRelease"
            }
        }

其中project.afterEvaluate闭包的调用位置实在 项目的settings.gradle构建之后,build.gradle构建之前。
task.dependsOn 表示将此任务task的调用位置放在resguardRelease或者assembleRelease task的后面

  2.task逻辑处理

通过api 获取app-release.apk文件位置,获取gradle配置的签名文件,获取渠道文件的路径配置:

        //渠道文件配置位置
        String channel = project.properties.get("channelFile")
        buildConfigs.each{ config ->
            if (config.file == null || !config.file.exists()) {
                System.out.println("ejApkRelease EjApkTask apk file not exit 1")
                return
            }
            //签名文件
            def signConfig = config.signConfig
            //app-release.apk位置
            String path = config.file.getAbsolutePath()
            System.out.println("path:"+path)
            InputParam.Builder builder = new InputParam.Builder()
                    .setChannel(channel)
                    .setInputFolder(useFolder(config.file))
                    .setApkPath(path)
                    .setSignFile(signConfig.storeFile)
                    .setKeypass(signConfig.keyPassword)
                    .setStorealias(signConfig.keyAlias)
                    .setStorepass(signConfig.storePassword)

            InputParam inputParam = builder.build()
            Main.gradleRun(inputParam)

        }

其中:
project.properties.get() 表示获取运行项目(app)目录下的gradle.properties文件里面的配置

解压,修改渠道文件,打包签名

  1.解压:

解压方式很简单,通过java自带api实现解压:

@SuppressWarnings("rawtypes")
    public static HashMap<String, Integer> unZipAPk(String fileName, String filePath) throws IOException {
        checkDirectory(filePath);
        ZipFile zipFile = new ZipFile(fileName);
        Enumeration emu = zipFile.entries();
        HashMap<String, Integer> compress = new HashMap<>();
        while (emu.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) emu.nextElement();
            if (entry.isDirectory()) {
                new File(filePath, entry.getName()).mkdirs();
                continue;
            }
            BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));

            File file = new File(filePath + File.separator + entry.getName());

            File parent = file.getParentFile();
            if (parent != null && (!parent.exists())) {
                parent.mkdirs();
            }
            //要用linux的斜杠
            String compatibaleresult = entry.getName();
            if (compatibaleresult.contains("\\")) {
                compatibaleresult = compatibaleresult.replace("\\", "/");
            }
            compress.put(compatibaleresult, entry.getMethod());
            FileOutputStream fos = new FileOutputStream(file);
            BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER);

            byte[] buf = new byte[BUFFER];
            int len;
            while ((len = bis.read(buf, 0, BUFFER)) != -1) {
                fos.write(buf, 0, len);
            }
            bos.flush();
            bos.close();
            bis.close();
        }
        zipFile.close();
        return compress;
    }
  1.修改渠道文件:

先删除解压包存在的签名文件,再寻找assets文件夹是否存在,存在直接修改渠道文件,不存在则添加assets文件夹并添加渠道文件,通过渠道文件获取要打的渠道,循环修改,并打包,签名:

public void buildApk() throws Exception {
        //删除签名文件
        File sinFile = new File(tempFile,"META-INF");
        if(sinFile.exists()){
            FileZipUtils.deleteDir(sinFile);
            System.out.println("删除原签名文件");
        }
        //存放签名包位置
        File fileassets = new File(tempFile,"assets");
        if(!fileassets.exists()){
            fileassets.mkdirs();
        }
        File fileEjChannel = new File(fileassets,"ej_channel");
        if(!fileEjChannel.exists()){
            fileEjChannel.createNewFile();
        }

        //打包apk位置
        File fileChannel = new File(fileApk,"channel");
        if(fileChannel.exists()){
            fileChannel.delete();
        }
        File unSinedFiles = new File(fileChannel,"unsign");
        File sinedFiles = new File(fileChannel,"sign");
        unSinedFiles.mkdirs();
        sinedFiles.mkdirs();
        File fileChannelTxt = new File(inputParam.channel);
        if(!fileChannelTxt.exists()){
            System.out.println("channel.txt not exit");
            return;
        }
        //获取所有渠道
        InputStream in = new FileInputStream(fileChannelTxt);
        int size = in.available();
        byte[] buffer = new byte[size];
        in.read(buffer);
        in.close();
        String allChannel = new String(buffer, "utf-8");
        //获取所有渠道数组
        String[] channels = allChannel.split(",");
        //循环渠道打包
        for(String content : channels){
            {
                OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(fileEjChannel),"UTF-8");
                System.out.println("channel content:"+content);
                //渠道名称加密
                String channel = ChannelEncode.encode(content);
                //不加密
//                String channel = content;
                System.out.println("channel:"+channel);
                osw.write(channel, 0, channel.length());
                osw.flush();
                osw.close();
                //压缩文件
                File outApkUnsin = new File(unSinedFiles,"release-"+content+"-unsin.apk");

                FileZipUtils.compress(tempFile,outApkUnsin);
                //签名
                File outApkSign = new File(sinedFiles,"release-"+content+"-sin.apk");
                signWithV1sign(outApkUnsin,outApkSign);
            }
        }

    }

使用方法

最后讲一下使用方式,由于本插件没有上传jcenter,所以需要下载下来编译一下,编译方式:


DCCFAA05-568A-45BB-B2AF-FD2CBB3BD15F.png

通过双击图中的uploadArchives 任务,会在项目的根目录创建本地maven仓库repo


1A8A8694-E18D-4774-9288-FADB8691311D.png

再在项目中引入此插件即可
[图片上传中...(55A09055-5C41-4965-9EC3-5684EC28F106.png-8fe92c-1513309013241-0)]

55A09055-5C41-4965-9EC3-5684EC28F106.png

这是编译通过之后会在右侧出现:


DB639938-DBCB-4177-A6F0-5670A9894528.png

任务,点击运行此任务即可打出渠道包

项目地址https://github.com/dengzhi00/EjApkChannelPlugin
觉得还行就看看吧

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,567评论 25 707
  • 这一章主要针对项目中可以用到的一些实用功能来介绍Android Gradle,比如如何隐藏我们的证书文件,降低风险...
    acc8226阅读 7,482评论 3 25
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,358评论 6 343
  • 一、作业内容:习题13-17解包、参数、文件读写习题13.使用import语句用于导入其他模块代码,被引用模块中的...
    小丰丰_72a2阅读 353评论 0 0