美文网首页Android开发经验谈Android技术知识Android开发
android插件自定义之多渠道打包插件(支持微信资源混淆and

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

作者: 君莫醉 | 来源:发表于2017-12-15 12:13 被阅读944次

    前言

      自定义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
    觉得还行就看看吧

    相关文章

      网友评论

        本文标题:android插件自定义之多渠道打包插件(支持微信资源混淆and

        本文链接:https://www.haomeiwen.com/subject/vfsrwxtx.html