一、目标

一、目标

本文是这次AndroidNativeEmu实操教程最精华的部分,这要从上次文章的call_object_method报错说起。

call_object_method主要有两个参数obj_idx和method_id,也就是对象id和方法id。顾名思义就是操作这个对象的方法。这个没毛病呀,我们先把报错的obj_idx和method_id打印出来:

JNIEnv->call_object_method(4096,3523215368) was called

4096 是谁? 3523215368 又是何许人也? 我们先从FindClass和GetMethodId开始

logger.debug("JNIEnv->FindClass(%s) was called, rc = %d" % (name,rc))
logger.debug("JNIEnv->GetMethodId(%d, %s, %s) was called,jvm_id = %d" % (clazz_idx, name, sig,method.jvm_id))

运行一下看结果

JNIEnv->FindClass(android/app/Activity) was called, rc = 3
JNIEnv->GetMethodId(3, getPackageManager, ()Landroid/content/pm/PackageManager;) was called,jvm_id = 3523215368

从打印结果来看,3523215368是android/app/Activity类的getPackageManager方法,这没毛病呀,我们上篇教程已经在Activity类里面增加了getPackageManager方法,不应该有问题的。

那现在来看看这个4096又是什么鬼?

在call_xxx_method中打印

logger.debug(type(pyobj))

运行一下看结果

<class '__main__.Application'>

what the fu**!!! 这段代码解释起来就是,调用Application类中的 Activity类的getPackageManager方法, 这是什么骚操作,首先Application类中目前我们并没有getPackageManager方法,其次怎么能从A类对象中调用B类的方法呢???

只有一种解释,Application和Activity类中都有getPackageManager方法,而且功能都是一样的,所以为了优雅,伟大的google工程师就把这两个jmethodID的地址设置成一样的,这样造成了Application类的对象可以调用Activity类的getPackageManager。

这个猜测是不对的,jmethodID相同的真实原因是 : Application 和 Activity 都是 ContextWrapper 的子类, getPackageManager 是 ContextWrapper 里的方法,获取的两个 jmethodid 自然是一样的。 感谢@葫芦娃

lei
1:lei

赶紧我们证明一下:

midsame
1:midsame

好吧,这不是玩死我们嘛,在我们AndroidNativeEmu里每个方法都有唯一的jmethodID,自然不存在jmethodID相同的情况,遇到这种骚操作只好崩溃以对了。

找到问题了就好解决了,我们在java_method_def方法定义的时候来增加一个jvm_id参数,并且把他们设置成一样的值

class Application(metaclass=JavaClassDef, jvm_name='android/app/Application'):
    def __init__(self):
        pass

    @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False,jvm_id=0xd2000000+0x1000)
    def getPackageManager(self, mu):
        logger.info("Im in Application.getPackageManager")
        pass

class Activity(metaclass=JavaClassDef, jvm_name='android/app/Activity'):
    def __init__(self):
        pass

    @java_method_def(name='getPackageManager', signature='()Landroid/content/pm/PackageManager;' , native=False,jvm_id=0xd2000000+0x1000)
    def getPackageManager(self, mu):
        logger.info("Im in Activity.getPackageManager")
        pass

# androidemu/java/java_method_def.py
class JavaMethodDef:

    def __init__(self, func_name, func, name, signature, native, args_list=None, modifier=None, ignore=None,jvm_id = None):

        if jvm_id == None:
            self.jvm_id = next_method_id()
        else:
            self.jvm_id = jvm_id
        logger.debug("JavaMethodDef name =%s,jvm_id = %s" % (name,self.jvm_id))

赶紧跑一下

 androidemu.java.jni_env | JNIEnv->call_object_method(4096,3523219456) was called
2020-11-25 20:42:03,974   DEBUG            androidemu.java.jni_env | <class '__main__.Application'>
2020-11-25 20:42:03,974   DEBUG            androidemu.java.jni_env | JNIEnv->CallXXXMethodX(android/app/Application, getPackageManager <()Landroid/content/pm/PackageManager;>, None) was called
2020-11-25 20:42:03,974    INFO                           __main__ | Im in Application.getPackageManager

成功的跑到了 Application.getPackageManager \(T∇T)/

100

关注微信公众号,最新技术干货实时推送

100