如果类的公共函数更新了一些java swing GUI元素,我必须检查调用此函数的上下文,并确保在EDT中完成更新。这就是我解决它的方法,但是因为可能有很多函数我必须做同样的事情(但是调用类的不同私有方法)或者我必须做同样的其他类,我已经厌倦了写总是相同的代码
public void updateGUI()
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
update();
}
});
}
else
{
update();
}
}
private void update()
{
//update the swing elements here
}
一种可能的解决方案可能是创建一个具有此更新方法的接口(名为UpdateInterface)。该类现在将实现此接口,并使用静态方法创建一个辅助类,该方法接受此接口的引用并在EDT上下文中调用update方法:
public static void callUpdateInEDT(UpdateInterface f)
{
if(!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
f.update();
}
});
}
else
{
f.update();
}
}
但这不是很灵活,因为我可能有一些其他私有方法,我应该在EDT上下文中调用。到目前为止我还没有找到SwingUtilities的解决方案或者我提出的更灵活的任何其他解决方案吗?
答案 0 :(得分:2)
因此,经过多年编写更多包装器代码,我在思考内部/匿名类的日子之前,我写了一个基于反射的API,它基本上简化了调用任意方法的任何数量的参数的过程< / p>
所以,你的代码会变得更像......
SwingSafeMethodInvoker.invokeLater(this, "update").execute();
这只是一种可能的解决方案(而且我很懒,所以我喜欢减少我必须(重新)编写的代码量)
代码很聪明,可以知道你是否在EDT上并采取适当的行动。关于这一点的好处是它允许我调用protected
方法(记住,在内部/匿名类之前,你必须创建一个支持,外部Runnable
类,所以它不会有能够调用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);
}
}
<强>的invokeLater 强>
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;
}
}
<强> InvokeAndWait 强>
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);
}
}
}
<强> MethodInvoker 强>
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;
}
}
答案 1 :(得分:0)
我已经通过代理解决了这样的样板代码。
您的UI管理抽象:
public interface UIManager {
void normal();
void internalCall();
void throwException();
}
跟踪实施:
public class UIManagerImpl implements UIManager {
private void traceCall(String method) {
new Exception(Thread.currentThread().getName() + " > " + method).printStackTrace(System.out);
}
@Override
public void normal() {
traceCall("normal");
}
@Override
public void internalCall() {
traceCall("internalCall");
normal();
}
@Override
public void throwException() {
traceCall("throwException");
throw new RuntimeException("doB");
}
}
一个简单的EDT感知代理:
public class EdtHandler implements InvocationHandler {
private Object target;
public EdtHandler(Object target) {
this.target = target;
}
public static <T> T newProxy(Class<T> contract, T impl) {
return (T) Proxy.newProxyInstance(impl.getClass().getClassLoader(), new Class<?>[] { contract }, new EdtHandler(impl));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (SwingUtilities.isEventDispatchThread()) {
dispatch(method, args);
} else {
SwingUtilities.invokeLater(() -> dispatch(method, args));
}
return null;
}
protected void dispatch(Method method, Object[] args) {
try {
method.invoke(target, args);
} catch (IllegalAccessException | InvocationTargetException e) {
onError(e);
}
}
protected void onError(Throwable e) {
throw new IllegalStateException(e);
}
}
现在主要检查:
public static void main(String[] args) {
UIManagerImpl impl = new UIManagerImpl();
UIManager edt = EdtHandler.newProxy(UIManager.class, impl);
Runnable block = () -> { System.out.println("---- Start ---- "); edt.normal(); edt.internalCall(); edt.throwException(); System.out.println("---- Stop ---- "); };
block.run();
SwingUtilities.invokeLater(block);
}
实施说明:
invokeLater
,无论你是否在EDT上。null
,更适合void
方法。return
识别代理:
答案 2 :(得分:0)
关于Lambda的一些读数后,我带着以下解决方案到达。我使用与SwingUtilites同名的静态方法创建一个简单的类,例如invokeLater:
public static void invokeLater(Runnable doRun)
{
if(SwingUtilities.isEventDispatchThread())
{
doRun.run();
}
else
{
SwingUtilities.invokeLater(() -> {
doRun.run();
});
}
}
现在将这个函数与Lambda一起使用是非常容易的,假设新类是SwingUtilities2(我还在为新类寻找一个好名字: - )):
SwingUtilities2.invokeLater(() -> {
//here is the code which should run in the EDT
});