Can I combine this into one loop? [duplicate] - java

This question already has answers here:
How to chain multiple different InputStreams into one InputStream
(5 answers)
Closed 8 years ago.
Hi i have a run method thats inside a Thread that writes either using a fileOutputStream stream or a RandomaccessFile. I have it in 2 while loops I'de like to combine it into 1 but I'm lost as to how. here is the run method.
package treadmakestop;
import java.awt.EventQueue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
/**
*
* #author brett
*/
public class Worker implements Runnable {
private final ReentrantLock PAUSELOCK;
private final Condition PAUSECONDITION;
private final AtomicBoolean PAUSED;
private final AtomicBoolean KEEPRUNNING;
private final AtomicBoolean KILLSTREAM;
private final File FILEIN = new File("C:\\Users\\brett\\Documents\\Back to the Beginning Origins Nova Neil Degrasse Tyson Documentary-D-EXw5CdPtM.mp4");
private final File WORKINGDIR = new File(System.getProperty("user.home") + File.separator + "Documents" + File.separator + "ConvertedTvFiles" + File.separator);
private final File FILEOUT = new File(WORKINGDIR + File.separator + "spew" + ".mp4");
private final JProgressBar TVPG;
private final long LENGTH = FILEIN.length();
private final byte[] b = new byte[1024];
private FileOutputStream FOUTS;
private FileInputStream FINS;
private RandomAccessFile RandFileOut;
private RandomAccessFile RandFileIn;
private int r;
private long Counter = 1;
public Worker(JProgressBar tvpg) {
PAUSED = new AtomicBoolean();
KEEPRUNNING = new AtomicBoolean(true);
KILLSTREAM = new AtomicBoolean(false);
PAUSELOCK = new ReentrantLock();
PAUSECONDITION = PAUSELOCK.newCondition();
TVPG = tvpg;
}
/**
* This is the main thread and copy file operation
*/
#Override
public void run() {
System.out.println("Runnable has started");
int progress;
try {
createFirstFileStream();
while ((r = FINS.read(b)) != -1 && KEEPRUNNING.get()) {
checkPauseState();
if (!KILLSTREAM.get()) {
Counter += r;
FOUTS.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killFileStream();
break;
}
}
createRandomFile();
while ((r = RandFileIn.read(b)) != -1 && KEEPRUNNING.get()) {
checkPauseState();
if (KILLSTREAM.get()) {
Counter += r;
RandFileOut.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killRandFile();
break;
}
}
} catch (IOException ex) {
Logger.getLogger(Worker.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println("Runnable has exited");
}
public void createFirstFileStream() throws IOException {
FINS = new FileInputStream(FILEIN);
FOUTS = new FileOutputStream(FILEOUT);
}
public void createRandomFile() throws IOException {
RandFileIn = new RandomAccessFile(FILEIN, "rw");
RandFileIn.seek(FILEOUT.length());
long pointer = RandFileIn.getFilePointer();
RandFileOut = new RandomAccessFile(FILEOUT, "rw");
RandFileOut.seek(pointer);
}
public boolean isPaused() {
return PAUSED.get();
}
public void pause() {
KILLSTREAM.set(true);
PAUSED.set(true);
}
public void resume() {
PAUSED.set(false);
PAUSELOCK.lock();
try {
PAUSECONDITION.signal();
} finally {
PAUSELOCK.unlock();
}
}
protected void checkPauseState() {
while (PAUSED.get()) {
PAUSELOCK.lock();
try {
PAUSECONDITION.await();
} catch (Exception e) {
} finally {
PAUSELOCK.unlock();
}
}
}
protected void updateProgress(int progress) {
if (EventQueue.isDispatchThread()) {
TVPG.setValue(progress);
if (KEEPRUNNING.get() == false) {
TVPG.setValue(0);
}
} else {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
updateProgress(progress);
}
});
}
}
public void killFileStream() throws IOException {
FINS.close();
FOUTS.flush();
FOUTS.close();
}
public void killRandFile() throws IOException {
RandFileIn.close();
RandFileOut.close();
}
public synchronized void stop() {
KEEPRUNNING.set(false);
resume();
}
}
Here is the whole class

