ASP源码.NET源码PHP源码JSP源码JAVA源码DELPHI源码PB源码VC源码VB源码Android源码

apktool编译和反编译apk与ecplise多渠道打包(2/3)

来源:网络整理     时间:2016-06-17     关键词:

本篇文章主要介绍了" apktool编译和反编译apk与ecplise多渠道打包",主要涉及到方面的内容,对于Javajrs看球网直播吧_低调看直播体育app软件下载_低调看体育直播感兴趣的同学可以参考一下: apktool 编译与反编译apk与Eclipse 多渠道打包想自己做个apk,还在为素材而苦恼吗?看到优秀的apk设计,还在为怎么看到别人的实现代码而苦恼吗?...

jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore 签名文件 -storepass 仓库密码 -keypass 口令密码 -signedjar 签名后的文件 待签名的文件 口令名

开始操作:

最好将签名文件也放到当前目录:

jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore alex.keystore -storepass 123456 -keypass 111111 -signedjar test_signer.apk E:\apk_build\test\dist\test.apk test

这一段很长,就不贴全了,贴个最后签名好的结果
 apktool编译和反编译apk与ecplise多渠道打包

Eclipse 实现多渠道打包

前面铺垫了这么多,终于开始搞大头了。

先说一下步骤:

  • 定义txt文件,保存不同的渠道信息。
  • 程序读取渠道信息。
  • 对apk 反编译 。
  • 替换清单文件中关于渠道包的关键字段。
  • 重新编译打包。
  • 签名。

是不是一气呵成。下面开始搞,将以友盟多渠道打包举例(流程过程中只贴部分代码,最后的工具类会贴到博客最后):

先看一下初始的文件目录,稍后会将该工程共享到github。

 apktool编译和反编译apk与ecplise多渠道打包

  • 定义txt 文件,保存不同的渠道信息,名字不要改,代码中使用的就是这个名字,改了会出问题。
baidu
yingyongbao
wandoujia

注意: 每一个渠道单独一行。

  • 程序读取渠道信息
/**
     * 获取渠道字段
     */privatevoidgetCannelFile() {
        File file = new File("channel.txt");
        // 如果文件不存在,则提示if (file.exists() && file.isFile()) {
            BufferedReader br = null;
            FileReader fr = null;
            try {

                // 获取到渠道的输入流
                br = new BufferedReader(new FileReader(file));

                String line = null;

                while ((line = br.readLine()) != null) {
                    // 获取到渠道
                    channelList.add(line.trim());
                }

                System.out.println(channelList);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fr != null) {
                        fr.close();
                    }
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("*********获取渠道成功 ***********");
        } else {
            System.err
                    .println("*********error: channel.txt文件不存在,请添加渠道文件***********");
        }
    }

获取到的渠道保存到一个集合中存储,后面用。

  • 对apk 反编译

使用apk 反编译,需要用到控制台命令,所以在此编译了一个类,用以调用控制台。

