如何将已存在的APK项目作为资源引用
android开发中,调用外部引用的方法分为三种:
- 引入jar包文件
- 把项目整体作为依赖
- 通过maven或是gradle直接配置依赖
基本上,项目如果要开发更简洁,引入外部的依赖是不可避免的,这些引入的方法都很简单,不多加详述,现在我主要探究的是如何通过已有的apk拓展源,来制作可用的依赖。
关于这点有两点设想
- 反编译apk,修改清单文件,重新打包,将apk作为插件供新程序调用。
- 反编译apk,将反编译出的class文件打成jar包,供新程序调用,引入资源文件,修改映射关系
反编译,重打包
将apk反编译后会解出一个smali文件夹和res文件夹以及一个解码过的清单文件,如果单纯的解压apk,得到的则是dex文件和resources.arsc文件以及没有解码的res和清单文件,将反编译后的文件夹重新打包,会生成一个build文件夹,里面的内容和解压apk得到的文件类似。也就是说,其实反编译得到的代码是一个完整的开发工程,只是不是Java而已。
反编译 apktool d -f -.apk,会生成一个apk的同名文件夹
重打包 apktool b -f 反编译得到的文件夹,然后会在该文件夹下的dist目录中得到未签名的apk
这时,包是没有签名的,没有签名的apk是不能安装到手机上的,所以,先简单说一下签名
每个程序在安装前都需要签名,即是是调试的apk也不例外,发布到市场上的apk一般会单独生成一个keystore,而一般调试用的是系统里面的一个debug.keystore
keytool(jdk的bin目录下)是用来生成keystore的,eclipse和studio里面有图形化的生成方式其实也是调用的这个命令
keytool -genkeypair -alias - debug.keystore -keyalg RSA -validity 100
-keystore debug.keystore
命令说明如下:
-genkeypair :指定生成数字证书
-alias :指定生成数字证书的别名
-keyalg:指定生成数字证书的算法 这里如RSA算法
-validity:指定生成数字证书的有效期
-keystore :指定生成数字证书的存储路径。 (这里默认在keytool.exe 目录下)
jarsigner(jdk的bin目录下)命令是用来签名的,调试的时候就是用这个命令签入debug.keystore的
jarsigner -verbose -keystore debug.keystore -signedjar
dst.apk src.apk debug.keystore
以上命令的说明:
-verbose:指定生成详细输出
-keystore:指定数字证书存储路径
-signedjar:该选项的三个参数为 签名后的apk包 未签名的apk包 数字证书别名
我的debug.keystore在用户目录下的.android文件夹中
查看证书信息keytool -list -keystore debug.keystore
默认密码为:android
debug.keystore签名的别名为androiddebugkey
如果用debug.keystore来签名:
jarsigner -verbose -keystore C:\Users\用户.android\debug.keystore -signedjar dst.apk src.apk androiddebugkey
正在添加: META-INF/MANIFEST.MF
正在添加: META-INF/ANDROIDD.SF
正在添加: META-INF/ANDROIDD.RSA
正在签名: AndroidManifest.xml
正在签名: classes.dex
正在签名: res/anim/calendar_push_left_in.xml
正在签名: res/anim/calendar_push_left_out.xml
正在签名: res/anim/calendar_push_right_in.xml
正在签名: res/anim/calendar_push_right_out.xml
正在签名: res/anim/push_bottom_in.xml
正在签名: res/anim/push_bottom_in2.xml
。
。
利用这一流程,来做若干测试:
- 修改反编译后的清单文件(比如为activity添加入口,去掉入口,修改包名等等),打包,签名,安装运行程序,程序能如预期的修改效果。
- 修改反编译后的资源文件(如替换图片),程序也能如期产生对应效果。
-
修改smali
Dalvik字节码Dalvik是google专门为Android操作系统设计的一个虚拟机,经过深度的优化。虽然Android上的程序是使用java来开发的,但是Dalvik和标准的java虚拟机JVM是不同的。Dalvik VM是基于寄存器的,而JVM是基于栈的;Dalvik有专属的文件执行格式dex(dalvik executable),而JVM则执行的是java字节码。Dalvik VM比JVM速度更快,占用空间更少。打包成apk的过程就是将java编译成class文件,然后将class文件封装成dex打包入apk的过程。
通过这个方法总结出利用原始apk文件部分功能的方法
- 修改apk入口,将之作为插件依附在新建的apk上
- 新建项目写好需要添加的功能,然后编译成apk文件,反编译出源码,对照修改原来的smali文件和资源文件,重新打包签名
反编译,提取class文件制作jar包
之前反编译到的文件通过jd-gui读取时有很多乱序代码,所以直接抠出Java代码放在开发环境中修改将是一项相当耗时,而且需要懂其中的的业务逻辑,不然就就不可能还原其代码,那么就只能将它打成jar包在新工程中调用,如果是这样,那么就剩下了一个问题,就是如何修复class文件中的资源id和资源本身的映射关系。
通过实践可知,如果将一个android项目导出为jar包,除了不能包含清单文件和一系列配置文件外(因为配置文件外部也有,如果里面也存在就会造成冲突),资源文件并不能被正确找到。毕竟,资源文件的映射ID是随机生成的。
而Android系统将代码生成apk过程如下:
1、使用Android SDK提供的aapt.exe(build-tools目录)生成R.Java类文件
2、使用Android SDK提供的aidl.exe(build-tools目录)把.aidl转成.java文件
3、使用JDK提供的javac.exe(jdk的bin目录下)编译.java类文件生成class文件
4、使用Android SDK提供的dx.bat(build-tools目录)命令行脚本生成classes.dex文件
5、使用Android SDK提供的aapt.exe(build-tools目录)生成资源包文件(包括res、assets、androidmanifest.xml等)
6、使用Android SDK提供的apkbuilder.bat(老版本在sdk中tool目录下,新版是没有这个文件的)生成未签名的apk安装文件
7、使用jdk的jarsigner.exe(jdk的bin目录下)对未签名的包进行apk签名
8、安装使用的adb.exe在SDK的platform-tools目录下
android.jar里面是有资源文件的,但是通过尝试,发现引入的jar是和android.jar包不一样的,引入的jar包相当于和本地写的代码是一样的,不允许重复,清单文件资源文件还有resources.arsc文件,resources.arsc一开始我认为里面存放的应该是资源的映射关系,所以放在jar包中能有用,但是却会提示和本地生成的resources.arsc有冲突。那么就不能将这些文件一同写入jar包了,但是如果分开来src和jar包只放代码,res放资源,由于开发环境中R映射文件是动态生成的。而我现在操作的是R文件映射关系已经固定的项目,强行指定固然可以一试,但却不是好方法。
dex2jar classes.dex //dex转jar
jar xf classes_dex2jar.jar //解压jar
jar cvf ketroa.jar * //打jar包
于是我又想到了第二种导入依赖的方法,项目指定,在eclipse中如果一个项目把另一个项目作为依赖,那么这个项目引用的包就会指向被引用项目的bin文件中自动生成的jar包,这个bin文件中除了有jar包,还有一个res资源文件夹,一个清单文件,和一个R.txt文件,虽然小,但是所有需要的文件都有。R.txt格式文件如下:
int dimen activity_horizontal_margin 0x7f040000
int dimen activity_vertical_margin 0x7f040001
int drawable ic_launcher 0x7f020000
这种方式虽然可行,但是也需要熟知Android生成apk的过程,对已有打包脚本进行修改才行,虽然是从两种出发点考虑,但是却和上面的强行指定是同一种路子。
关于aapt,可以通过调出命令文档可知aapt d -param apk可以查看apk的各种信息。