Android是基于Linux内核的操作系统,用Java写的应用程序被Android运行时虚拟机运行。
因为Android是基于Linux的,而Linux执行ELF格式的可执行文件,所以用C++编写的ELF格式的可执行文件也可以在Android运行,但有些限制。首先,Android /sdcard目录不能给文件设置+x可执行位,而把文件复制到其他文件夹需要root权限。其次,把一般Linux里的ELF可执行文件复制到Android,是不能运行的,因为系统架构等不匹配。但是,从Android Open Source Project(AOSP)源代码里可以验证,Android操作系统里的许许多多的功能都是C++写的ELF格式的可执行程序,只要在AOSP里恰当地写一个C++程序,恰当地编译,是可以在Android运行的。
因为Android运行时(Android Runtime,ART)是一个Java虚拟机,所以Android可以运行Java程序。Java程序包括带有main函数的程序(我把它称为纯Java程序)和Android Studio生成的所谓的应用。
可以想到,Android运行时一定是个C++程序。要运行纯Java程序的代码类似art hello.class。
在Android系统中,应用程序是由Launcher启动起来的,其实,Launcher本身也是一个应用程序,其它的应用程序安装后,就会Launcher的界面上出现一个相应的图标,点击这个图标时,Launcher就会对应的应用程序启动起来。[1]罗升阳. Android应用程序启动过程源代码分析. . 2011-08-19 [2019-05-20].
通过学习Android OS编译流程可以知道,ART会被编译为两份,一份是host,一份是target。target指手机上运行的ART。host版本把Android预装的应用编译为ELF二进制文件(?)。既然有host ART,下文介绍的用命令行启动纯Java程序可以在电脑上运行。
运行方式为
out/host/linux-x86/bin/art --32 -cp ~/Project/Java/2/Hello.dex
-EnableRWProfiling:true -EnableHeapSizeProfiling:false Hello
[2]out/host/linux-x86/bin/art的源文件是art/tools/art。
用命令行启动纯Java程序
dalvikvm是Android 4.4以前就存在的命令,在4.4以后其内部调用art。
调用方式如下
dalvikvm -cp /sdcard/Hello.dex -EnableRWProfiling:true -EnableHeapSizeProfiling:true Hello
-EnableRWProfiling:true -EnableHeapSizeProfiling:true是我自己定义的两个art虚拟机选项。
app_process是Android 4.4加入的命令,在内部调用art。app_process的源代码在frameworks/base/cmds/app_process/app_main.cpp,文件开头有注释
/*
* Main entry of app process.
*
* Starts the interpreted runtime, then starts up the application.
*
*/
这样看来用app_process启动程序,一定会用解释方式执行,不会进行AOT静态编译,把代码编译成本地代码。我猜JIT即时编译仍然可能发生。
app_process的调用方式如下
app_process -Djava.class.path=Hello.dex -EnableRWProfiling:true -EnableHeapSizeProfiling:true /sdcard/ Hello
app_process是Android上所有Java应用进程的起源。init.zygote32.rc在系统启动阶段启动app_process,并把它命名为zygote。[4]罗升阳. Android系统进程Zygote启动过程的源代码分析. . 2011-09-19 [2019-06-02].
用命令行启动应用
用命令行启动应用需要指定应用的包名和活动名。
adb shell cmd package list packages可以列出所有包名:
...
package:com.android.managedprovisioning
package:com.android.dreams.phototable
package:com.facebook.katana
package:com.android.smspush
...
可见facebook的包名是com.facebook.katana。
注,Android 7.0以前需要使用命令pm list packages。
应用的AndroidManifest.xml文件写着默认活动名,就是在启动器上点击应用图标,启动的活动。但是apk是压缩文件,但直接解压缩的话,AndroidManifest.xml是乱码的。我们要用apktool解压缩apk文件。Android SDK里面没有这样的工具。
注意AndroidManifest.xml属于资源文件,运行apktool的时候不能加选项--no-res。如果apktool报错“Exception in thread “main” brut.androlib.AndrolibException: unsupported res type name for bags. Found: style2”,参见https://github.com/iBotPeaches/Apktool/issues/1719 ,可以用thejunkjon的fork。
在解压后的AndroidManifest.xml里面搜索“android.intent.action.MAIN”,会找到
说明com.facebook.katana.LoginActivity是默认activity。有的程序会把这个值写成”.LoginActivity“,前面的包名可以省略。[6]ccpat. Android应用的默认Activity配置. . 2017-01-24 [2019-05-20].
知道了包名和活动名,就可以启动应用了。
am start -n com.facebook.katana/.LoginActivity
调用cat /system/bin/am,可以发现am是一个Bash脚本。
#!/system/bin/sh
if [ "$1" != "instrument" ] ; then
cmd activity "$@"
else
base=/system
export CLASSPATH=$base/framework/am.jar
exec app_process $base/bin com.android.commands.am.Am "$@"
fi
所以am其实调用app_process。这就表示如果要传入虚拟机参数,不能使用am命令。所以在Android用命令行启动应用的方法是
CLASSPATH=/system/framework/am.jar app_process -EnableRWProfiling:true -EnableHeapSizeProfiling:true /system/bin com.android.commands.am.Am start -S -n com.facebook.katana/.LoginActivity
[7]变量赋值和命令写在同一行的语法合法性在《Bash脚本解决一种问题的多种方法》有分析。
[+]
罗升阳. Android应用程序启动过程源代码分析. . 2011-08-19 [2019-05-20].
out/host/linux-x86/bin/art的源文件是art/tools/art。
罗升阳. Android系统进程Zygote启动过程的源代码分析. . 2011-09-19 [2019-06-02].
ccpat. Android应用的默认Activity配置. . 2017-01-24 [2019-05-20].
变量赋值和命令写在同一行的语法合法性在《Bash脚本解决一种问题的多种方法》有分析。