如何在手机上打包生成APK


by Amour.wang

通常我们都是在电脑上开发android应用,但是有些情况下不方便带电脑,又想临时修改一些参数重新生成apk,于是就发现了一个神器 AIDE(http://www.android-ide.com/)。有免费的版本基本够用了,还有高级版需要$.

然而本篇文章并不是要介绍AIDE,出于程序猿一贯的好奇心,于是决定研究一下这当中的奥秘。本篇文章主要就是介绍apk 打包的流程及如何在手机上生成一个apk 的过程。

 

一、apk 的打包过程分析

既然是要生成Apk 那就得来了解一下APK 的生成过程,以往我们都是在eclipse 中点一下run 然后就可以在bin 文件夹下找到xxx.apk. 再高级一点的使用ant 编译,或者谷歌家大力推崇的 android studio 中的gradle build,都大致是这样。

       但是没有过程啊,于是开始研究这个过程,基本可以分为如下几步


从这个图就可以很明确的看出蓝色的小方块就是我们需要的工具了,白色的小方块是输入,浅蓝色的小方块是中间产物。有了这个流程不管在什么平台上只要找到相应的工具都可以完成apk 的编译打包过程了,目前在 windows,OSX,linux 上网上都已经有很多人有分享了相关的资料的,这边我就不再重复了。下面重点来了,这个目前在网上很少有相关的资料,所以特别整理一下分享给大家。

一、在手机上完成APK 的编译打包。

按照上述的流程分为如下几个步骤,待我一一详细介绍

1.      aapt

google 官方有提供windows 下的aapt.exe 和 linux/mac 的aapt,可是这个aapt 是X86 架构的,在arm 下无法使用,于是想到可以自己编译一个arm 版的。

于是开始各种折腾,aapt 谷歌是有提供官方源码在frameworks/base/tools/aapt/下,又是各种折腾,最后还是直接用别人生成的的比较好用(以后有时间再折腾,还得看下aapt 的源码更深入了解apk 资源的编译原理,这个包含的内容较多就不在这篇详细介绍了)。

Aapt 的基本用法

Aapt  package

-f  overwrite file

-m  make package directoriesunder location specified by –J

[-S resource-sources [-S resource-sources ...]] \

-J  specify where to outputR.java resource constant definitions

-M  specify full path toAndroidManifest.xml to include in zip

-I  add an existing package tobase include set

-F  specify the apk file tooutput

在使用之前必须先把aapt 拷贝到自己应用的目录下,再chmod 为可执行,

String[] chmod = {"chmod", "744", aaptLoc.getAbsolutePath()};
Process chmodProcess = Runtime.getRuntime().exec(chmod);

 

private boolean runaapt(File aaptLoc, File androidJarLoc,String actName) {
    try {
       String[] args = {
               aaptLoc.getAbsolutePath(), //Thelocation of AAPT
               
"package", "-v", "-f", "-m",
                "-S", buildFolder.getAbsolutePath() + "/res/",                

 "-J", genFolder.getAbsolutePath(),
               
"-A", assetsFolder.getAbsolutePath(),              

 "-M", buildFolder.getAbsolutePath() + "/AndroidManifest.xml",               

"-I", androidJarLoc.getAbsolutePath(),

                "-F", binFolder.getAbsolutePath() + "/" + actName + ".apk.res"/

        };
        Process aaptProcess = Runtime.getRuntime().exec(args);
        int code =aaptProcess.waitFor();
        if (code!= 0){
            System.err.println("AAPTexited with error code " +code);
            copyStream(aaptProcess.getErrorStream(),System.out);
            return false;
        }
        return true;
    } catch (IOExceptione) {
       
System.out.println("AAPT failed");
        e.printStackTrace();
        return false;
    } catch (InterruptedException e) {
       
System.out.println("AAPT failed");
        e.printStackTrace();
        return false;
    }
}

 

2.      aidl

源码在frameworks/base/tools/aidl/,这个跟aapt 差不多,依葫芦画瓢,这边就不再详细描述。

 

3.      javac compiler

把java 变成class 文件,这个比较简单了,从SDK tools里面抠出来,直接就用了 ecj.jar

private boolean ecj(File androidJarLoc, String actName, String mainActivityLoc) {
    {
        System.out.println("Compiling with ECJ...");
        Main main = new Main(new PrintWriter(System.out), new PrintWriter(System.err), false, null, null);
        String[] args = {
                ("-verbose"), 
                "-extdirs", libsFolder.getAbsolutePath(),
                "-bootclasspath", androidJarLoc.getAbsolutePath(),
                "-classpath", srcFolder.getAbsolutePath()
                + ":" + genFolder.getAbsolutePath()  
                + ":" + libsFolder.getAbsolutePath(),
                "-1.6",
                "-target", "1.6", 
                "-proc:none", 
                "-d", binFolder.getAbsolutePath() + "/classes/", 
                srcFolder.getAbsolutePath() + "/" + mainActivityLoc + "/" + actName + ".java", 
        };
        System.out.println("Compiling: " + srcFolder.getAbsolutePath() + "/" + mainActivityLoc + "/" + actName + ".java");

        if (main.compile(args)) {
            System.out.println();
            return true;
        } else {
            System.out.println();
            System.out.println("Compilation with ECJ failed");
            return false;
        }
    }
}

 

       4.dex

这个也是一样从SDK buildtools 里面抠出来 dx.jar,这些个jar的用法参数都是参考各路大神,时间有限也没再多去研究

private boolean dexer(int cores) {
    try {
        System.out.println("Dexing with DX Dexer...");
        String[] args;
        args = new String[]{
                "--verbose",
                "--num-threads=" + cores,
                "--output=" + binFolder.getAbsolutePath() + "/classes.dex", // 
                binFolder.getAbsolutePath() + "/classes/" 
        };
         com.android.dx.command.dexer.Main.Arguments dexArgs = new com.android.dx.command.dexer.Main.Arguments();
        dexArgs.parse(args);

        int resultCode = com.android.dx.command.dexer.Main.run(dexArgs);

        if (resultCode != 0) {
            System.err.println("DX Dexer result code: " + resultCode);
            return false;
        }
        return true;
    } catch (Exception e) {
        System.out.println("DX Dexer failed");
        e.printStackTrace();
        return false;
    }
}

5.      apkbuilder

一样的套路在 SDK 的tools 目录下 sdklib.jar,执行完这步,就已经生成了一个未签名的APK

private boolean buildapk(String sketchName, boolean verbose) {
    try {
        System.out.println("Building APK file with APKBuilder...");

        com.android.sdklib.build.ApkBuilder builder = new com.android.sdklib.build.ApkBuilder(new File(binFolder.getAbsolutePath() + "/" + sketchName + ".apk.unsigned"),
                new File(binFolder.getAbsolutePath() + "/" + sketchName + ".apk.res"), 
                new File(binFolder.getAbsolutePath() + "/classes.dex"), 
                null, (verbose ? System.out : null)        );
        
        builder.addSourceFolder(srcFolder); 
        builder.sealApk();
        return true;
    } catch (Exception e) {
        e.printStackTrace();
        System.out.println("APKBuilder failed");
        return false;
    }
}

6.      sign

这个eclipse中是调用java 来对apk 进行签名的没有现成的jar包可以用。从谷歌家拿 https://code.google.com/archive/p/zip-signer/,好了一切顺利的话(通常这种情况发生的概率跟中500万差不多),你就可以看到你在手机上生成的apk了

 

private boolean signApk(String actName) {
    String mode = "testkey";
    String inFilename = binFolder.getAbsolutePath() + "/" + actName + ".apk.unsigned";
    String outFilename = binFolder.getAbsolutePath() + "/" + actName + ".apk";
    ZipSigner signer;
    try {
        signer = new ZipSigner();
        signer.setKeymode(mode);
        signer.signZip(inFilename, outFilename);
        return true;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return false;
}

 

二、小结

遗留的一些已知的问题:

1.      aidl 没有处理,这个应该跟aapt 类似,不过过程太折腾了,暂时没有时间弄

2.      多个dex 合并,这个部分没有处理,但是在SDK里面有看到对应的jar包,这部分应该还好

3.      JNI 编译,这个估计只有大神才可以做到

当中还有很多未知的问题,因个人精力所限,暂时也不能一一查明。

 

 

 

引用查询资料出处

http://www.cnblogs.com/royi123/p/3576746.html

http://1025250620.iteye.com/blog/1974214

https://code.google.com/archive/p/zip-signer/


  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值