首页 文章

取消Java Webstart自定义下载

提问于
浏览
1

当您在Java Webstart应用程序中下载资源时,通常会显示一个下载进度窗口,其中显示了下载的进度 . 如果此窗口是默认进度窗口,则它具有取消按钮 . 我基本上试图在自定义下载进度窗口中实现此取消按钮 .

由于没有方法可以调用取消下载,我试图找出在默认进度窗口中如何完成此操作 . 由于使用ServiceManager实现,找到实际的实现有点棘手 . 但我终于找到了这个:[jdk-source on googlecode (DownloadServiceImpl)] .

当您搜索“取消”或只是向下滚动到进度方法时,您将看到它应该像抛出RuntimeException一样简单 . 可悲的是,这并没有真正起作用 . 它只是停止调用progress方法 . 资源仍然在后台下载,loadPart方法永远不会返回 .

如果你想亲自尝试一下,我已经准备了一个小例子 . 你需要某种网络服务器(当然,本地网络服务器就足够了) . 我在使用Java 1.6.0_21(和apache tomcat 6)的Windows XP(32位)上试过这个 .

一个简单的jnlp文件看起来像这样(你可能想要更改端口):

<?xml version="1.0" encoding="utf-8"?>
<jnlp 
  spec="1.0+"
  codebase="http://127.0.0.1:8080/DownloadTest" 
  href="DownloadTest.jnlp" 
  version="1.0">

  <information>
    <title>DownloadTest</title>
    <vendor>Download Tester</vendor>
  </information>

  <resources os="Windows">
    <java version="1.6.0_18+" href="http://java.sun.com/products/autodl/j2se" />
    <jar href="DownloadTest.jar" main="true"/>
    <jar href="largeResource.jar" download="lazy" part="One"/>
  </resources>

  <application-desc main-class="downloadtest.Main">
  </application-desc>
</jnlp>

接下来,您需要一个大文件作为资源(内容根本不重要) . 例如,在许多Windows机器上,您将在"Windows\Driver Cache\i386"下找到"driver.cab" . 必须将该文件添加到jar存档( jar -cf largeResource.jar <input file> ) .

主程序看起来像这样(你需要包含jnlp.jar作为lib,你可以在 <jdk_home>\sample\jnlp\servlet 找到它):

package downloadtest;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.jnlp.DownloadService;
import javax.jnlp.DownloadServiceListener;
import javax.jnlp.ServiceManager;
import javax.jnlp.UnavailableServiceException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingWorker;

public class Main {
    private static DownloadService downloadService;
    private static DownloadServiceListener customDownloadWindow;

    static {
        try {
            downloadService = (DownloadService) ServiceManager.lookup("javax.jnlp.DownloadService");
        } catch (UnavailableServiceException ex) {
            System.err.println("DownloadService not available.");
        }
        customDownloadWindow = new CustomProgress();
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("DownloadTest");
        frame.setBounds(0, 0, 200, 100);
        frame.setDefaultCloseOperation(JDialog.EXIT_ON_CLOSE);
        frame.setLayout(null);
        JButton startDownload = new JButton("download");
        startDownload.setBounds(20, 20, 150, 40);
        startDownload.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                new SwingWorker<Void, Void>() {
                    @Override
                    protected Void doInBackground() {
                        try {
                            downloadService.loadPart("One", customDownloadWindow);
                            //downloadService.loadPart("One", downloadService.getDefaultProgressWindow());
                        } catch (IOException ex) {
                            ex.printStackTrace();
                            System.err.println("IOException loadPart.");
                        }
                        return null;
                    }
                }.execute();
            }
        });
        frame.add(startDownload);
        frame.setVisible(true);
    }
}

您可以通过取消注释一个“downloadService.loadPart ...”行并注释掉另一个来尝试每个下载进度窗口 .

最后是自定义进度窗口本身:

package downloadtest;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.jnlp.DownloadServiceListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

public class CustomProgress implements DownloadServiceListener {
    JFrame frame = null;
    JProgressBar progressBar = null;
    boolean uiCreated = false;
    boolean canceled = false;

    public CustomProgress() {
    }

    private void create() {
        JPanel top = createComponents();
        frame = new JFrame(); // top level custom progress indicator UI
        frame.getContentPane().add(top, BorderLayout.CENTER);
        frame.setBounds(300,300,400,300);
        frame.pack();
        updateProgressUI(0);
    }

