如果类的公共(public)函数更新了一些 java swing GUI 元素,我必须检查在哪个上下文中调用该函数并确保更新是在 EDT 中完成的。这就是我解决它的方法,但是由于可能有很多函数我必须执行相同的操作(但调用类的不同私有(private)方法)或其他类我必须执行相同的操作,所以我厌倦了总是编写相同的代码
public void updateGUI()
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
update();
}
});
}
else
{
update();
}
}
private void update()
{
//update the swing elements here
}
一种可能的解决方案可能是创建一个具有此更新方法的接口(interface)(名为 UpdateInterface)。该类现在将实现此接口(interface),并且我使用静态方法创建一个辅助类,该方法获取此接口(interface)的引用并在 EDT 上下文中调用更新方法:
public static void callUpdateInEDT(UpdateInterface f)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
f.update();
}
});
}
else
{
f.update();
}
}
但这不是很灵活,因为我可能有一些其他私有(private)方法,我应该在 EDT 上下文中调用它们。是否有任何我到目前为止还没有找到的 SwingUtilities 解决方案,或者是否有任何其他比我提出的更灵活的解决方案?
最佳答案
因此,经过多年编写更多包装器代码之后,我开始思考,在内部/匿名类出现之前,我编写了一个基于反射的 API,它基本上可以简化使用任意数量的参数调用任何方法的过程
所以,你的代码会变得更像......
SwingSafeMethodInvoker.invokeLater(this, "update").execute();
这只是一种可能的解决方案(而且我很懒,所以我喜欢减少必须(重新)编写的代码量)
代码足够智能,可以知道您是否处于 EDT 状态并采取适当的操作。最棒的是它允许我调用 protected 方法(请记住,在内部/匿名类之前,您必须创建一个独立的外部可运行类,因此它无法调用 protected
方法)
该 API 构建在其他反射实用程序之上,因此有点复杂。
警告此 API 使用反射,众所周知反射效率较低,因此您需要知道何时不使用它。它还“打破”了 protected
和 private
适用于类的访问限制,因此您可以做一些非常有趣的事情,其中大部分我会阻止您尝试/做.
这也不会拾取您可能对代码进行的任何重构,因此,如果您重命名一个方法,这将看不到它,请小心。
请记住:这是我编写的更大反射 API 的一部分,它允许我在类上执行方法,因此故意将其分解为多个类
SwingSafeMethodInvoker
public class SwingSafeMethodInvoker {
public static InvokeLater invokeLater(Object obj, String methodName) {
return new InvokeLater(obj, methodName);
}
public static InvokeLater invokeLater(Class clazz, String methodName) {
return new InvokeLater(clazz, methodName);
}
public static InvokeAndWait invokeAndWait(Object obj, String methodName) {
return new InvokeAndWait(obj, methodName);
}
public static InvokeAndWait invokeAndWait(Class clazz, String methodName) {
return new InvokeAndWait(clazz, methodName);
}
public static InvokeAfter invokeAfter(Object obj, String methodName) {
return new InvokeAfter(obj, methodName);
}
public static InvokeAfter invokeAfter(Class clazz, String methodName) {
return new InvokeAfter(clazz, methodName);
}
public static InvokeAfterAndWait invokeAfterAndWait(Object obj, String methodName) {
return new InvokeAfterAndWait(obj, methodName);
}
public static InvokeAfterAndWait invokeAfterAndWait(Class clazz, String methodName) {
return new InvokeAfterAndWait(clazz, methodName);
}
public static MethodInvoker invoke(Object obj, String methodName) {
return new MethodInvoker(obj, methodName);
}
public static MethodInvoker invoke(Class clazz, String methodName) {
return new MethodInvoker(clazz, methodName);
}
}
调用稍后
import java.awt.EventQueue;
import javax.swing.SwingUtilities;
/**
* This will make a synchronised call into the EventQueue.
*
* There is no way to determine when the actually call will be made. If you
* need to wait for the result of the call, you are better of using
* InvokeAndWait instead
*
* If the invoke method is called within the ETD, it will be executed
* immediately.
*
* If you want the call to occur later (ie have it placed at the end
* of the EventQueue, use InvokeAfter)
*
* The invoke method will always return null.
* @author shane
*/
public class InvokeLater extends AbstractSwingMethodInvoker {
public InvokeLater(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeLater(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeLater(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() throws SynchronisedDispatcherException {
if (EventQueue.isDispatchThread()) {
run();
} else {
SwingUtilities.invokeLater(this);
}
return null;
}
}
调用并等待
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
public class InvokeAndWait extends AbstractSwingMethodInvoker {
public InvokeAndWait(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeAndWait(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeAndWait(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() {
if (EventQueue.isDispatchThread()) {
run();
} else {
try {
SwingUtilities.invokeAndWait(this);
} catch (InterruptedException ex) {
throw new InvocationException("Failed to invokeAndWait", ex);
} catch (InvocationTargetException ex) {
throw new InvocationException("Failed to invokeAndWait", ex);
}
}
return getResult();
}
}
InvokeAfter
import javax.swing.SwingUtilities;
/**
* This will place the method call onto the end of the event dispatching
* queue and return immediately.
*
* There is no means to known when the call actually takes and place and if
* you are interested in the return result, you are better of using InvokeAndWait
* @author shane
*/
public class InvokeAfter extends InvokeLater {
public InvokeAfter(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public InvokeAfter(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public InvokeAfter(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public Object execute() throws SynchronisedDispatcherException {
SwingUtilities.invokeLater(this);
return null;
}
}
AbstractSwingMethodInvoker
import core.util.MethodInvoker;
public abstract class AbstractSwingMethodInvoker extends MethodInvoker implements Runnable {
private Object result;
public AbstractSwingMethodInvoker(Object parent, Class parentClass, String methodName) {
super(parent, parentClass, methodName);
}
public AbstractSwingMethodInvoker(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public AbstractSwingMethodInvoker(Class clazz, String methodName) {
this(null, clazz, methodName);
}
@Override
public void run() {
result = super.execute();
}
public Object getResult() {
return result;
}
public class SynchronisedDispatcherException extends Error {
public SynchronisedDispatcherException(String message) {
super(message);
}
public SynchronisedDispatcherException(String message, Throwable cause) {
super(message, cause);
}
}
}
方法调用器
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* Provides a means to invoke any method on any class/object using reflection
*
* @author Shane Whitegead
*/
public class MethodInvoker {
private Object parent;
private Class parentClass;
private String methodName;
private List<Parameter> lstParameters;
public MethodInvoker() {
}
public MethodInvoker(Object parent, Class parentClass, String methodName) {
this.parent = parent;
this.parentClass = parentClass;
this.methodName = methodName;
lstParameters = new ArrayList<Parameter>(5);
}
public MethodInvoker(Object parent, String methodName) {
this(parent, parent.getClass(), methodName);
}
public MethodInvoker(Class clazz, String methodName) {
this(null, clazz, methodName);
}
public static MethodInvoker invoke(Object parent, String methodName) {
return new MethodInvoker(parent, methodName);
}
public static MethodInvoker invoke(Class parent, String methodName) {
return new MethodInvoker(parent, methodName);
}
public static MethodInvoker invoke(Object parent, Class clazz, String methodName) {
return new MethodInvoker(parent, clazz, methodName);
}
public MethodInvoker setParent(Object parent) {
this.parent = parent;
if (parent != null) {
setParentClass(parentClass.getClass());
}
return this;
}
public MethodInvoker setParentClass(Class parent) {
this.parentClass = parent;
return this;
}
public MethodInvoker setMethodName(String method) {
this.methodName = method;
return this;
}
public <T> MethodInvoker with(Class<T> type, T value) {
with(new Parameter(value, type));
return this;
}
public MethodInvoker with(Class[] types, Object[] parameters) {
if (types == null || parameters == null) {
} else if (types.length != parameters.length) {
} else {
for (int index = 0; index < types.length; index++) {
with(types[index], parameters[index]);
}
}
return this;
}
public MethodInvoker with(Parameter parameter) {
lstParameters.add(parameter);
return this;
}
public Object getParent() {
return parent;
}
public Class getParentClass() {
return parentClass;
}
public String getMethodName() {
return methodName;
}
public Class[] getParameterTypes() {
List<Class> lstTypes = new ArrayList<Class>(lstParameters.size());
for (Parameter parameter : lstParameters) {
lstTypes.add(parameter.getType());
}
return lstTypes.toArray(new Class[lstTypes.size()]);
}
public Object[] getParameterValues() {
List<Object> lstTypes = new ArrayList<Object>(lstParameters.size());
for (Parameter parameter : lstParameters) {
lstTypes.add(parameter.getValue());
}
return lstTypes.toArray(new Object[lstTypes.size()]);
}
public Object execute() {
Object result = null;
Class type = getParentClass();
String methodName = getMethodName();
Class[] lstTypes = getParameterTypes();
Object[] lstValues = getParameterValues();
Object parent = getParent();
try {
Method method = findMethod(type, methodName, lstTypes);
if (method == null) {
throw new NoSuchMethodException(getMethodDescription(type, methodName, lstTypes));
}
method.setAccessible(true);
// logger.info("Method = " + method);
result = method.invoke(parent, lstValues);
} catch (Exception ex) {
StringBuilder sb = new StringBuilder(64);
sb.append("parent = ").append(parent).append("\n");
sb.append("type = ").append(type).append("\n");
sb.append("methodName = ").append(methodName).append("\n");
for (int index = 0; index < lstTypes.length; index++) {
sb.append("[").append(index).append("] ").append(lstTypes[index].getName()).append(lstValues[index]).append("\n");
}
System.err.println("Called by\n" + sb.toString());
throw new InvocationException("Failed to invoke " + methodName, ex);
}
return result;
}
public static Field findField(Class parent, String name) {
Field field = null;
try {
field = parent.getDeclaredField(name);
} catch (NoSuchFieldException noSuchFieldException) {
try {
field = parent.getField(name);
} catch (NoSuchFieldException nsf) {
if (parent.getSuperclass() != null) {
field = findField(parent.getSuperclass(), name);
}
}
}
if (field != null) {
field.setAccessible(true);
}
return field;
}
/**
* This method basically walks the class hierarchy looking for a matching
* method.
*
* The issue is getXMethod only returns a list of the methods for the current
* class and not the inherited methods. This method basically over comes that
* limitation.
*
* If no method can be found matching the criteria, then this method returns a
* null value.
*
* This makes this method not only very powerful, but also very dangerous, as
* it has the power of finding ANY declared method within the hierarchy,
* public, protected or private.
*
* @param parent
* @param name
* @param lstTypes
* @return
*/
public static Method findMethod(Class parent, String name, Class[] lstTypes) {
Method method = null;
try {
method = parent.getDeclaredMethod(name, lstTypes);
} catch (NoSuchMethodException noSuchMethodException) {
try {
method = parent.getMethod(name, lstTypes);
} catch (NoSuchMethodException nsm) {
if (parent.getSuperclass() != null) {
method = findMethod(parent.getSuperclass(), name, lstTypes);
}
}
}
return method;
}
/**
* Builds up a description of the method call in the format of
* [package.class.method([{package.class}{, package.class}{...}])
*
* This is typically used to throw a NoMethodFound exception.
*
* @param clazz
* @param name
* @param lstTypes
* @return
*/
public static String getMethodDescription(Class clazz, String name, Class[] lstTypes) {
StringBuilder sb = new StringBuilder();
sb.append(clazz.getName()).append(".").append(name).append("(");
for (Class type : lstTypes) {
sb.append(type.getName()).append(", ");
}
if (lstTypes.length > 0) {
sb.delete(sb.length() - 2, sb.length());
}
sb.append(")");
return sb.toString();
}
public class Parameter<T> {
private T value;
private Class<T> clazz;
public Parameter(T value, Class<T> clazz) {
this.value = value;
this.clazz = clazz;
}
public T getValue() {
return value;
}
public Class<T> getType() {
return clazz;
}
}
public class InvocationException extends Error {
public InvocationException(String message) {
super(message);
}
public InvocationException(String message, Throwable cause) {
super(message, cause);
}
}
public static boolean hasMethod(Object instance, String methodName) {
return hasMethod(instance.getClass(), methodName);
}
public static boolean hasMethod(Object instance, String methodName, Class[] types) {
return hasMethod(instance.getClass(), methodName, types);
}
public static boolean hasMethod(Class clazz, String methodName) {
return hasMethod(clazz, methodName, new Class[0]);
}
public static boolean hasMethod(Class clazz, String methodName, Class[] types) {
return findMethod(clazz, methodName, types) != null;
}
}
关于java - 如有必要,在 Java Swing EDT 中运行函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33075704/