等待 long-运行 操作并显示弹出窗口

Wait for long-running operation and show popup

是否可以等待方法(比如 METHOD1)完成,但如果 运行 长于 X 秒,则调用另一个方法直到 METHOD1 returns?

一些伪代码:

method1();
startCountdown(1000); // time in millis
while (method1() still running) {
    method2(); // shows a popup with spinner (Swing/AWT)
}

我猜,肯定是用并发来做的,但我不习惯并发编程。所以,我不知道如何开始。

使用的UI框架是Swing/AWT。

我以前一直在为这个问题苦苦挣扎。 我最终做的是创建一个单独的 class 来扩展 AsyncTask。向返回我的对象​​的 class 添加了一个 interface/listener。在我开始我的 AsyncTask 之前,我将禁用按钮并放置一个加载微调器。一旦 AsyncTask 返回,我将进行处理并重新启用按钮并取下加载微调器。粗略地说,我在示例中进行了休息调用,但它可以应用于需要一段时间的任何事情。这是比 while 循环更好的选择的原因是它不会燃烧循环检查条件。

public class RestCall extends AsyncTask {
    private Context mContext;
    private static final String TAG = "RestCall";
    private AsyncResponse mListener;

 public RestCall(Context context, URL url, AsyncResponse listener) {
        this.mListener = listener;
        this.mContext = context;
        this.url = url;
    }

 public interface AsyncResponse {
        void processFinish(JSONArray results);
    }

@Override
protected Object doInBackground(Object[] objects) {
    Log.d(TAG, "doInBackground: Thread: " + Thread.currentThread().getName());

    return getResultsInJSONArray(url);

}

private JSONArray getResultsInJSONArray(URL url) {
//Here is where you will be doing the bulk of the work
//Doing a rest call and
//Processing results to JSONArray
}

@Override
protected void onPostExecute(Object o) {
    super.onPostExecute(o);
    Log.d(TAG, "onPostExecute: Handing off Object");
    mListener.processFinish((JSONArray) o);
}

现在,在您的原始 class 中,您需要将以下内容添加到您的 class 中:

public class myClass

private restCall call;

然后从您制作的那个界面创建一个侦听器。然后将结果传递给方法。

restCall.AsyncResponse listener = results -> handleResults(results); 

通过侦听器设置,您可以执行 AsyncTask。

//here is were you would throw up the loading bar.
call = new restCall(this, url, listener);
call.execute();

 private void handleResults(JSONArray results){
//process what you need to
//take down loading bar
}

因此,基本思路是结合使用 SwingWorker 和 Swing Timer

想法是如果 TimerSwingWorker 之前触发 DONE,则执行其他一些工作流,否则停止 Timer,例如.. .

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
import javax.swing.Timer;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private JLabel label;
        private JButton startButton;

        boolean hasCompleted = false;

        public TestPane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridwidth = GridBagConstraints.REMAINDER;

            label = new JLabel("Waiting for you");
            startButton = new JButton("Start");

            add(label, gbc);
            add(startButton, gbc);

            startButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    startButton.setEnabled(false);
                    startWork();
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        protected void startWork() {
            label.setText("Something wicked this way comes");

            // You could build an isoloated workflow, which allowed you to pass
            // three targets, the thing to be executed, the thing to be 
            // executed if time run over and the thing to be executed when
            // the task completed (all via a single interface),
            // but, you get the idea
            Timer timer = new Timer(2000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (hasCompleted) {
                        return;
                    }
                    label.setText("Wickedness is a bit slow today");
                }
            });
            timer.setRepeats(false);

            SomeLongRunningOperation worker = new SomeLongRunningOperation();
            worker.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    switch (worker.getState()) {
                        case DONE:
                            hasCompleted = true;
                            timer.stop();
                            label.setText("All is done");
                            startButton.setEnabled(true);
                            break;
                    }
                }
            });
            worker.execute();
            timer.start();
        }

    }

    public class SomeLongRunningOperation extends SwingWorker<Void, Void> {

        @Override
        protected Void doInBackground() throws Exception {
            Thread.sleep(5000);
            return null;
        }

    }
}

调整时间,看看您会得到什么不同的效果。

为什么要使用 SwingWorker?因为它有自己的状态回调,这使得它更容易处理

