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老皮!!!欢迎大家来找我探讨交流👀

相关推荐
敲上瘾23 分钟前
MySQL主从集群解析:从原理到Docker实战部署
android·数据库·分布式·mysql·docker·数据库架构
Jomurphys35 分钟前
测试 - 单元测试(JUnit)
android·junit·单元测试
fatiaozhang95271 小时前
中国移动中兴云电脑W132D-RK3528-2+32G_安卓9_ADB开启线刷包
android·adb·电脑·电视盒子·刷机固件·机顶盒刷机·中兴云电脑w132d
selt79110 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
Yao_YongChao11 小时前
Android MVI处理副作用(Side Effect)
android·mvi·mvi副作用
非凡ghost12 小时前
JRiver Media Center(媒体管理软件)
android·学习·智能手机·媒体·软件需求
席卷全城12 小时前
Android 推箱子实现(引流文章)
android
齊家治國平天下12 小时前
Android 14 系统中 Tombstone 深度分析与解决指南
android·crash·系统服务·tombstone·android 14
maycho12314 小时前
MATLAB环境下基于双向长短时记忆网络的时间序列预测探索
android
思成不止于此14 小时前
【MySQL 零基础入门】MySQL 函数精讲(二):日期函数与流程控制函数篇
android·数据库·笔记·sql·学习·mysql