一、目标
本文是这次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 自然是一样的。 感谢@葫芦娃
赶紧我们证明一下:
好吧,这不是玩死我们嘛,在我们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)/
关注微信公众号,最新技术干货实时推送