Something like this should work:
createFirstFileStream();
createRandomFile();
InputStream combined = new SequentialInputStream(FINS,
Channels.createInputStream(RandomFileIn.getChannel());
// your while loop
while ((r = combined.read(b)) != -1 && KEEPRUNNING.get()) {
if (!KILLSTREAM.get()) {
Counter += r;
FOUTS.write(b, 0, r);
progress = (int) Math.round(100 * Counter / LENGTH);
updateProgress(Math.min(progress, 100));
} else {
killFileStream();
break;
}
}
Alternatively, wrap the loop in a function and call it twice, using the Channels call above to wrap the RandomAccessFile to an InputStream.

Related

ScheduledThreadPoolExecutor scheduled purging memory leak

I am trying to write a enclosed class to download images from the web that will refresh every 30 seconds. I might want to download 1 image or I might want to download N images. And I might want to stop downloading a certain image at any time. I wrote the following class that works great except when I stop downloading an image memory is not being released for that task. Or if I stop all images from being downloaded memory is not released (This won't happen in production). I have tried several different ways to achieve this. My last attempt was to purge the tasks from the ScheduledThreadPoolExecutor every 30 seconds using the same executor or with the code below a separate executor. I am also providing code to test releasing memory, although my example stops all images from being downloaded when in a real life usage I should be able only stop one image and the memory be released from that one task.
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
public class ImageLoadTask implements Runnable {
private static ScheduledThreadPoolExecutor taskExecutorService = new ScheduledThreadPoolExecutor(500);
private static ScheduledThreadPoolExecutor purgeExecutorService = new ScheduledThreadPoolExecutor(500);
private static Runnable purgeRunnable = () -> purge();
private ScheduledFuture<?> scheduledFuture;
private URL pictureURL;
private Consumer<BufferedImage> successMethod;
private Consumer<String> failMethod;
private ImageURLStreamHandler streamHandler = new ImageURLStreamHandler();
private boolean displaySuccess = false;
private boolean displayError = false;
private boolean isCancelled = false;
static {
taskExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
taskExecutorService.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
taskExecutorService.setRemoveOnCancelPolicy(true);
purgeExecutorService.scheduleWithFixedDelay(purgeRunnable, 30L, 30L, TimeUnit.SECONDS);
}
public ImageLoadTask(String url) {
try {
this.pictureURL = new URL(url);
} catch (MalformedURLException e) {
if(failMethod != null) {
failMethod.accept(e.getMessage()); ;
}
if(displayError) {
System.out.println("(ImageLoadTask) URL is malformed: " + url+"\n"+ e.getMessage());
}
}
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod) {
this(url);
this.successMethod = successMethod;
}
public ImageLoadTask(String url, Consumer<BufferedImage> successMethod, Consumer<String> failMethod) {
this(url, successMethod);
this.failMethod = failMethod;
}
public void start() {
scheduledFuture = taskExecutorService.scheduleAtFixedRate(this, 0L, 30L, TimeUnit.SECONDS);
}
public void stop() {
if(isCancelled)
return;
isCancelled = true;
scheduledFuture.cancel(true);
scheduledFuture = null;
pictureURL = null;
successMethod = null;
failMethod = null;
streamHandler = null;
taskExecutorService.remove(this);
taskExecutorService.purge();
}
public static void purge() {
System.out.println("Purging");
taskExecutorService.purge();
}
#Override
public void run() {
if(!isCancelled) {
try {
BufferedImage image = loadImage(pictureURL);
if(displaySuccess) {
System.out.println("Image received for url " + pictureURL);
}
if(successMethod != null && !isCancelled) {
successMethod.accept(image); ;
}
} catch (IOException e) {
if(failMethod != null && !isCancelled) {
failMethod.accept(e.getMessage());
}
if(displayError) {
System.out.println("Error occured retrieving image for url: " + pictureURL +"\n"+ e.getMessage());
}
}
}
}
public void displayError(boolean displayError) {
this.displayError = displayError;
}
public void displaySuccess(boolean displaySuccess) {
this.displaySuccess = displaySuccess;
}
private BufferedImage loadImage(URL input) throws IOException {
if (input == null) {
throw new IllegalArgumentException("input == null!");
}
InputStream istream = null;
try {
istream = streamHandler.openConnection(input).getInputStream();
} catch (IOException e) {
throw new IIOException("Can't get input stream from URL!", e);
}
ImageInputStream stream = ImageIO.createImageInputStream(istream);
BufferedImage bi;
try {
bi = ImageIO.read(stream);
if (bi == null) {
stream.close();
}
} finally {
istream.close();
}
return bi;
}
#Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize");
}
class ImageURLStreamHandler extends URLStreamHandler {
#Override
protected URLConnection openConnection(URL url) throws IOException {
URL target = new URL(url.toString());
URLConnection connection = target.openConnection();
// Connection settings
connection.setConnectTimeout(60000); // 1 min
connection.setReadTimeout(60000); // 1 min
return connection;
}
}
}
Test App:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ImageLoadTaskTest {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
Gui gui = new Gui();
}
});
}
static class Gui extends JFrame {
private static final long serialVersionUID = 1L;
private List<ImageLoadTask> tasks = new ArrayList<>();
private boolean running = false;
private JButton startStopButton = new JButton("Start");
private JButton purgeButton = new JButton("Purge");
private ActionListener startStopListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(running) {
stopTasks();
} else {
startTasks();
}
}
};
private ActionListener purgeListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
ImageLoadTask.purge();
}
};
public Gui() {
setTitle("Image Load Task Test");
setBounds(250, 250, 300, 150); // Size
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPanel = new JPanel();
setContentPane(contentPanel);
startStopButton.addActionListener(startStopListener);
contentPanel.add(startStopButton);
purgeButton.addActionListener(purgeListener);
contentPanel.add(purgeButton);
setVisible(true);
}
private void startTasks() {
running = true;
System.out.println("Starting tasks");
for(int i = 0; i < 2500; i++) {
ImageLoadTask task = new ImageLoadTask("http://placehold.it/120x120&text=image" + i, this::success, this::fail);
task.start();
tasks.add(task);
}
startStopButton.setText("Stop");
}
private void stopTasks() {
running = false;
System.out.println("Stopping " + tasks.size() + " tasks");
for(ImageLoadTask task : tasks) {
task.stop();
}
tasks.clear();
startStopButton.setText("Start");
System.out.println("Stopped tasks ");
//ImageLoadTask.purge();
}
private void success(BufferedImage image) {
//System.out.println("Success!");
}
private void fail(String message) {
//System.out.println("Fail! "+ message);
}
}
}
You don't close your 'stream' when you interrupt ImageIO.read(stream).

javax.sound.sampled - trying to start audio sample repeatedly doesn't work