    private JPanel createComponents() {
        JPanel top = new JPanel();
        top.setBackground(Color.WHITE);
        top.setLayout(new BorderLayout(20, 20));

        String lblText = "<html><font color=green size=+2>JDK Documentation</font>" +
                   "
The one-stop shop for Java enlightenment!
</html>"; JLabel lbl = new JLabel(lblText); top.add(lbl, BorderLayout.NORTH); progressBar = new JProgressBar(0, 100); progressBar.setValue(0); progressBar.setStringPainted(true); top.add(progressBar, BorderLayout.CENTER); JButton cancelButton = new JButton("Cancel"); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { CustomProgress.this.canceled = true; } }); top.add(cancelButton, BorderLayout.SOUTH); return top; } public void progress(URL url, String version, long readSoFar, long total, int overallPercent) { updateProgressUI(overallPercent); } public void upgradingArchive(java.net.URL url, java.lang.String version, int patchPercent, int overallPercent) { updateProgressUI(overallPercent); } public void validating(java.net.URL url, java.lang.String version, long entry, long total, int overallPercent) { updateProgressUI(overallPercent); } public void downloadFailed(URL url, String string) { System.err.println("Download failed"); } private void updateProgressUI(int overallPercent) { if (overallPercent > 0 && overallPercent < 99) { if (!uiCreated) { uiCreated = true; // create custom progress indicator's UI only if // there is more work to do, meaning overallPercent > 0 and < 100 // this prevents flashing when RIA is loaded from cache create(); } progressBar.setValue(overallPercent); if (canceled) { throw new RuntimeException("canceled by user"); } SwingUtilities.invokeLater(new Runnable() { public void run() { frame.setVisible(true); } }); } else { // hide frame when overallPercent is above 99 SwingUtilities.invokeLater(new Runnable() { public void run() { if (frame != null) { frame.setVisible(false); frame.dispose(); } } }); } } }

这基本上取自Oracle教程(http://download.oracle.com/javase/tutorial/deployment/webstart/customProgressIndicatorForAppln.html) . 我刚刚添加了一个取消按钮 .

当您将其构建为jar文件并将其与largeResource.jar和DownloadTest.jnlp一起放在Web服务器的公用文件夹中时,您应该能够通过Web浏览器启动该应用程序 . 然后单击下载按钮,在完成之前单击下载窗口中的取消按钮 . 尝试自定义进度窗口后,您需要从Java缓存中删除应用程序(或仅删除资源)(因为无论单击取消按钮,资源都会在后台下载) .

那么,为什么这会使用默认进度窗口而不是自定义进度窗口?是否有可能通过自定义下载窗口取消下载?

任何帮助或提示赞赏 .

德拉克斯

1 回答

  • 0

    好的,看看你展示的Google样本,并在课程的最底层找到了它

    /* 
     * Progress Helper class
     *
     * The DownloadServiceListerner interface defined in the JNLP API is 
     * a subset of the DownloadProgressWindow interface used by elsewhere.
     *
     * this class is used to create a Helper object that implements both.
     */
    private class ProgressHelper extends CustomProgress {
    
    
        private DownloadServiceListener _dsp = null;
    
        public ProgressHelper() {
            _dsp = null;
        }
    
        public ProgressHelper(DownloadServiceListener dsp) {
            setAppThreadGroup(Thread.currentThread().getThreadGroup());
            setListener(dsp);
            _dsp = dsp;
            if (_dsp instanceof DefaultProgressHelper) {
                ((DefaultProgressHelper) _dsp).initialize();
            }
            // for bug #4432604:
            _dsp.progress(null, null, 0, 0, -1);
        }
    
        public void done() {
            if (_dsp instanceof DefaultProgressHelper) {
                ((DefaultProgressHelper) _dsp).done();
            } else {
                // make sure callbacks to DownloadServiceListener have
                // been called before returning (for TCK test)
                flush();
            }
        }
    }
    

    有趣的是,它看起来像将当前线程的 ThreadGroup 设置为应用程序线程组 . 所以这让我相信,通过这样做,实际下载更接近应用程序(不确定正确的术语是什么),这样在取消检查中类中的 RuntimeException 抛出确实会影响它 . 否则,我的预感是,在您的应用程序中,下载实际上发生在另一个线程中,并且由应用程序抛出 Exception ,因此,允许它完成 .

相关问题