1. 程式人生 > >android外掛自定義之多渠道打包外掛(支援微信資源混淆andResGuard)

android外掛自定義之多渠道打包外掛(支援微信資源混淆andResGuard)

前言

  自定義android studio外掛?想想就覺得是一件令人興奮的事。最近閒來無事決定完善之前的一些程式碼操作,然後就想到了之前的apk多渠道打包工具,覺得還是太麻煩,何不用外掛的形式引入工程自動打包呢。說做就做…

資料整理

簡單流程圖

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
任務,點選執行此任務即可打出渠道包