I am programming a little drum sequencer, a roland tr808 knockoff with 16 steps/measure and 16 instruments(=drum samples). User has a gui where he can thus create a 16x16 pattern.
However, if a sample is played more than once in quick succession, it often just gets played once. Say, I got a bassdrum on step 1, 5, 9 and 13 and tempo's 130BPM, it sometimes plays just the bd on 1 and 9, and sometimes the ones on 5 and/or 13 as well. If the sample is very short or the tempo is slow, the chances are higher that every step in the pattern is played correctly. So I assume that the audio line doesn't like it when I try to play a sample again when it hasn't finished yet.
But actually I thought I'd taken that into account in my code. I'd be really thankful if someone told me what's wrong with my code.
Here's my complete code as suggested by Andrew Thompson, modified so that it takes some samples from the internet. Loading them takes a bit, though. the part causing the issue is probably the play() method in the Instrument class:
package testbox;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.sound.sampled.*;
public class boomboxtest {
public static void main(String[] args) {
Sequencer seq = new Sequencer();
//bassdrum
seq.toggleInstrument(0,0);
seq.toggleInstrument(0,4);
seq.toggleInstrument(0,8);
seq.toggleInstrument(0,12);
//snare
seq.toggleInstrument(1,4);
seq.toggleInstrument(1,12);
//Hihat
seq.toggleInstrument(2, 2);
seq.toggleInstrument(2, 6);
seq.toggleInstrument(2, 10);
//Bongo
seq.toggleInstrument(3, 6);
seq.toggleInstrument(3, 10);
seq.setTempo(130);
seq.play();
}
}
class Sequencer {
private Mixer mixer;
private List<SequencerListener> listeners = new ArrayList<SequencerListener>();
public static final int INSTR_COUNT = 4;
private int tempo_bpm = 120;
private ExecutorService executor;
private int current_step = 0;
private int current_max_step = 16;
private boolean[][] pattern = new boolean[32][INSTR_COUNT];
private ArrayList<Instrument> instruments;
Line[] lines = new Line[16];
private SequencerEngine seq;
private String[] filenames = {"http://www.canadianmusicartists.com/sample/kick_02.wav", "http://www.canadianmusicartists.com/sample/snare01.wav", "http://www.canadianmusicartists.com/sample/H_closedhat_01.wav", "http://www.canadianmusicartists.com/sample/bongo01.wav"};
public Sequencer() {
seq = new SequencerEngine();
try{
Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo();
mixer = AudioSystem.getMixer(mixerInfo[0]);
} catch (Exception e) {e.printStackTrace();}
instruments = new ArrayList<Instrument>(INSTR_COUNT);
for (int i = 0; i < INSTR_COUNT; i++) {
System.out.println("Loading instrument " + i);
Instrument instr = new Instrument(filenames[i], mixer);
instruments.add(instr);
lines[i] = instr.getLine();
}
syncMixer();
executor = Executors.newCachedThreadPool();
executor.submit(seq);
}
public void syncMixer() {
if (mixer.isSynchronizationSupported(lines, false)) {
mixer.synchronize(lines, false);
} else {
System.out.println("No hay synchronisado");
}
}
public boolean isPlaying() {
return seq.getRunning();
}
public boolean toggleInstrument (int instrument, int beat) {
pattern[beat][instrument] = !pattern[beat][instrument];
return pattern[beat][instrument];
}
public void play() {
seq.toggleRun(true);
}
public void pause() {
seq.toggleRun(false);
}
public void stop() {
pause();
setCurrent_step(0);
}
public int getTempo() {
return tempo_bpm;
}
public void setTempo(int tempo) {
if (tempo < 30) {
tempo = 30;
} else if (tempo > 200) {
tempo = 200;
} else {
this.tempo_bpm = tempo;
}
}
public int getCurrent_step() {
return current_step;
}
public void setCurrent_step(int current_step) {
this.current_step = current_step;
}
public boolean[][] getPattern() {
return pattern;
}
public void kill() {
seq.kill();
executor.shutdownNow();
}
public void addListener(SequencerListener toAdd) {
listeners.add(toAdd);
}
public class SequencerEngine implements Runnable{
private boolean running;
private boolean alive = true;
public void run() {
while( getAlive()) {
while (getRunning()) {
if (current_step >= current_max_step) {
current_step = 0;
}
for (; current_step < current_max_step ; current_step++) {
stepListen();
if(!getRunning()) {
break;
}
long time = System.currentTimeMillis();
long steptime = 60000/(4*tempo_bpm);
for (int k = 0; k < INSTR_COUNT; k++) {
if (pattern[current_step][k]) {
instruments.get(k).play();
}
}
while((System.currentTimeMillis()-time) < steptime) {}
}
}
}
}
public void stepListen() {
for (SequencerListener sl : listeners) {
sl.stepEvent(current_step);
}
}
public boolean getRunning() {
return running;
}
public boolean getAlive() {
return alive;
}
public void toggleRun(boolean toggle) {
running = toggle;
}
public void kill() {
alive = false;
}
}
}
class Instrument {
private String name;
private File soundFile;
private AudioInputStream stream;
private AudioFormat format;
private DataLine.Info info;
private Clip clip;
private Mixer mixer;
public Instrument(String filename, Mixer mixer ) {
this.name = filename;
try {
//soundFile = new File("sounds/" + filename);
URL url = new URL(filename);
this.mixer = mixer;
//stream = AudioSystem.getAudioInputStream(soundFile);
stream = AudioSystem.getAudioInputStream(url);
format = stream.getFormat();
info = new DataLine.Info(Clip.class, format);
clip = (Clip) mixer.getLine(info);
clip.open(stream);
}
catch (Exception e) {
e.printStackTrace();
}
}
public void play() {
clip.stop();
clip.setFramePosition(0);
clip.start();
}
public Line getLine() {
return clip;
}
}
interface SequencerListener {
void stepEvent(int current_step);
}
The samples are of rather questionable quality, but especially the bassdrum sample illustrates my problem really good.

RoboVM implementation of recording demo using AudioQueue results in "No #Marshaler found" error

