基本上,我有一个自动更新/安装程序,用于更新和/或安装“魔兽世界”插件到我们客户的电脑上。'更新器'做的第一件事就是检查服务器是否与服务器上的更新程序安装程序版本相同。这非常顺利!实际上整个更新程序都工作得很好,除了这一个问题。如果更新程序发现服务器的版本号与更新程序的版本号不同,则会从服务器下载安装程序(在zip中),解压缩它,从包含在zip中的exe文件开始安装,然后关闭当前打开的更新程序,以便安装可以正确完成。
所以我在自我更新的卡片上放置了一个进度条(使用cardLayout来显示更新器,SelfUpdate,Login和AddonList三个不同卡片),以及JProgressBar下面的JTextArea。它查询服务器上的版本号,并将该版本与当前安装的更新程序版本进行比较。如果版本相同,则切换到登录卡片并继续进行下一步,到目前为止都很好,如果版本不同,则向服务器发送查询,并将zip文件发送回更新器以供处理。
这就是它不起作用的地方。zip文件大小合适,当然我想显示此下载的进度。zip文件下载很好(在SwingWorker doInBackground()方法中运行),然后更新程序将installer.exe文件解压缩到客户系统驱动器上的文件夹中,使用Runtime启动并启动installer.exe,弹出安装屏幕,然后使用System.exit(0)关闭当前更新程序。所有这些都可以工作,但JProgressBar本身将不会更新。它始终保持在0%。此外,taskOutput JTextArea也不会更新,但主要问题是进度条不会更新。如果可以修复这个问题而不更新JTextArea,我可以容忍这种情况。(也尝试在文本区域的位置放置JLabel但没有成功)
所以我知道我做错了什么,但我无法想象是什么。以下是发生“魔法”部分的代码。 :) 如果我在这里逐步解释,请原谅我。
Controller.java处理程序的所有流程,以下是你需要查看的部分。
// Imports packages needed for use in this file.
import javax.swing.*;
import java.awt.CardLayout;
import java.io.*;
// class instantiation
public class Controller {
// Variables needed for use in this class
private static Updater util;
private static int loginReturnState = 0;
/**
* Single contructor for this class
*/
public Controller() {
}
/**
* Main method which runes everything in the updater program
*
*/
public static void main(String[] args) {
util = new Updater();
// Schedule a job for the event dispatch thread.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// setup a new GUI
util.createGUI();
JFrame rFrame = util.getRootFrame();
JPanel gFrame = util.getCardLayoutFrame();
CardLayout cards = (CardLayout) gFrame.getLayout();
cards.show(gFrame, "UpdateUpdaterPanel");
rFrame.pack();
rFrame.setLocationRelativeTo(null);
rFrame.setVisible(true);
SelfUpdater selfUp = new SelfUpdater();
int needUpdate = selfUp.checkForUpdate(); if (needUpdate == 2) {// Update available for download. Download it.
selfUp.downloadUpdate();
}
}
});
}
}
好的,下一个文件是Updater类。为了将代码放在这里,希望能够编译并显示问题,我需要将这个类放在那里,否则我在uPdater类中拥有的实用方法将不会存在于被调用的范围内,而我广泛地使用它们。
// Packages needed for use in this file
import javax.swing.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.CardLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseAdapter;
import java.io.*;
// Class instantiation
public class Updater {
// These final variables are set to the path for each of the images used
public static final String BG_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\background.png";
public static final String APP_ICON_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\app-icon.png";
public static final String CLOSE_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\close.png";
public static final String FINOFF_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\FinishedOff.png";
public static final String FINON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\FinishedOn.png";
public static final String ICON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon.png";
public static final String ICON64_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon64x64.png";
public static final String ICON256_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon256x256.png";
public static final String INSTOFF_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\InstallUpdatesOFF.png";
public static final String INSTON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\InstallUpdatesON.png";
public static final String LOGO_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\logo.png";
public static final String MIN_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\minimize.png";
public static final String PROGBACK_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\ProgressBackground.png";
public static final String PROGBAR_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\ProgressBar.png";
public static final String INSTRUCTIONS_BUTTON = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\instructions.png";
public static final String PURCHASE_BUTTON = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\Purchase.png";
// These are various final variables for use throughout the updater
public static final String URL_CONNECT = "http://dynastyaddons.com/api/index2.php?";
public static final String TITLE_TEXT = "Dynasty Updater";
public static final int BUFFER_SIZE = 4096;
public static final double UPDATER_VERSION = 1.1;
// Public variables needed for this class workings
private static JFileChooser fc;
private static JFrame rootFrame;
// Private variables for this class use alone
private static String PHPSessionID;
private static boolean debugToggle = true;
private CardLayout cards;
private JPanel guiFrame;
private Point mouseDownCompCoords;
private Login login;
private Addons addons;
/**
* Sole contructor. (For invocation by subclass contructors, typically implicit.)
*/
public Updater() {
// First instantiate any variables needed
PHPSessionID = "notset";
guiFrame = new JPanel();
cards = new CardLayout();
guiFrame.setLayout(cards);
}
// Various methods follow
/**
* This will create the root gui for showing in the main method
*
*/
public void createGUI() {
rootFrame = new JFrame();
SpringLayout rootLayout = new SpringLayout();
// setup root frame
rootFrame.setUndecorated(true);
rootFrame.setBackground(new Color(0,0,0,0));
rootFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rootFrame.setPreferredSize(new Dimension(800,600));
rootFrame.setTitle(TITLE_TEXT);
// add the background image
JLabel bg = new JLabel(new ImageIcon(BG_IMAGE_PATH));
bg.setLayout(rootLayout);
rootFrame.add(bg);
// setup title bar
mouseDownCompCoords = null;
mouseDownCompCoords = null;
rootFrame.addMouseListener(new MouseListener() {
public void mouseReleased(MouseEvent e){ mouseDownCompCoords = null; }
public void mousePressed(MouseEvent e){ mouseDownCompCoords = e.getPoint(); }
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){ }
public void mouseClicked(MouseEvent e){ }
});
rootFrame.addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e){}
public void mouseDragged(MouseEvent e){
Point currCoords = e.getLocationOnScreen();
rootFrame.setLocation(currCoords.x - mouseDownCompCoords.x, currCoords.y - mouseDownCompCoords.y);
}
});
// Display and place the logo
JLabel logo = new JLabel(new ImageIcon(LOGO_IMAGE_PATH));
bg.add(logo);
// add Close and Minimize buttons
JPanel cmButt = createCloseAndMinButtons();
bg.add(cmButt);
// create card frame
guiFrame.setPreferredSize(new Dimension(800, 435));
guiFrame.setOpaque(false);
bg.add(guiFrame);
// Constrain the parts of your BG
rootLayout.putConstraint(SpringLayout.WEST, logo, 30, SpringLayout.WEST, rootFrame);
rootLayout.putConstraint(SpringLayout.NORTH, logo, 70, SpringLayout.NORTH, rootFrame);
rootLayout.putConstraint(SpringLayout.NORTH, cmButt, 0, SpringLayout.NORTH, rootFrame);
rootLayout.putConstraint(SpringLayout.EAST, cmButt, 0, SpringLayout.EAST, rootFrame);
rootLayout.putConstraint(SpringLayout.SOUTH, guiFrame, 0, SpringLayout.SOUTH, rootFrame);
rootLayout.putConstraint(SpringLayout.WEST, guiFrame, 0, SpringLayout.WEST, rootFrame);
rootLayout.putConstraint(SpringLayout.EAST, guiFrame, 0, SpringLayout.EAST, rootFrame);
// Create self updater panel and get it ready to show
SelfUpdater selfUp = new SelfUpdater();
JPanel selfUpd = selfUp.getSelfUpdatePanel();
// get the cardlayout and add the three panels to it, then show the self-updater to begin.
guiFrame.add(selfUpd, "UpdateUpdaterPanel"); }
/**
* Creates a panel which has the close and minimize buttons on it.
*/
private static JPanel createCloseAndMinButtons() {
JPanel a = new JPanel();
SpringLayout closeLO = new SpringLayout();
a.setLayout(closeLO);
a.setPreferredSize(new Dimension(150,150));
a.setOpaque(false);
// Close Button
JButton cButt = new JButton(new ImageIcon(Updater.CLOSE_IMAGE_PATH));
cButt.setBorderPainted(false);
cButt.setContentAreaFilled(false);
cButt.setFocusPainted(false);
cButt.setOpaque(false);
cButt.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { System.exit(0); }
});
a.add(cButt);
// Minimize Button
JButton mButt = new JButton(new ImageIcon(Updater.MIN_IMAGE_PATH));
mButt.setBorderPainted(false);
mButt.setContentAreaFilled(false);
mButt.setFocusPainted(false);
mButt.setOpaque(false);
mButt.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) { rootFrame.setState(JFrame.ICONIFIED); }
});
a.add(mButt);
// Constrain parts on panel
closeLO.putConstraint(SpringLayout.EAST, cButt, 12, SpringLayout.EAST, a);
closeLO.putConstraint(SpringLayout.NORTH, cButt, -2, SpringLayout.NORTH, a);
closeLO.putConstraint(SpringLayout.EAST, mButt, 30, SpringLayout.WEST, cButt);
closeLO.putConstraint(SpringLayout.NORTH, mButt, -2, SpringLayout.NORTH, a);
// Return the jframe
return a;
}
/**
* Returns the guiFrame which holds the cardlayout for the changing of the panels.
*
* @return JPanel Returns the frame which holds the cardlayout.
*/
public JPanel getCardLayoutFrame() {
return guiFrame;
}
/**
* This will return the cardlayout for use in the controller.
*
* @return CardLayout Returns the cardlayout for use in the Controller.
*/
public CardLayout getCardLayout() { return cards; }
/**
* Returns the current version of this updater as a double
*
* @return the version of this updater as a double.
*/
public double getUpdaterVersion() { return UPDATER_VERSION; }
/**
* Returns a string that tells if the customer is useing windows or mac
*
* @return the operating system in use by the customer
*/
public static String getOSType() {
String retString = "";
String oST = System.getProperty("os.name").toLowerCase();
int spacePos = oST.indexOf(" ");
if (spacePos > 0) {
retString = oST.substring(0, spacePos);
}
retString.trim();
return retString;
}
/**
* Returns the main root frame for display purposes in controller.java
*
* @return JFrame returns rootFrame.
*/
public JFrame getRootFrame() { return rootFrame; }
}
好的,记住这个类只是在后台执行一些实用程序的操作,并且可能不涉及除创建背景GUI和告知SelfUpdate卡片显示之外的任何事情。
最后,这是SelfUpdater.java文件,它显示带有进度条的SelfUpdater面板,并执行所有服务器查询,这很好地工作。请注意,我在downloadUpdate方法中(在控制器文件中调用)中有System.out.println调试语句,它们可以正常工作并在控制台中输出。那个方法中的selfUpdateProgress.setValue(int)和taskOutput.setText(txt)语句什么都没有做。此外,我在doInBackground中放置了一个println,在那里它设置setProgress(int),并且可以在控制台上正确打印,但进度条不更新。就是这样。
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.util.zip.*;
// Create and start class
public class SelfUpdater extends JPanel {
// These are the variables needed for use in this class
private JPanel selfUpdatePanel;
private JProgressBar selfUpdateProgress;
private Task task;
private JTextArea taskOutput;
private Updater util;
/**
* Default class constructor, instantiates variables and sets up GUI.
*
*
**/
public SelfUpdater() {
// Progress Bar
selfUpdateProgress = new JProgressBar(0, 100);
selfUpdateProgress.setValue(0);
selfUpdateProgress.setStringPainted(true);
selfUpdateProgress.setPreferredSize(new Dimension(470,29));
// Layout
SpringLayout selfUpdateLayout = new SpringLayout();
SpringLayout pBarLayout = new SpringLayout();
// Font and color setup for Task Output
Font myFont = new Font("Serif", Font.BOLD, 16);
Color myColor = new Color(255, 170, 0);
// Description of progress pane
taskOutput = new JTextArea("Checking server for updates to the updater.", 1, 39);
taskOutput.setFont(myFont);
taskOutput.setForeground(myColor);
taskOutput.setEditable(false);
taskOutput.setOpaque(false); // this is false after setting up the window.
//extras needed for the class workings
util = new Updater();
// Images for progress bar setup
JLabel pBar = new JLabel(new ImageIcon(util.PROGBACK_IMAGE_PATH));
pBar.setOpaque(false);
//pBar.setLayout(pBarLayout);
//pBar.add(taskOutput);
//pBar.add(selfUpdateProgress);
// Main panel
selfUpdatePanel = new JPanel();
selfUpdatePanel.setPreferredSize(new Dimension(800, 435));
selfUpdatePanel.setLayout(selfUpdateLayout);
selfUpdatePanel.setOpaque(false);
selfUpdatePanel.add(taskOutput);
selfUpdatePanel.add(selfUpdateProgress);
selfUpdatePanel.add(pBar);
// Constrain your bits.
// First constrain PBar to the self update panel
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, pBar, -40, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, pBar, 150, SpringLayout.WEST, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, taskOutput, -50, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, taskOutput, 175, SpringLayout.WEST, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, selfUpdateProgress, -84, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, selfUpdateProgress, 171, SpringLayout.WEST, selfUpdatePanel); }
/**
* Will return the jpanel that contains the gui elements for the Self-Update
*
* @return JPanel The Self-Update gui.
*
*/
public JPanel getSelfUpdatePanel() { return selfUpdatePanel; }
/**
* This will check the server to see if an update is available for the updater
*
* @return int Returns an int that will describe the answer from the server.
* Possible Return values:
* 0 = Problem checking for update: Server return NULL
* 1 = Second type of problem checking for update: Server returned empty string
* 2 = Update available.
* 3 = Update not needed.
*
*/
public int checkForUpdate() {
// ask the server what version it's updater is.
String serverUpdaterVersion = "";
try {
HttpConnect conn = new HttpConnect();
String urlRequest = util.URL_CONNECT + "action=get_updater&platform=" + util.getOSType();
serverUpdaterVersion = conn.textRequest(urlRequest, 1);
} catch (MalformedURLException e) {
JOptionPane.showMessageDialog(null, "We were unable to connect to the server while checking for an updater update, please check your internet connection.", "Attention!",JOptionPane.ERROR_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "We were unable to open the connection to the server while checking for an updater update, please check your internet connection.", "Attention!",JOptionPane.ERROR_MESSAGE);
}
// check for errors and return proper int.
if (serverUpdaterVersion == null) { // There is an error if the server returns null, return 0
return 0;
} else if (serverUpdaterVersion.equals("")) { // if the server returns an empty string return 1 for an error
return 1;
} else { // not null or empty string. check versions
// Check version of this updater to servers version
double sVers = Double.parseDouble(serverUpdaterVersion);
if((double)sVers == util.getUpdaterVersion()) { // return 3 if no update needed here.
return 3;
} else { // otherwise return 2 so we can get the update
return 2;
}
}
}
/**
* This will download the update from the server.
*
* @return File The downloaded file from the server. A zip file usually.
*
*/
public void downloadUpdate() {
// turn on wait cursor
util.getRootFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// start a new task which does the download
// Instances of javax.swing.SwingWorker are not reusuable, so
// we create new instances as needed.
task = new Task();
// add a property listener to the task so it knows to update the progress meter.
task.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
switch(evt.getPropertyName()) {
case "progress":// set the progress value and the task output value.
selfUpdateProgress.setIndeterminate(false);
int prg = (Integer) evt.getNewValue();
selfUpdateProgress.setValue(prg);
taskOutput.setText("Downloading and unzipping update. Progress = " + prg + "%");
System.out.println("Percent progress: " + prg + "%");
break;
}
}
});
// execute or start the task.
task.execute();
}
/**
* This method will unzip the downloaded file, and start the installation.
* Then it will close this program while the installation completes.
*
*
*/
public void unzipNewUpdater() {
// create strings for naming the zip file to open up and where to put it.
String zipFile = "NewUpdater.zip";
String outputFolder = System.getenv("SystemDrive") + "\\DynastyAddons";
// Try the unzipping and catch any errors.
try {
// Setup the input stream and get the entry ready to unzip.
ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry ze = zis.getNextEntry();
// loop to open each entry and output it to the output folder and file.
while(ze!=null) {
// Get the name
String entryName = ze.getName();
// get the file to unzip
File f = new File(outputFolder + File.separator + entryName);
// Create folders needed to hold the unzipped stuff
f.getParentFile().mkdirs();
// get output stream setup to copy the file to from the zip
FileOutputStream fos = new FileOutputStream(f);
// get length of file to make sure we get the whole thing moved
int len;
byte buffer[] = new byte[1024];
// loop thru the entry writing the whole file to the output directory
while((len = zis.read(buffer)) > 0){
fos.write(buffer, 0, len);
}
// close the output stream and get the next entry ready for the next iteration of the loop.
fos.close();
ze = zis.getNextEntry();
}
// close the entry and input stream since we're done.
zis.closeEntry();
zis.close();
} catch (FileNotFoundException ex) {
// throw error
} catch (IOException ex) {
// throw error
}
}
// This private class handles the download and unzip of the new updater if needed.
class Task extends SwingWorker<Void, Void> {
@Override
public Void doInBackground() {
try {
HttpConnect conn = new HttpConnect();
String downloadURL = util.URL_CONNECT + "action=download_updater&platform=" + util.getOSType();
conn.downloadFile(downloadURL);
InputStream inputStream = conn.getInputStream();
FileOutputStream outputStream = new FileOutputStream(System.getenv("SystemDrive") + "\\DynastyAddons\\NewUpdater.zip");
byte[] buffer = new byte[util.BUFFER_SIZE];
int bytesRead = -1;
long totalBytesRead = 0;
int percentCompleted = 0;
long fileSize = conn.getContentLength();
taskOutput.setText("Downloading Newest Updater.");
//setProgress(0);
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
percentCompleted = (int) (totalBytesRead * 100 / fileSize);
setProgress(percentCompleted);
}
outputStream.close();
conn.disconnect();
} catch (IOException ex) {
JOptionPane.showMessageDialog(util.getRootFrame(), "Error downloading Updater file. Please send this entire error to support@dynastyaddons.com. \n Error: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
setProgress(0);
}
return null;
}
@Override
public void done() {
// Create msg window saying update downloaded.
JOptionPane.showMessageDialog(util.getRootFrame(), "The new updater has been downloaded. \nClick OK to close this updater and start the installation of the new version.", "Attention", JOptionPane.PLAIN_MESSAGE);
// Unzip the updater
unzipNewUpdater();
// Tell the cursor we're not busy anymore
setProgress(100);
selfUpdateProgress.setValue(100);
taskOutput.setText("Done Downloading New Updater. Installing...");
util.getRootFrame().setCursor(null);
// Create command used to open the udpater installer.
String cmd = "cmd /c " + System.getenv("SystemDrive") + "\\DynastyAddons\\InstallDynastyAddonsUpdater.exe";
// Here is where we run the exe that will install the new updater.
try{
Runtime.getRuntime().exec(cmd);
}catch (FileNotFoundException ex){
// handle this error
}catch (IOException ex) {
// handle this error
}
// Now close this updater so the new one can install.
System.exit(0);
}
}
}
希望代码能够正常编译,如果需要查看其工作情况,请告知我,我将更新内容以使其在编译中正常工作。所以我试了许多方法,使SelfUpdater类实现PropertyChangeListener并实现PropertyChange(event),我尝试将JProgressBar馈送到Task类的构造函数中。我尝试在SwingWorker中输出整数并使用Publish和Process来更新taskOutput和JProgressBar,我尝试重新编写整个类来修复代码并使其更容易找出问题。使用教程和一些我的代码进行测试(它们表现得很好),但每次我回到这里的更新程序代码时,该进度条都无法更新。我真的希望有人能给我一点见解,以便我可以完成这个项目并开始做一些新的事情!这是我必须解决的最后一件事(除非在Mac上存在问题),然后我就可以回到测试并将其分发给我们的客户了。
欢迎对代码的任何部分进行评论(我可能做了一些愚蠢的事情)。我总是寻找对我的代码的批评,特别是如果我没有按照良好的设计执行某些操作。如果需要更多信息,我可以轻松地粘贴组成此更新程序的七个类中的所有代码。
所有内容都已编译,并且工作正常,除了这个JProgressBar不会显示进度,尽管我可以在控制台上输出它。提前感谢您的回答,对于这篇迷你小说,非常抱歉,我还没有问过问题,并且一直能够从其他人的问题和答案中找到答案,现在我已经被卡住了,准备完成这个项目!:D
如果我在发布帖子时出现问题,请提前谅解,记住这是我第一次发布帖子,虽然我已经阅读了成百上千的帖子,但我确信我的新手错误已经搞砸了一些东西,因此请随意责骂我,并指出我的错误以便我进行修正。:)
编辑:更新了selfUpdater.java以反映仅使用一个Spring布局。