Android 通过bin二进制程序调用jar原理

最近在研究monkey测试,发现monkey测试的代码都是JAVA编写的,通过编译生成jar包,而我们在执行测试时直接执行/system/bin/monkey这个二进制程序的,那么它是如何能调起java程序的呢?

先来看看monkey二进程程序的生成.

java 复制代码
development\cmds\monkey\Android.mk
LOCAL_PATH:= $(call my-dir)
 
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := monkeylib
LOCAL_MODULE_STEM := monkey
include $(BUILD_JAVA_LIBRARY)
 
################################################################
include $(CLEAR_VARS)
LOCAL_MODULE := monkey
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := monkey
LOCAL_REQUIRED_MODULES := monkeylib
include $(BUILD_PREBUILT)

Android.mk分了两部分,第一部分是编译生成monkey.jar,第二部分是把monkey这个文件编译生成monkey的二进制程序,看看monkey这个文件。

java 复制代码
development\cmds\monkey\monkey
base=/system
export CLASSPATH=$base/framework/monkey.jar
.........
exec app_process $base/bin com.android.commands.monkey.Monkey "$@"

monkey这是一个shell文件,先设置把monkey.jar添加环境变量中,然后执行app_process这个二进制程序,同时传入monkey jar的路径和类名。

app_process是在frameworks\base\cmds\路径下,直接看它的main函数

java 复制代码
frameworks\base\cmds\app_process\app_main.cpp
int main(int argc, char* const argv[])
{
    .........
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));//创建AndroidRuntime
   ..............
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {//monkey测试时,传入的是monkey jar的路径和包名,所以进入这个分支
            className.setTo(arg);//className包含com.android.commands.monkey.Monkey
            break;
        } else {
            --i;
            break;
        }
    }
 
    Vector<String8> args;
    if (!className.isEmpty()) {
        ...............
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);//com.android.commands.monkey.Monkey添加到AndroidRuntime中
        .............
    } else {
       ............
 
    }
    。。。。。。。。。。。。。
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {//className为com.android.commands.monkey.Monkey
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

app_process.cpp的main方法先解析argv参数,创建AndroidRuntime的实例,并传入类名,最后调用AndroidRuntime.start

java 复制代码
framework/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ...........
    //当前环境是否在ANDROID_ROOT、ANDROID_RUNTIME_ROOT、ANDROID_TZDATA_ROOT
    const char* rootDir = getenv("ANDROID_ROOT");
    ...............
    const char* runtimeRootDir = getenv("ANDROID_RUNTIME_ROOT");
    ..................
    const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");
    .............
 
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);//jni初始化
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {//启动java虚拟机
        return;
    }
    onVmCreated(env);//java虚拟机创建
 
    if (startReg(env) < 0) {//注册jni
        ALOGE("Unable to register all android natives\n");
        return;
    }
    .............................................................................
    //jnv的环境设置和类型转换
    .............................................................................
    char* slashClassName = toSlashClassName(className != NULL ? className : "");//className为com.android.internal.os.RuntimeInit
    jclass startClass = env->FindClass(slashClassName);//得到java类,startClass为com.android.internal.os.RuntimeInit
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");//通过类名获得类名里的main方法
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);//由C调用JAVA,反射调起com.android.internal.os.RuntimeInit的main方法,进入到java世界
 
        }
    }
    ...............
}

start方法里面首先判断当前环境是否在ANDROID_ROOT、ANDROID_RUNTIME_ROOT、ANDROID_TZDATA_ROOT,然后调用startVm,startVm主要是设置虚拟机的一些参数,onVmCreated是通过jni的环境检查类名是否存在,即检查com.android.internal.os.RuntimeInit是否存在,最后通过反射调起java的main方法。

我们继续看com.android.internal.os.RuntimeInit的main方法

java 复制代码
frameworks\base\core\java\com\android\internal\os\RuntimeInit.java
public static final void main(String[] argv) {
   .........
    commonInit();//做些log的打印和异常的捕获
    nativeFinishInit();//又进入C/C++世界
    ...............
}
java 复制代码
framework/base/core/jni/AndroidRuntime.cpp
static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onStarted();//mCurRuntime是App_main.cpp里的AppRuntime
}
java 复制代码
virtual void onStarted()
    {
       ...............
        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        ar->callMain(mClassName, mClass, mArgs);
        .....................
    }
java 复制代码
framework/base/core/jni/AndroidRuntime.cpp
status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
    const Vector<String8>& args)
{
    ................
    methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");//通过类名找到静态方法名为main的id
    if (methodId == NULL) {
        ALOGE("ERROR: could not find method %s.main(String[])\n", className.string());
        return UNKNOWN_ERROR;
    }
    
     //args类型转换
    const size_t numArgs = args.size();
    stringClass = env->FindClass("java/lang/String");
    strArray = env->NewObjectArray(numArgs, stringClass, NULL);
 
    for (size_t i = 0; i < numArgs; i++) {
        jstring argStr = env->NewStringUTF(args[i].string());
        env->SetObjectArrayElement(strArray, i, argStr);
    }
 
    env->CallStaticVoidMethod(clazz, methodId, strArray);//调起类名里的main静态方法,即com.android.commands.monkey.Monkey的main方法
    return NO_ERROR;
}

总结:通过命令执行monkey时,先设置monkey.jar的环境变量,然后执行app_process的程序,并传入monkey二进制程序的路径和monkey的类名,这时就进入到了app_process程序。在app_process的main方法里,创建AndroidRuntime的实例,对参数进行校验,把com.android.internal.os.RuntimeInit包名传给AndroidRuntime;在AndroidRuntime里做jni的初始化,设置虚拟机参数,注册jni,然后反射com.android.internal.os.RuntimeInit的main静态方法,最后在AndroidRuntime里通过反射调起类名里的main方法。

app_process程序还是非常不错的,利用好它,我们可以仿照monkey程序,开发出一些特殊的程序。

👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀

相关推荐
双桥wow31 分钟前
Android Framework开机动画开发
android
fanged7 小时前
天马G前端的使用
android·游戏
molong93111 小时前
Kotlin 内联函数、高阶函数、扩展函数
android·开发语言·kotlin
叶辞树13 小时前
Android framework调试和AMS等服务调试
android
慕伏白15 小时前
【慕伏白】Android Studio 无线调试配置
android·ide·android studio
低调小一15 小时前
Kuikly 小白拆解系列 · 第1篇|两棵树直调(Kotlin 构建与原生承载)
android·开发语言·kotlin
跟着珅聪学java15 小时前
spring boot 整合 activiti 教程
android·java·spring
川石课堂软件测试17 小时前
全链路Controller压测负载均衡
android·运维·开发语言·python·mysql·adb·负载均衡
2501_9159214317 小时前
iOS 26 电耗监测与优化,耗电问题实战 + 多工具 辅助策略
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_9159214318 小时前
苹果软件混淆与 iOS 应用加固白皮书,IPA 文件加密、反编译防护与无源码混淆方案全解析
android·ios·小程序·https·uni-app·iphone·webview