I'm trying to implement iOS audio recording using RoboVM using the Apple's AudioQueue guide and their sample SpeakHere project
and am running into this error:
No #Marshaler found for parameter 1 of #Callback method <AQRecorder: void HandleInputBuffer(AQRecorder,org.robovm.apple.audiotoolbox.AudioQueue,org.robovm.apple.audiotoolbox.AudioQueueBuffer,org.robovm.apple.coreaudio.AudioTimeStamp,int,org.robovm.apple.coreaudio.AudioStreamPacketDescription)>
Any ideas? Here's the code I'm using:
Main.java:
import org.robovm.apple.coregraphics.CGRect;
import org.robovm.apple.foundation.NSAutoreleasePool;
import org.robovm.apple.uikit.UIApplication;
import org.robovm.apple.uikit.UIApplicationDelegateAdapter;
import org.robovm.apple.uikit.UIApplicationLaunchOptions;
import org.robovm.apple.uikit.UIButton;
import org.robovm.apple.uikit.UIButtonType;
import org.robovm.apple.uikit.UIColor;
import org.robovm.apple.uikit.UIControl;
import org.robovm.apple.uikit.UIControlState;
import org.robovm.apple.uikit.UIEvent;
import org.robovm.apple.uikit.UIScreen;
import org.robovm.apple.uikit.UIWindow;
public class IOSDemo extends UIApplicationDelegateAdapter {
private UIWindow window = null;
#Override
public boolean didFinishLaunching(UIApplication application,
UIApplicationLaunchOptions launchOptions) {
final AQRecorder aqRecorder = new AQRecorder();
final UIButton button = UIButton.create(UIButtonType.RoundedRect);
button.setFrame(new CGRect(115.0f, 121.0f, 91.0f, 37.0f));
button.setTitle("Start", UIControlState.Normal);
button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener() {
#Override
public void onTouchUpInside(UIControl control, UIEvent event) {
if(button.getTitle(UIControlState.Normal) == "Stop"){
aqRecorder.stopRecord();
button.setTitle("Start", UIControlState.Normal);
}
else{
aqRecorder.startRecord();
button.setTitle("Stop", UIControlState.Normal);
}
}
});
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setBackgroundColor(UIColor.lightGray());
window.addSubview(button);
window.makeKeyAndVisible();
try {
aqRecorder.setUpAudioFormat();
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return true;
}
public static void main(String[] args) {
try (NSAutoreleasePool pool = new NSAutoreleasePool()) {
UIApplication.main(args, null, IOSDemo.class);
}
}
}
AQRecorder.java:
import org.robovm.apple.audiotoolbox.AudioFile;
import org.robovm.apple.audiotoolbox.AudioQueue;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer;
import org.robovm.apple.audiotoolbox.AudioQueue.AudioQueuePtr;
import org.robovm.apple.coreaudio.AudioFormat;
import org.robovm.apple.coreaudio.AudioStreamBasicDescription;
import org.robovm.apple.coreaudio.AudioStreamPacketDescription;
import org.robovm.apple.coreaudio.AudioTimeStamp;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.ptr.FunctionPtr;
import org.robovm.rt.bro.ptr.VoidPtr;
public class AQRecorder {
AudioStreamBasicDescription mDataFormat; // 2
AudioQueue mQueue; // 3
//AudioQueueBufferRef mBuffers[kNumberBuffers]; // 4
AudioFile mAudioFile; // 5
int bufferByteSize; // 6
int mCurrentPacket; // 7
boolean mIsRunning; // 8
public void startRecord(){
mQueue.start(null);
}
public void stopRecord(){
mQueue.stop(true);
}
#Callback
static void HandleInputBuffer(
AQRecorder aqData,
AudioQueue inAQ,
AudioQueueBuffer inBuffer,
AudioTimeStamp inStartTime,
int inNumPackets,
AudioStreamPacketDescription inPacketDesc
) {
AQRecorder pAqData = aqData; // 1
if (inNumPackets == 0 && pAqData.mDataFormat.mBytesPerPacket() != 0)
inNumPackets = inBuffer.mAudioDataByteSize() / pAqData.mDataFormat.mBytesPerPacket();
if (!aqData.mIsRunning) // 5
return;
System.out.println(inBuffer.mAudioData());
}
void setUpAudioFormat() throws NoSuchMethodException{
mDataFormat = new AudioStreamBasicDescription(
16000, // mSampleRate
AudioFormat.LinearPCM, // mFormatID
(1 << 2), // mFormatFlags
512, // mBytesPerPacket
1, // mFramesPerPacket
512, // mBytesPerFrame
1, // mChanneslPerFrame
16, // mBitsPerChannel
0 // mReserved
);
AudioQueuePtr mQueuePtr = new AudioQueuePtr();
mQueuePtr.set(mQueue);
VoidPtr self = new VoidPtr();
#SuppressWarnings("rawtypes")
Class[] cArg = new Class[6];
cArg[0] = AQRecorder.class;
cArg[1] = AudioQueue.class;
cArg[2] = AudioQueueBuffer.class;
cArg[3] = AudioTimeStamp.class;
cArg[4] = int.class;
cArg[5] = AudioStreamPacketDescription.class;
FunctionPtr handleInputBuffer = new FunctionPtr((AQRecorder.class).getDeclaredMethod("HandleInputBuffer", cArg));
AudioQueue.newInput(mDataFormat, handleInputBuffer, self, null, "", 0, mQueuePtr);
}
};
With RoboVM 1.0.0-beta-3 I was finally able to get audio record and playback working. Not sure why the recording audio queue takes up to 20 seconds to stop, but here is some sample code that works in the simulator and on my iPhone 4:
Main Class:
import java.util.Vector;
import org.robovm.apple.coregraphics.*;
import org.robovm.apple.foundation.*;
import org.robovm.apple.uikit.*;
public class TestAudioQueueCrash extends UIApplicationDelegateAdapter
{
private UIWindow window = null;
private int clickCount = 0;
#Override
public boolean didFinishLaunching(UIApplication application, UIApplicationLaunchOptions launchOptions)
{
final UIButton button = UIButton.create(UIButtonType.RoundedRect);
button.setFrame(new CGRect(15.0f, 121.0f, 291.0f, 37.0f));
button.setTitle("Click me!", UIControlState.Normal);
button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener()
{
#Override
public void onTouchUpInside(UIControl control, UIEvent event)
{
if (clickCount == 0)
{
button.setTitle("Recording for 5 seconds... (SPEAK!)", UIControlState.Normal);
Runnable r = new Runnable()
{
public void run()
{
try
{
clickCount = 1;
AudioRecord record = new AudioRecord();
record.startRecording();
long when = System.currentTimeMillis() + 5000;
final Vector<byte[]> v = new Vector();
byte[] ba = new byte[3072];
while (System.currentTimeMillis() < when)
{
int n = 0;
while (n<3072)
{
int i = record.read(ba, n, 3072-n);
if (i==-1 || i == 0) break;
n += i;
}
if (n>0)
{
byte[] ba2 = new byte[n];
System.arraycopy(ba, 0, ba2, 0, n);
v.addElement(ba2);
}
}
System.out.println("DONE RECORDING");
record.release();
System.out.println("RECORDER STOPPED");
System.out.println("Playing back recorded audio...");
button.setTitle("Playing back recorded audio...", UIControlState.Normal);
AudioTrack at = new AudioTrack();
at.play();
while (v.size() > 0)
{
ba = v.remove(0);
at.write(ba, 0, ba.length);
Thread.yield();
}
at.stop();
button.setTitle("DONE", UIControlState.Normal);
System.out.println("FINISHED PIPING AUDIO");
}
catch (Exception x)
{
x.printStackTrace();
button.setTitle("ERROR: " + x.getMessage(), UIControlState.Normal);
}
clickCount = 0;
}
};
new Thread(r).start();
}
}
});
window = new UIWindow(UIScreen.getMainScreen().getBounds());
window.setBackgroundColor(UIColor.lightGray());
window.addSubview(button);
window.makeKeyAndVisible();
return true;
}
public static void main(String[] args)
{
try (NSAutoreleasePool pool = new NSAutoreleasePool())
{
UIApplication.main(args, null, TestAudioQueueCrash.class);
}
}
}
AQRecorderState:
/*<imports>*/
import java.util.Hashtable;
import org.robovm.rt.bro.*;
import org.robovm.rt.bro.annotation.*;
import org.robovm.rt.bro.ptr.*;
/*</imports>*/
/*<javadoc>*/
/*</javadoc>*/
/*<annotations>*//*</annotations>*/
/*<visibility>*/public/*</visibility>*/ class /*<name>*/AQRecorderState/*</name>*/
extends /*<extends>*/Struct<AQRecorderState>/*</extends>*/
/*<implements>*//*</implements>*/ {
protected static Hashtable<Integer, AudioRecord> mAudioRecords = new Hashtable<>();
protected static int mLastID = 0;
/*<ptr>*/public static class AQRecorderStatePtr extends Ptr<AQRecorderState, AQRecorderStatePtr> {}/*</ptr>*/
/*<bind>*/
/*</bind>*/
/*<constants>*//*</constants>*/
/*<constructors>*/
public AQRecorderState() {}
public AQRecorderState(AudioRecord ar)
{
this.mID(++mLastID);
mAudioRecords.put(mID(), ar);
}
/*</constructors>*/
/*<properties>*//*</properties>*/
/*<members>*/
#StructMember(0) public native int mID();
#StructMember(0) public native AQRecorderState mID(int mID);
/*</members>*/
/*<methods>*//*</methods>*/
public AudioRecord getRecord()
{
return mAudioRecords.get(mID());
}
public static void drop(int mStateID)
{
mAudioRecords.remove(mStateID);
}
}
AudioRecord:
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.lang.reflect.Method;
import org.robovm.apple.audiotoolbox.AudioQueue;
import org.robovm.apple.audiotoolbox.AudioQueue.AudioQueuePtr;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer.AudioQueueBufferPtr;
import org.robovm.apple.audiotoolbox.AudioQueueError;
import org.robovm.apple.coreaudio.AudioFormat;
import org.robovm.apple.coreaudio.AudioStreamBasicDescription;
import org.robovm.apple.coreaudio.AudioStreamPacketDescription.AudioStreamPacketDescriptionPtr;
import org.robovm.apple.coreaudio.AudioTimeStamp.AudioTimeStampPtr;
import org.robovm.apple.coreaudio.CoreAudio;
import org.robovm.apple.corefoundation.CFRunLoopMode;
import org.robovm.rt.VM;
import org.robovm.rt.bro.Bro;
import org.robovm.rt.bro.Struct;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.annotation.Library;
import org.robovm.rt.bro.annotation.Pointer;
import org.robovm.rt.bro.ptr.FunctionPtr;
import org.robovm.rt.bro.ptr.VoidPtr;
/*<annotations>*/#Library("AudioToolbox")/*</annotations>*/
public class AudioRecord
{
protected double mSampleRate;
protected AudioFormat mFormatID;
protected int mFormatFlags;
protected int mBytesPerPacket;
protected int mFramesPerPacket;
protected int mBytesPerFrame;
protected int mChannelsPerFrame;
protected int mBitsPerChannel;
protected AudioQueue mQueue = null;
private int kNumberBuffers = 3;
private PipedInputStream mPIS;
private PipedOutputStream mPOS;
private int mStateID = -1;
private boolean mRunning = false;
public AudioRecord() throws IOException
{
mSampleRate = 44100;
mFormatID = AudioFormat.LinearPCM;
mFormatFlags = CoreAudio.AudioFormatFlagIsPacked | CoreAudio.AudioFormatFlagIsSignedInteger;
mBytesPerPacket = 2;
mFramesPerPacket = 1;
mBytesPerFrame = 2;
mChannelsPerFrame = 1;
mBitsPerChannel = 16;
mPOS = new PipedOutputStream();
mPIS = new PipedInputStream(mPOS);
}
public static int getMinBufferSize(int sampleRate, int channelConfig, int audioFormat)
{
// TODO Auto-generated method stub
return 0;
}
public int deriveBufferSize(AudioQueue audioQueue, AudioStreamBasicDescription ASBDescription, double seconds)
{
int maxBufferSize = 0x50000;
int maxPacketSize = ASBDescription.getMBytesPerPacket();
System.out.println(3);
double numBytesForTime = ASBDescription.getMSampleRate() * maxPacketSize * seconds;
return (int)(numBytesForTime < maxBufferSize ? numBytesForTime : maxBufferSize);
}
public void release()
{
System.out.println("RECORD QUEUE STOPPING...");
mRunning = false;
mQueue.stop(true);
// mQueue.dispose(true);
System.out.println("RECORD QUEUE STOPPED");
try
{
mPOS.close();
mPIS.close();
AQRecorderState.drop(mStateID);
}
catch (Exception x) { x.printStackTrace(); }
}
public int read(byte[] abData, int i, int length) throws IOException
{
return mPIS.read(abData, i, length);
}
/*<bind>*/static { Bro.bind(AudioRecord.class); }/*</bind>*/
/*<constants>*//*</constants>*/
/*<constructors>*//*</constructors>*/
/*<properties>*//*</properties>*/
/*<members>*//*</members>*/
#Callback
public static void callbackMethod(
#Pointer long refcon,
AudioQueue inAQ,
AudioQueueBuffer inBuffer,
AudioTimeStampPtr inStartTime,
int inNumPackets,
AudioStreamPacketDescriptionPtr inPacketDesc
)
{
try
{
System.out.println("a");
AQRecorderState.AQRecorderStatePtr ptr = new AQRecorderState.AQRecorderStatePtr();
ptr.set(refcon);
System.out.println("b");
AQRecorderState aqrs = ptr.get();
System.out.println("c");
byte[] ba = VM.newByteArray(inBuffer.getMAudioData().getHandle(), inBuffer.getMAudioDataByteSize());
System.out.println("d");
aqrs.getRecord().receive(ba);
System.out.println("e");
}
catch (Exception x) { x.printStackTrace(); }
inAQ.enqueueBuffer(inBuffer, 0, null);
System.out.println("f");
}
private void receive(byte[] ba)
{
if (mRunning) try { mPOS.write(ba); } catch (Exception x) { x.printStackTrace(); }
}
public void startRecording() throws Exception
{
AudioStreamBasicDescription asbd = new AudioStreamBasicDescription(mSampleRate, mFormatID, mFormatFlags, mBytesPerPacket, mFramesPerPacket, mBytesPerFrame, mChannelsPerFrame, mBitsPerChannel, 0);
AudioQueuePtr mQueuePtr = new AudioQueuePtr();
AudioQueueBufferPtr mBuffers = Struct.allocate(AudioQueueBufferPtr.class, kNumberBuffers);
System.out.println(11);
AQRecorderState aqData = new AQRecorderState(this);
mStateID = aqData.mID();
System.out.println(12);
Method callbackMethod = null;
Method[] methods = this.getClass().getMethods();
int i = methods.length;
while (i-->0) if (methods[i].getName().equals("callbackMethod"))
{
callbackMethod = methods[i];
break;
}
FunctionPtr fp = new FunctionPtr(callbackMethod );
System.out.println(13);
VoidPtr vp = aqData.as(VoidPtr.class);
System.out.println(14);
AudioQueueError aqe = AudioQueue.newInput(asbd, fp, vp, null, null, 0, mQueuePtr);
System.out.println(CFRunLoopMode.Common.value());
System.out.println(aqe.name());
mQueue = mQueuePtr.get();
System.out.println(2);
int bufferByteSize = deriveBufferSize(mQueue, asbd, 0.5);
System.out.println("BUFFER SIZE: "+bufferByteSize);
AudioQueueBufferPtr[] buffers = mBuffers.toArray(kNumberBuffers);
for (i = 0; i < kNumberBuffers; ++i)
{
mQueue.allocateBuffer(bufferByteSize, buffers[i]);
mQueue.enqueueBuffer(buffers[i].get(), 0, null);
}
mRunning = true;
mQueue.start(null);
}
}
AQPlayerState:
/*<imports>*/
import java.util.Hashtable;
import org.robovm.rt.bro.*;
import org.robovm.rt.bro.annotation.*;
import org.robovm.rt.bro.ptr.*;
/*</imports>*/
/*<javadoc>*/
/*</javadoc>*/
/*<annotations>*//*</annotations>*/
/*<visibility>*/public/*</visibility>*/ class /*<name>*/AQPlayerState/*</name>*/
extends /*<extends>*/Struct<AQPlayerState>/*</extends>*/
/*<implements>*//*</implements>*/ {
protected static Hashtable<Integer, AudioTrack> mAudioTracks = new Hashtable<>();
protected static int mLastID = 0;
/*<ptr>*/public static class AQPlayerStatePtr extends Ptr<AQPlayerState, AQPlayerStatePtr> {}/*</ptr>*/
/*<bind>*/
/*</bind>*/
/*<constants>*//*</constants>*/
/*<constructors>*/
public AQPlayerState() {}
public AQPlayerState(AudioTrack ar)
{
this.mID(++mLastID);
this.mID2(mLastID);
mAudioTracks.put(mID(), ar);
}
/*</constructors>*/
/*<properties>*//*</properties>*/
/*<members>*/
#StructMember(0) public native int mID();
#StructMember(0) public native AQPlayerState mID(int mID);
#StructMember(1) public native int mID2();
#StructMember(1) public native AQPlayerState mID2(int mID2);
/*</members>*/
/*<methods>*//*</methods>*/
public AudioTrack getTrack()
{
return mAudioTracks.get(mID());
}
public static void drop(int mStateID)
{
mAudioTracks.remove(mStateID);
}
}
AudioTrack:
import java.lang.reflect.Method;
import java.util.Vector;
import org.robovm.apple.audiotoolbox.AudioQueue;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer;
import org.robovm.apple.audiotoolbox.AudioQueue.AudioQueuePtr;
import org.robovm.apple.audiotoolbox.AudioQueueBuffer.AudioQueueBufferPtr;
import org.robovm.apple.audiotoolbox.AudioQueueError;
import org.robovm.apple.audiotoolbox.AudioQueueParam;
import org.robovm.apple.coreaudio.AudioFormat;
import org.robovm.apple.coreaudio.AudioStreamBasicDescription;
import org.robovm.apple.coreaudio.CoreAudio;
import org.robovm.rt.bro.Bro;
import org.robovm.rt.bro.Struct;
import org.robovm.rt.bro.annotation.Callback;
import org.robovm.rt.bro.annotation.Pointer;
import org.robovm.rt.bro.ptr.BytePtr;
import org.robovm.rt.bro.ptr.FunctionPtr;
import org.robovm.rt.bro.ptr.VoidPtr;
public class AudioTrack {
public static final int MODE_STREAM = -1;
private int kNumberBuffers = 3;
private Vector<byte[]> mData = new Vector<>();
private int mStateID = -1;
private boolean mRunning = false;
protected double mSampleRate;
protected AudioFormat mFormatID;
protected int mFormatFlags;
protected int mBytesPerPacket;
protected int mFramesPerPacket;
protected int mBytesPerFrame;
protected int mChannelsPerFrame;
protected int mBitsPerChannel;
protected AudioQueue mQueue = null;
public AudioTrack()
{
mSampleRate = 44100;
mFormatID = AudioFormat.LinearPCM;
mFormatFlags = CoreAudio.AudioFormatFlagIsPacked | CoreAudio.AudioFormatFlagIsSignedInteger;
mBytesPerPacket = 2;
mFramesPerPacket = 1;
mBytesPerFrame = 2;
mChannelsPerFrame = 1;
mBitsPerChannel = 16;
}
public static int getMinBufferSize(int sampleRate, int channelConfigurationMono, int encodingPcm16bit)
{
// TODO Auto-generated method stub
return 0;
}
public int deriveBufferSize(AudioStreamBasicDescription ASBDescription, int maxPacketSize, double seconds)
{
int maxBufferSize = 0x50000;
int minBufferSize = 0x4000;
double numPacketsForTime = ASBDescription.getMSampleRate() / ASBDescription.getMFramesPerPacket() * seconds;
int outBufferSize = (int)(numPacketsForTime * maxPacketSize);
if (outBufferSize > maxBufferSize) return maxBufferSize;
if (outBufferSize < minBufferSize) return minBufferSize;
return outBufferSize;
}
/*<bind>*/static { Bro.bind(AudioTrack.class); }/*</bind>*/
/*<constants>*//*</constants>*/
/*<constructors>*//*</constructors>*/
/*<properties>*//*</properties>*/
/*<members>*//*</members>*/
#Callback
public static void callbackMethod(
#Pointer long refcon,
AudioQueue inAQ,
AudioQueueBuffer inBuffer
)
{
System.out.println("In Callback");
AQPlayerState.AQPlayerStatePtr ptr = new AQPlayerState.AQPlayerStatePtr();
ptr.set(refcon);
AQPlayerState aqps = ptr.get();
AudioTrack me = aqps.getTrack();
me.nextChunk(inAQ, inBuffer);
}
private void nextChunk(AudioQueue inAQ, AudioQueueBuffer inBuffer)
{
byte[] ba = null;
long when = System.currentTimeMillis() + 30000;
while (mRunning && System.currentTimeMillis() < when)
{
if (mData.size() > 0)
{
ba = mData.remove(0);
break;
}
try { Thread.yield(); } catch (Exception x) { x.printStackTrace(); }
}
if (ba == null) ba = new byte[0];
System.out.println("PLAYING BYTES: "+ba.length);
if (ba.length>0)
{
VoidPtr vp = inBuffer.getMAudioData();
BytePtr bp = vp.as(BytePtr.class); //Struct.allocate(BytePtr.class, ba.length);
bp.set(ba);
// inBuffer.setMAudioData(vp);
inBuffer.setMAudioDataByteSize(ba.length);
}
mQueue.enqueueBuffer(inBuffer, 0, null);
}
public void play()
{
final AudioTrack me = this;
Runnable r = new Runnable()
{
public void run()
{
AudioStreamBasicDescription asbd = new AudioStreamBasicDescription(mSampleRate, mFormatID, mFormatFlags, mBytesPerPacket, mFramesPerPacket, mBytesPerFrame, mChannelsPerFrame, mBitsPerChannel, 0);
AudioQueuePtr mQueuePtr = new AudioQueuePtr();
Method callbackMethod = null;
Method[] methods = me.getClass().getMethods();
int i = methods.length;
while (i-->0) if (methods[i].getName().equals("callbackMethod"))
{
callbackMethod = methods[i];
break;
}
FunctionPtr fp = new FunctionPtr(callbackMethod );
AQPlayerState aqData = new AQPlayerState(me);
mStateID = aqData.mID();
VoidPtr vp = aqData.as(VoidPtr.class);
// AudioQueueError aqe = AudioQueue.newOutput(asbd, fp, vp, CFRunLoop.getCurrent(), new CFString(CFRunLoopMode.Common.value()), 0, mQueuePtr);
AudioQueueError aqe = AudioQueue.newOutput(asbd, fp, vp, null, null, 0, mQueuePtr);
System.out.println(aqe.name());
mQueue = mQueuePtr.get();
int bufferByteSize = deriveBufferSize(asbd, 2, 0.5);
System.out.println("BUFFER SIZE: "+bufferByteSize);
System.out.println("Volume PARAM:"+(int)AudioQueueParam.Volume.value());
mQueue.setParameter((int)AudioQueueParam.Volume.value(), 1.0f);
mRunning = true;
AudioQueueBufferPtr mBuffers = Struct.allocate(AudioQueueBufferPtr.class, kNumberBuffers);
AudioQueueBufferPtr[] buffers = mBuffers.toArray(kNumberBuffers);
for (i = 0; i < kNumberBuffers; ++i)
{
mQueue.allocateBuffer(bufferByteSize, buffers[i]);
nextChunk(mQueue, buffers[i].get());
}
System.out.println("STARTING QUEUE");
mQueue.start(null);
System.out.println("QUEUE STARTED");
/*
System.out.println("RUNNING LOOP");
do
{
System.out.print(".");
CFRunLoop.runInMode(CFRunLoopMode.Default, 0.25, false);
System.out.print("#");
}
while (mRunning);
System.out.println("!!!");
CFRunLoop.runInMode(CFRunLoopMode.Default, 1, false);
System.out.println("DONE RUNNING LOOP");
mQueue.stop(true);
AQPlayerState.drop(mStateID);
System.out.println("QUEUE STOPPED");
*/
}
};
new Thread(r).start();
}
public void write(byte[] ba, int i, int length)
{
while (mData.size() > 10) Thread.yield();
System.out.println("SOUND IN: "+length+" bytes");
mData.addElement(ba);
}
public void stop()
{
System.out.println("STOPPING AUDIO PLAYER");
mRunning = false;
mQueue.stop(true);
AQPlayerState.drop(mStateID);
}
public void release()
{
// TODO Auto-generated method stub
}
}