正如我在评论中所说,您可以将工作流程提炼成一个可重复使用的概念,例如...

public class TimedTask<V> {
    
    public static interface Task<V> {
        public V execute() throws Exception;
    }
    
    public static interface TimedTaskListener<V> extends EventListener {
        public void taskIsTakingLongThenExepected(TimedTask task);
        public void taskDidComplete(TimedTask task, V value);
    }
    
    private Task<V> task;
    private TimedTaskListener<V> listener;
    
    private V value;
    
    private int timeOut;
    private Timer timer;
    private SwingWorker<V, Void> worker;
    private boolean hasCompleted = false;

    public TimedTask(int timeOut, Task<V> task, TimedTaskListener<V> listener) {
        this.task = task;
        this.listener = listener;
        this.timeOut = timeOut;
    }

    public V getValue() {
        return value;
    }

    public int getTimeOut() {
        return timeOut;
    }

    protected Task<V> getTask() {
        return task;
    }

    protected TimedTaskListener<V> getListener() {
        return listener;
    }
    
    public void execute() {
        if (timer != null || worker != null) {
            return;
        }
        
        hasCompleted = false;
        worker = new SwingWorker<V, Void>() {
            @Override
            protected V doInBackground() throws Exception {
                value = task.execute();
                return value;
            }
        };
        worker.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                switch (worker.getState()) {
                    case DONE:
                        hasCompleted = true;
                        timer.stop();
                        getListener().taskDidComplete(TimedTask.this, value);
                        break;
                }
            }
        });
        
        timer = new Timer(getTimeOut(), new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (hasCompleted) {
                    return;
                }
                getListener().taskIsTakingLongThenExepected(TimedTask.this);
            }
        });
        timer.setRepeats(false);

        worker.execute();
        timer.start();
    }
    
}

然后您可以将第一个示例中的 startWork 方法替换为...

protected void startWork() {
    label.setText("Something wicked this way comes");
    TimedTask.Task<Void> task = new TimedTask.Task<Void>() {
        @Override
        public Void execute() throws Exception {
            Thread.sleep(5000);
            return null;
        }
    };
    TimedTask<Void> timedTask = new TimedTask(2000, task, new TimedTask.TimedTaskListener<Void>() {
        @Override
        public void taskIsTakingLongThenExepected(TimedTask task) {
            label.setText("Wickedness is taking it's sweet time");
        }

        @Override
        public void taskDidComplete(TimedTask task, Void value) {
            label.setText("Wickedness has arrived");
            startButton.setEnabled(true);
        }
    });
    timedTask.execute();
}

虽然 SwingWorker 是完成这项工作的合适工具,但对于简单的任务,您可以使用 Thread 完成 off-edt 长任务,并使用 swing Timer 进行更新图形用户界面:

import java.awt.*;
import javax.swing.*;
import javax.swing.Timer;

public class Main{

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.add(new TestPane());
        frame.pack();
        frame.setVisible(true);
    }
}

class TestPane extends JPanel{

    private static Dimension size = new Dimension(250, 100);
    private final JLabel label;
    private final JButton start;
    private int counter;
    private Timer timer;

    public TestPane() {
        setLayout(new BorderLayout(10, 10));
        label = new JLabel("Click START to run long process", JLabel.CENTER);
        add(label,BorderLayout.NORTH);
        start = new JButton("START");
        start.addActionListener(e-> start() );
        add(start, BorderLayout.SOUTH);
    }

    private void start() {
        start.setEnabled(false);
        int processRunTime = 10;
        int updateTime = 1; //if this value >= processRunTime update() is not invoked
        counter = 1;
        simulateLongProcessOf(processRunTime);
        timer = new Timer(1000*updateTime, e->update(counter++));
        label.setText("Long process started");
        timer.start();
    }

    private void stop() {
        label.setText("Long process ended");
        timer.stop();
        start.setEnabled(true);
    }

    @Override
    public Dimension preferredSize() {
        return size;
    }

    private void simulateLongProcessOf(int seconds){

        Thread t1 = new Thread(()->{
            try {
                Thread.sleep(1000*seconds);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }finally {
                SwingUtilities.invokeLater(()->stop());
            }
        });
        t1.start();
    }

    private void update(int count){
        label.setText("Update # "+ count+" : long process is running" );
    }
}