Android用命令行启动应用
2019年5月20日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]
运行方式为
out/host/linux-x86/bin/art --32 -cp ~/Project/Java/2/Hello.dex
-EnableRWProfiling:true -EnableHeapSizeProfiling:false Hello
用命令行启动纯Java程序
dalvikvm是Android 4.4以前就存在的命令,在4.4以后其内部调用art。[3]TODO:显示源代码
调用方式如下
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
用命令行启动应用
用命令行启动应用需要指定应用的包名和活动名。
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。
pm list packages
。[5]应用的AndroidManifest.xml文件写着默认活动名,就是在启动器上点击应用图标,启动的活动。但是apk是压缩文件,但直接解压缩的话,AndroidManifest.xml是乱码的。我们要用apktool解压缩apk文件。Android SDK里面没有这样的工具。
--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”,会找到
<activity android:name="com.facebook.katana.LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
说明com.facebook.katana.LoginActivity
是默认activity。有的程序会把这个值写成”.LoginActivity“,前面的包名可以省略。[6]
知道了包名和活动名,就可以启动应用了。
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
参考资料
- 罗升阳. Android应用程序启动过程源代码分析. . 2011-08-19 [2019-05-20].↑
- out/host/linux-x86/bin/art的源文件是art/tools/art。↑
- JesusFreke. How to execute the dex file in android with command?. . 2019-01-26 [2019-05-20].↑
- 罗升阳. Android系统进程Zygote启动过程的源代码分析. . 2011-09-19 [2019-06-02].↑
- Todd Kennedy. Implement shell commands for package and user services
. . 2015-10-30 [2019-05-20].↑ - ccpat. Android应用的默认Activity配置. . 2017-01-24 [2019-05-20].↑
- 变量赋值和命令写在同一行的语法合法性在《Bash脚本解决一种问题的多种方法》有分析。↑
有可能通过命令启动应用的时候,禁用 jit 嘛
就是达到清单文件加入 vmSafeMode=true 的效果
就是三方 app 无法修改清单文件,通过命令启动来实现禁用这个 app 使用 jit