Downloading file/files in Java. Multithreading, this works?

First, everyone needs to know i'm relatively new to Java coding. To be more precise i'm completely new to Object Oriented Programming.
To the question.
I am trying to create a download class that updates a progress bar it was given to show its progress. And possibly anything else I decide to give it in the future to update.
The issue currently is that, in my head, this shouldn't work. I can do anything i want on the "main" method and the GUI is still responsive and quick. In my experience in past programming, this is not possible unless i thread the GUI. Why is this?
Since it works, is this ok to do it this way?
Class Main
package atomicElectronics;
import java.io.IOException;
import atomicElectronics.physical.AtomFrame;
import atomicElectronics.utility.Download;
public class Initial {
static AtomFrame atomLauncher;
public static void main(String[] args) {
atomLauncher = new AtomFrame();
atomLauncher.start();
System.out.println(Integer.MAX_VALUE);
Download theDownload = new Download();
theDownload.fileProgressBar(atomLauncher.progressBar);
try {
theDownload.exicute("http://download.videolan.org/pub/videolan/vlc/last/win64/vlc-2.1.3-win64.exe", "C:\\Users\\TrinaryAtom\\AppData\\Roaming");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// TODO Add Download Methods
// theDownload.updateBarTotal(JProgressBar);
// theDownload.updateLabelSpeed(String);
// theDownload.updateLabelTotal(String);
// theDownload.addFile(File);
// theDownload.addFiles(Files);
}
}
Class AtomFrame
package atomicElectronics.physical;
import javax.swing.JFrame;
import java.awt.FlowLayout;
import javax.swing.JProgressBar;
public class AtomFrame extends JFrame{
public JProgressBar progressBar;
private static final long serialVersionUID = 4010489530693307355L;
public static void main(String[] args){
AtomFrame testFrame = new AtomFrame();
testFrame.start();
}
public AtomFrame(){
initializeComponents();
}
public void initializeComponents(){
this.setSize(400, 400);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Atom Launcher");
this.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
progressBar = new JProgressBar();
this.add(progressBar);
//this.pack();
}
public void start() {
this.setVisible(true);
}
public void close() {
this.dispose();
}
}
Class Download
package atomicElectronics.utility;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.swing.JProgressBar;
public class Download {
private static final int BUFFER_SIZE = 4096;
private JProgressBar fileProgressBar;
public Download() {
}
public void fileProgressBar(JProgressBar fileBar) {
fileProgressBar = fileBar;
}
public void exicute(String fileURL, String saveDir) throws IOException {
URL url = new URL(fileURL);
HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();
int responseCode = httpConn.getResponseCode();
// always check HTTP response code first
if (responseCode == HttpURLConnection.HTTP_OK) {
String fileName = "";
String disposition = httpConn.getHeaderField("Content-Disposition");
String contentType = httpConn.getContentType();
double contentLength = httpConn.getContentLength();
if (disposition != null) {
// extracts file name from header field
int index = disposition.indexOf("filename=");
if (index > 0) {
fileName = disposition.substring(index + 9,
disposition.length());
}
} else {
// extracts file name from URL
fileName = fileURL.substring(fileURL.lastIndexOf("/") + 1,
fileURL.length());
}
System.out.println("Content-Type = " + contentType);
System.out.println("Content-Disposition = " + disposition);
System.out.println("Content-Length = " + contentLength);
System.out.println("fileName = " + fileName);
// opens input stream from the HTTP connection
InputStream inputStream = httpConn.getInputStream();
String saveFilePath = saveDir + File.separator + fileName;
// opens an output stream to save into file
FileOutputStream outputStream = new FileOutputStream(saveFilePath);
double totalRead = 0;
int bytesRead = -1;
byte[] buffer = new byte[BUFFER_SIZE];
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalRead += bytesRead;
System.out.println((totalRead / contentLength) * 100);
fileProgressBar.setValue((int)((totalRead / contentLength) * 100));
}
outputStream.close();
inputStream.close();
System.out.println("File downloaded");
} else {
System.out.println("No file to download. Server replied HTTP code: " + responseCode);
}
httpConn.disconnect();
}
}
Suggestions:
Use a SwingWorker to do your background thread work.
Inside your SwingWorker, set its progress "bound" property via setProgress(int progress). The value should be between 1 and 100.
Don't have your SwingWorker/file downloader hold the JProgressBar or any Swing components.
Add a PropertyChangeListener to your SwingWorker and monitor changes in the progress property.
Never make your Swing fields (or most and and all fields) public. Limit access, and instead change object state via methods.
Read the tutorial Concurrency in Swing for the necessary details.
For example, the code below is a gross simplification and downloads no files, but should give you the idea:
import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
public class Initial {
static AtomFrame atomLauncher;
public static void main(String[] args) {
atomLauncher = new AtomFrame();
atomLauncher.start();
System.out.println(Integer.MAX_VALUE);
final Download theDownload = new Download();
theDownload.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if ("progress".equals(pcEvt.getPropertyName())) {
int progress = theDownload.getProgress();
atomLauncher.setProgress(progress);
}
}
});
theDownload.execute();
}
}
class AtomFrame extends JFrame {
// ********* should be private!
private JProgressBar progressBar;
private static final long serialVersionUID = 4010489530693307355L;
public static void main(String[] args) {
AtomFrame testFrame = new AtomFrame();
testFrame.start();
}
public void setProgress(int progress) {
progressBar.setValue(progress);
}
public AtomFrame() {
initializeComponents();
}
public void initializeComponents() {
this.setSize(400, 400);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Atom Launcher");
this.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
progressBar = new JProgressBar();
this.add(progressBar);
// this.pack();
}
public void start() {
this.setVisible(true);
}
public void close() {
this.dispose();
}
}
class Download extends SwingWorker<Void, Void> {
private static final long SLEEP_TIME = 300;
private Random random = new Random();
#Override
protected Void doInBackground() throws Exception {
int myProgress = 0;
while (myProgress < 100) {
myProgress += random.nextInt(10);
setProgress(myProgress);
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {}
}
return null;
}
}

