如何使用非泛型参数调用方法(反射)?
我有一个小问题。 我正在开发一个 Android 应用程序。 在那里您可以从其他应用程序(包)动态加载类。 首先,我不想“破解”第三方应用程序,我想尝试为我自己的应用程序构建插件。 那我有什么?
2 个测试应用程序和 1 个库(包含在两个应用程序中)。
所以app1的代码:
package com.ftpsynctest.app1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import android.app.Activity;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import com.syncoorp.ftpsyncx.commons.SyncFile;
import dalvik.system.PathClassLoader;
public class App1Activity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SyncFile f = new SyncFile("bla");
String classname = "com.ftpsynctest.app2.classcall";
String classpath = getApk("com.ftpsynctest.app1") + ":" + getApk("com.ftpsynctest.app2");
PathClassLoader myClassLoader = new dalvik.system.PathClassLoader(classpath, ClassLoader.getSystemClassLoader());
try {
Class c = Class.forName(classname, true, myClassLoader);
for (Method m : c.getDeclaredMethods()) {
System.out.println("Method: " + m.getName());
for (Type t : m.getGenericParameterTypes()) {
System.out.println(" - type: " + t.toString());
}
m.invoke(c.newInstance(), new Object[] {
new com.syncoorp.ftpsyncx.commons.SyncFile("bla")
});
break;
}
}
catch (ClassNotFoundException e) {e.printStackTrace();}
catch (IllegalArgumentException e) {e.printStackTrace();}
catch (IllegalAccessException e) {e.printStackTrace();}
catch (InvocationTargetException e) {e.printStackTrace();}
catch (InstantiationException e) {e.printStackTrace();}
}
private String getApk(String packageName) {
try { return this.getPackageManager().getApplicationInfo(packageName, 0).sourceDir;}
catch (NameNotFoundException e) {e.printStackTrace();}
return "";
}
}
所以我想创建类com.ftpsynctest.app2.classcall并调用方法modify< /strong> ,参数类型为 com.syncoorp.ftpsyncx.commons.SyncFile。
我的 app2 代码:
package com.ftpsynctest.app2;
import com.syncoorp.ftpsyncx.commons.SyncFile;
public class classcall {
public SyncFile modify(SyncFile file) {
file.change_date = 123;
return file;
}
}
我首先安装了 app2 来提供类给 app1。 成功后我启动了app1。
我的输出:
01-10 22:21:48.804:INFO/System.out(4681):方法:修改
01-10 22:21:48.809: INFO/System.out(4681): - type: class com.syncoorp.ftpsyncx.commons.SyncFile
所以现在看起来不错。找到的方法的参数类型为com.syncoorp.ftpsyncx.commons.SyncFile 和我提供的是一样的。
但我收到以下错误:
java.lang.IllegalArgumentException: argument type mismatch
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.ftpsynctest.app1.App1Activity.onCreate(App1Activity.java:44)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1615)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
at android.app.ActivityThread.access$1500(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3691)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:907)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:665)
at dalvik.system.NativeStart.main(Native Method)
但是为什么呢?我的输出告诉我它是 SyncFile,我将 SyncFile 放入调用命令中。 那里出了什么问题? 编译 app2 是否会从 SyncFile 创建一个与编译后的 app1 不同的类?如果是,为什么? SyncFile 类是我的“commons”库中两个项目共享的相同物理类。
有人有解决方案或答案吗?
I have a little problem.
I am developing an Android applikation.
There you can dynamicly load classes from other applications (packages).
First of all, i do not want to "hack" an third-party app, i want to try to build up plugins for my own app.
So what do i have?
2 Test applications and 1 library which is in both apps included.
So the code for app1:
package com.ftpsynctest.app1;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import android.app.Activity;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import com.syncoorp.ftpsyncx.commons.SyncFile;
import dalvik.system.PathClassLoader;
public class App1Activity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SyncFile f = new SyncFile("bla");
String classname = "com.ftpsynctest.app2.classcall";
String classpath = getApk("com.ftpsynctest.app1") + ":" + getApk("com.ftpsynctest.app2");
PathClassLoader myClassLoader = new dalvik.system.PathClassLoader(classpath, ClassLoader.getSystemClassLoader());
try {
Class c = Class.forName(classname, true, myClassLoader);
for (Method m : c.getDeclaredMethods()) {
System.out.println("Method: " + m.getName());
for (Type t : m.getGenericParameterTypes()) {
System.out.println(" - type: " + t.toString());
}
m.invoke(c.newInstance(), new Object[] {
new com.syncoorp.ftpsyncx.commons.SyncFile("bla")
});
break;
}
}
catch (ClassNotFoundException e) {e.printStackTrace();}
catch (IllegalArgumentException e) {e.printStackTrace();}
catch (IllegalAccessException e) {e.printStackTrace();}
catch (InvocationTargetException e) {e.printStackTrace();}
catch (InstantiationException e) {e.printStackTrace();}
}
private String getApk(String packageName) {
try { return this.getPackageManager().getApplicationInfo(packageName, 0).sourceDir;}
catch (NameNotFoundException e) {e.printStackTrace();}
return "";
}
}
So i want to create the class com.ftpsynctest.app2.classcall and call the method modify with a parameter of type com.syncoorp.ftpsyncx.commons.SyncFile.
My app2 code:
package com.ftpsynctest.app2;
import com.syncoorp.ftpsyncx.commons.SyncFile;
public class classcall {
public SyncFile modify(SyncFile file) {
file.change_date = 123;
return file;
}
}
I first installed app2 to provide the class to app1.
After that succeed i started app1.
My Output:
01-10 22:21:48.804: INFO/System.out(4681): Method: modify
01-10 22:21:48.809: INFO/System.out(4681): - type: class com.syncoorp.ftpsyncx.commons.SyncFile
So for now it looks good. the parameter type of the found method is com.syncoorp.ftpsyncx.commons.SyncFile
and my provided one is the same.
But i get the following error:
java.lang.IllegalArgumentException: argument type mismatch
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.ftpsynctest.app1.App1Activity.onCreate(App1Activity.java:44)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1615)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
at android.app.ActivityThread.access$1500(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.app.ActivityThread.main(ActivityThread.java:3691)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:907)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:665)
at dalvik.system.NativeStart.main(Native Method)
But why? my output tells me that it is SyncFile and i put SyncFile to the invoke command.
Whats the problem there?
Can it be that compiling app2 creates a class from SyncFile which is different from the compiled app1 ? if yes, why ? the SyncFile class is the same physical class within my "commons" library which both projects share.
Anybody has a solution or answer?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在这种情况下,可能有两个名称相同的类
SyncFile
对不同的类加载器可见。即使类的名称完全相同,在同一个包中,即使具有相同的字节代码,虚拟机也会将它们视为不同的类,因为它们来自不同的位置(类加载器)。在运行时,类的标识由其包、名称和加载它的类加载器实例定义。预计每个类只能由一个类加载器找到/加载。如果不是这种情况,结果将根据访问类时有效的类加载器而有所不同。
new com.syncoorp.ftpsyncx.commons.SyncFile
可能会使用由应用程序的本地类加载器加载并与之关联的类,而被调用的方法需要与myClassLoader
关联的版本。由于两个类加载器都知道“相同”的类(由包+类名标识),但每个类加载器只知道其中的一个,从 JVM 的角度来看,它们是两个不同的类。您可以尝试通过
myClassloader
加载的SyncFile
类的反射来创建SyncFile
实例,即请注意,您将面临此问题在您的应用程序和“插件”交换它们都包含的任何类实例的任何地方,即无处不在的反射。考虑是否值得这么麻烦,或者您是否想采取更好的方法,例如 IPC。
There may be two identically named classes
SyncFile
visible to different classloaders in this case. Even if the classes are named exactly the same, in the same package, even with the same byte code they will be considered different classes by the VM because they come from different locations (classloaders).At runtime, the identity of a class is defined by its package, its name and the classloader instance that loaded it. It is expected that every class can only be found/loaded by exactly one classloader. If that is not the case, the result will vary based upon which classloader is in effect when the class is accessed.
new com.syncoorp.ftpsyncx.commons.SyncFile
will probably use the class loaded by and associated with the app's local classloader whereas the called method expects the version associated withmyClassLoader
. Since both classloaders know the 'same' class (identified by package+class name), but each one only knows one of them, from the JVM's perspective, they are two different classes.You could try and create your
SyncFile
instance via reflection from theSyncFile
class loaded bymyClassloader
, i.e.Note that you will face this problem everywhere where your app and a 'plugin' exchange instances of any class they both contain, i.e. reflection everywhere. Consider if it's worth the hassle or if you want to resort to some better way, e.g. IPC.
编辑:这个答案是不正确的。请参阅下面的反例。
您将
Method.invoke()
参数的格式设置错误。请参阅文档 此处。您只需传递多个参数即可调用,而不是传递所有参数的单个 Object[] 数组。这就是Object...args
表示法的含义:该方法接受任意数量的对象。对于您的示例,更改
为
应该可以解决问题。
反例:
Refl.java:
Refl2.java:
Edit: This answer is incorrect. See below for a counterexample.
You've formatted your argument to
Method.invoke()
wrong. See the documentation here. Instead of passing a single Object[] array of all arguments, you just pass multiple arguments to invoke. That's what theObject... args
notation means: the method accepts any number of Objects.For your example, changing
to
should fix the problem.
Counterexample:
Refl.java:
Refl2.java: