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脚本解决一种问题的多种方法》有分析。