Make threads usage efficient in Java

I have coded a simple application in Java that downloads particular images from a list of html links provided. Everything was working fine until I added the feature of having to download from a list of html links rather than just one. I had to implement the wait() and notify() methods which forced me to change the approach a little. Now, the downloads work fine, but the GUI does not update while the download is in progress.
I make the 1st thread wait from HTML.java and notify it at the end of DownloadImages.java. For this I had to invoke buttonPressed class as an object rather than a thread, which is why I think my GUI won't update.
Is there a way to simplify or make thread-usage more efficient in my code? Thanks in advance.
Here is skeleton of my code:
/*Test.java*/
package my;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Test extends javax.swing.JFrame {
public static buttonPressed bp;
public static boolean alldone;
/** Creates new form Test */
public Test() {
initComponents();
}
public static class buttonPressed implements Runnable {
Thread t1, t2;
buttonPressed() {
t1 = new Thread(this, "downloadAction");
t1.start();
}
public void suspendThread() {
System.out.println("suspended");
alldone = false;
}
public synchronized void resumeThread() {
System.out.println("resumed");
alldone = true;
notify();
}
public void run() {
String[] len = new String[]{/*list of urls*/};
for (int i = 0; i &lt len.length; i++) {
System.out.println("going times: " + i);
t2 = new Thread(new HTML(), "HTMLthread");
t2.start();
synchronized (this) {
while (!alldone) {
try {
wait();
} catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
}
private void downloadActionPerformed(java.awt.event.ActionEvent evt) {
bp = new buttonPressed();
try {
bp.t1.join();
} catch (InterruptedException e) {
System.out.println("Main Thread: interrupted");
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Test().setVisible(true);
}
});
}
private javax.swing.JButton download;
public static javax.swing.JProgressBar progress;
}
/*HTML.java*/
package my;
import java.util.ArrayList;
class HTML implements Runnable {
private Thread t3;
public HTML() {
Test.bp.suspendThread();
}
public void run() {
downloadHTML();
ArrayList xyz = parseHTML();
t3 = new Thread(new DownloadImages(xyz), "DownDecrypt");
t3.start();
}
private void downloadHTML() {
// Downloads the HTML file
}
private ArrayList parseHTML() {
// Parses the HTML file and gets links to images
return new ArrayList();
}
}
/*DownloadImages.java*/
package my;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
class DownloadImages implements Runnable {
static int current = 0, previous = 0;
static boolean speedFlag;
ArrayList<String> links = new ArrayList<String>();
private Thread t4;
public DownloadImages(ArrayList param1) {
this.links = param1;
speedFlag = true;
}
public void run() {
t4 = new Thread(new getSpeed(), "getSpeed");
t4.start();
download(links);
}
private void download(ArrayList<String> param1) {
String[] imgurl = new String[param1.size()];
URLConnection conn = null;
InputStream is = null;
ByteArrayOutputStream bais = null;
int prog;
for (int i = 0; i < param1.size(); i++) {
current = 0;
imgurl[i] = param1.get(i);
try {
conn = new URL(imgurl[i]).openConnection();
int fsize = conn.getContentLength();
is = new BufferedInputStream(conn.getInputStream());
bais = new ByteArrayOutputStream();
byte[] byteChunk = new byte[1024];
int n;
while ((n = is.read(byteChunk)) > 0) {
bais.write(byteChunk, 0, n);
current = current + 1024;
prog = (int) (current * 100.0 / fsize);
Test.progress.setValue(prog);
}
} catch (MalformedURLException ex) {
Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
byte[] imgBytes = bais.toByteArray();
try {
FileOutputStream fos = new FileOutputStream(i + ".jpg");
fos.write(imgBytes);
fos.flush();
fos.close();
} catch (FileNotFoundException ex) {
System.out.println("FileNotFoundException : " + ex);
} catch (IOException e) {
e.printStackTrace();
}
}
speedFlag = false;
// Resume the thread to start downloading the next link
Test.bp.resumeThread();
}
private static class getSpeed implements Runnable {
int kbytesPerSecond;
private final int fireTime;
public getSpeed() {
fireTime = 1000;
}
public void run() {
while (speedFlag) {
try {
Thread.sleep(fireTime);
} catch (InterruptedException ex) {
Logger.getLogger(getSpeed.class.getName()).log(Level.SEVERE, null, ex);
}
kbytesPerSecond = (((current - previous) / 1024) / (fireTime / 1000));
System.out.println(kbytesPerSecond);
previous = current;
}
}
}
}
As far as the GUI is concerned you need to read about Swing concurrency. In short, use SwingWorker.
Mind that you use old AWT stuff (java.awt.EventQueue).
I suggest you have an ExecutorService like Executors.newCachedThreadPool and submit() the tasks to it. Collect the Future objects so you know when they are done. This will be more efficient and manageable than creating Threads all over the place.
You can have just one pool like
static final ExecutorService POOL = Executors.newCachedThreadPool();
to submit a task
POOL.submit(new Callable<Void>() {
public Void call() throws InterruptedException {
while (speedFlag) {
Thread.sleep(1000);
kbytesPerSecond = (current - previous) / 1024;
System.out.println(kbytesPerSecond);
previous = current;
}
}
});
Even better for repeating tasks is to use a scheduled executor service.
static final ScheduledExecutorService POOL = Executors.newScheduledThreadPool(4);
Future task = POOL.scheduleAtFixedRate(new Runnable() {
public void run() {
kbytesPerSecond = (current - previous) / 1024;
System.out.println(kbytesPerSecond);
previous = current;
}
}, 1, 1, TimeUnit.SECONDS);
// to end the task
task.cancel(false);

Categories