/**
     * 执行控制台指令
     * 
     * @param cmd
     */publicvoidrunCmd(String cmd) {
        Runtime rt = Runtime.getRuntime();
        BufferedReader br = null;
        InputStreamReader isr = null;
        try {
            // 执行
            Process p = rt.exec(cmd);
            // 获取对应流,一遍打印控制台输出的信息
            isr = new InputStreamReader(p.getInputStream());
            br = new BufferedReader(isr);
            String msg = null;
            while ((msg = br.readLine()) != null) {
                System.out.println(msg);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (isr != null) {
                    isr.close();
                }
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

指令很简单,就和我们之前编译的apk 几乎相似

// 1, 将该App 反编译
        String cmdUnpack = "cmd.exe /C java -jar apktool.jar d -f -s "
                + apkName;
        runCmd(cmdUnpack);

apkName是待编译的apk。

  • 替换清单文件,该过程稍微复杂。因为我们需要替换多个清单文件,所以需要保持一份最初的已做替换
// 2, 移动清单文件,作为备份// 获取编译后后的目录名 和目录文件
        String decodeDir = apkName.split(".apk")[0];
        File decodeDirFile = new File(decodeDir);

        // 获取清单文件
        String maniPath = decodeDirFile.getAbsolutePath()
                + "\\AndroidManifest.xml";

        // 获取备份清单文件目录 工程根目录
        String maniPathSave = basePath + "\\AndroidManifest_back.xml";

        // 备份清单文件new File(maniPath).renameTo(new File(maniPathSave));
        System.out.println("*********备份清单文件 ***********");
  • 备份完清单文件之后,就开始对每一个渠道执行 替换清单文件 -> 重新编译打包 -> 重新签名。

替换清单文件,在此单独写了一个方法

/**
     * 修改渠道值
     * 
     * @param sourcePath
     *            备份清单文件地址
     * @param targetPath
     *            目标清单文件地址
     * @param channelStr
     *            要求该的渠道值
     */publicvoidupdateChannel(String sourcePath, String targetPath,
            String channelStr) {

        BufferedReader br = null;
        FileReader fr = null;
        FileWriter fw = null;
        try {
            // 从备份中读取内容
            fr = new FileReader(sourcePath);
            br = new BufferedReader(fr);
            String line = null;
            StringBuffer sb = new StringBuffer();
            while ((line = br.readLine()) != null) {
                // 如果某一行存在qwertyy 则替换该值if (line.contains("qwertyy")) {
                    line = line.replaceAll("qwertyy", channelStr);
                }
                sb.append(line + "\n");
            }
            // 写到目标清单文件
            fw = new FileWriter(targetPath);
            fw.write(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fr != null) {
                    fr.close();
                }
                if (br != null) {
                    br.close();
                }
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

说一下思路: 先从备份的清单文件中将数据按行读取出来,如果某一行定义了我们的标示,则替换标示为我们的指定渠道值,这样我们就获取到了一个即将打包的清单文件字符串。将其写入到需要打包的目录中。

在此定义的标示为qwertyy,应该不会有重复值,如果有,可以自己改,但此值要与清单文件中的对应,例如我测试包中的清单文件的标示为:

 "UMENG_CHANNEL" android:value="qwertyy"/>
  • 开始打包
// 重新打包
            String cmdPack = String.format(
                    "cmd.exe /C java -jar apktool.jar b %s %s", decodeDir,
                    apkName);

            runCmd(cmdPack);
  • 重新签名
// 签名文件地址
            String keyStorePath = basePath + "\\" + keystoreName;

            // 未签名的apk 地址
            String unsign_apk_path = decodeDir + "\\dist\\" + apkName;

            // 签名后的apk
            String sign_apk_path = basePath + "\\" + channelList.get(i)
                    + ".apk";

            String signCmd = jarsignerPath
                    + " -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore "
                    + keyStorePath + " -storepass " + storepass + " -keypass "
                    + keypass + " -signedjar " + sign_apk_path + " "
                    + unsign_apk_path + " " + keyName;

            runCmd(signCmd);

signCmd有一点特殊,应为我无法再代码中获取到jarsigner命令,所以走了一个迂回的方式。

jarsignerPath的值为:C:\\Program Files\\Java\\jdk1.8.0_91\\bin\\jarsigner.exe,其实就是我本地jdk中该程序的地址。

最后看一下整体的类:

publicclassSplitApk {
    HashMap qudao = new HashMap();// 渠道号,渠道名    ArrayList channelList = new ArrayList<>();

    String basePath;// 当前文件夹路径// apk 名private String apkName;

    // 秘钥文件名private String keystoreName ;

    //仓库密码private String storepass ;

    // 口令密码private String keypass ;

    // 口令名private String keyName ;

    // 签名工具地址(全路径)private String jarsignerPath ;

    publicSplitApk(String apkName, String keystoreName, String storepass,
            String keyName, String keypass, String jarsignerPath) {
        this.apkName = apkName;
        this.keystoreName = keystoreName;
        this.storepass = storepass;
        this.keypass = keypass;
        this.keyName = keyName;
        this.jarsignerPath = jarsignerPath;

        this.basePath = new File("").getAbsolutePath();
    }

    publicvoidmySplit() {
        getCannelFile();// 获得自定义的渠道号        modifyChannel(); // 开始打包    }

    /**
     * 修改渠道字段
     */publicvoidmodifyChannel() {

        // 1, 将该App 反编译
        String cmdUnpack = "cmd.exe /C java -jar apktool.jar d -f -s "
                + apkName;
        runCmd(cmdUnpack);

        System.out.println("*********反编译Apk 成功 ***********");

        // 2, 移动清单文件,作为备份// 获取编译后后的目录名 和目录文件
        String decodeDir = apkName.split(".apk")[0];

        // 
        File decodeDirFile = new File(decodeDir);

        // 获取清单文件
        String maniPath = decodeDirFile.getAbsolutePath()
                + "\\AndroidManifest.xml";

        // 获取备份清单文件目录 工程根目录
        String maniPathSave = basePath + "\\AndroidManifest_back.xml";

        // 备份清单文件new File(maniPath).renameTo(new File(maniPathSave));
        System.out.println("*********备份清单文件 ***********");

        for (int i = 0; i < channelList.size(); i++) {

            System.out.println("*********开始搞----"+channelList.get(i)+" ***********");
            // 获取备份文件的内容,修改渠道值,并保存到maniPath 中
            updateChannel(maniPathSave, maniPath, channelList.get(i));
            System.out.println("*********修改清单文件,替换清单文件成功 ***********");

            // 重新打包
            String cmdPack = String.format(
                    "cmd.exe /C java -jar apktool.jar b %s %s", decodeDir,
                    apkName);

            runCmd(cmdPack);

            System.out.println("*********4,打包成功,开始重新签名 ***********");

            // 签名文件地址
            String keyStorePath = basePath + "\\" + keystoreName;

            // 未签名的apk 地址
            String unsign_apk_path = decodeDir + "\\dist\\" + apkName;

            // 签名后的apk
            String sign_apk_path = basePath + "\\" + channelList.get(i)
                    + ".apk";

            String signCmd = jarsignerPath
                    + " -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore "
                    + keyStorePath + " -storepass " + storepass + " -keypass "
                    + keypass + " -signedjar " + sign_apk_path + " "
                    + unsign_apk_path + " " + keyName;

            runCmd(signCmd);

            System.out.println("*********5," + channelList.get(i)
                    + "签名成功***********");
        }
    }

    /**
     * 修改渠道值
     * 
     * @param sourcePath
     *            备份清单文件地址
     * @param targetPath
     *            目标清单文件地址
     * @param channelStr
     *            要求该的渠道值
     */publicvoidupdateChannel(String sourcePath, String targetPath,
            String channelStr) {

        BufferedReader br = null;
        FileReader fr = null;
        FileWriter fw = null;
        try {
            // 从备份中读取内容
            fr = new FileReader(sourcePath);
            br = new BufferedReader(fr);
            String line = null;
            StringBuffer sb = new StringBuffer();
            while ((line = br.readLine()) != null) {
                // 如果某一行存在qwertyy 则替换该值if (line.contains("qwertyy")) {
                    line = line.replaceAll("qwertyy", channelStr);
                }
                sb.append(line + "\n");
            }
            // 写到目标清单文件
            fw = new FileWriter(targetPath);
            fw.write(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (fr != null) {
                    fr.close();
                }
                if (br != null) {
                    br.close();
                }
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 获取渠道字段
     */privatevoidgetCannelFile() {
        File file = new File("channel.txt");
        // 如果文件不存在,则提示if (file.exists() && file.isFile()) {
            BufferedReader br = null;
            FileReader fr = null;
            try {

                // 获取到渠道的输入流
                br = new BufferedReader(new FileReader(file));

                String line = null;

                while ((line = br.readLine()) != null) {
                    // 获取到渠道
                    channelList.add(line.trim());
                }

                System.out.println(channelList);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (fr != null) {
                        fr.close();
                    }
                    if (br != null) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("*********获取渠道成功 ***********");
        } else {
            System.err
                    .println("*********error: channel.txt文件不存在,请添加渠道文件***********");
        }
    }

    /**
     * 执行控制台指令
     * 
     * @param cmd
     */publicvoidrunCmd(String cmd) {
        Runtime rt = Runtime.getRuntime();
        BufferedReader br = null;
        InputStreamReader isr = null;
        try {
            // 执行
            Process p = rt.exec(cmd);
            // 获取对应流,一遍打印控制台输出的信息
            isr = new InputStreamReader(p.getInputStream());
            br = new BufferedReader(isr);
            String msg = null;
            while ((msg = br.readLine()) != null) {
                System.out.println(msg);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (isr != null) {
                    isr.close();
                }
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

工具类编写好了,就看怎么使用了

相关图片

相关文章