Timer generating new objects + thread every time scheduled

45 views
Skip to first unread message

k.ar...@googlemail.com

unread,
Sep 11, 2015, 9:57:53 AM9/11/15
to CodenameOne Discussions
IDE: NetBeans
Desktop OS : Windows 8.1 64 bit
Simulator any
Device N/A

Hello!

I am trying to implement a looped sound (testing only at the moment) the following way (there may be a better way, but this is not the point for me now):
a task (check-task) is running every 500 ms checking if the sound is still running. If it checks that the sound is about to finish within these 500ms it cancels itself and schedules another task (play-task) to start the other sound once this sound has finished (I am doing this more complicated, in order to tackle issues which could occur if the device is busy or does not finish right on time). This play-task is just running once and after starting the sound to play it starts the timer to schedule the check-task (every 500 ms). This goes on forever.

The problem is, that while debugging I can see that every time when scheduling the play-task and the loop starts again, a timer object is added, a thread object is added and a "JFXMedia Player EventQueueThread" is running. I have tested this for 10 minutes and had hundreds of objects shown in the debugger. 
Is this a problem? Is this something which has to do with Netbeans? With the way Codename One implements Timer? Or is this my code? I have tried this code (without playing stuff) in a normal java application and there is just one timer running all the time.

Here is my code (I have tried to add only the vital parts). If someone could help me with this issue this would be great!

class MyMedia{
        private Media m_Media;
        private InputStream m_InputStream;
        private Runnable m_Callback;
        private String m_Mimetype;

        public MyMedia(String name, String mimetype) throws Exception{
            m_InputStream = theme.getData(name);
            m_Callback = new SoundFinished(this);
            m_Mimetype = mimetype;
            m_Media = create();
        }
        
        public int getDuration(){
            return m_Media.getDuration();
        }
        
        public int getTime(){
            return m_Media.getTime();
        }
            
        public boolean isPlaying(){
            return m_Media.isPlaying();
        }
        
        Media create() throws Exception{
            return MediaManager.createMedia(m_InputStream, m_Mimetype, m_Callback);   
        }
        
        public Media getMedia(){
            return m_Media;
        }
        public void reload() throws Exception{ 
            m_InputStream.reset();
            m_Media = create();         
        }
    }
    //called once the sound is finished, reloads it to play it again later
    class SoundFinished implements Runnable{

        MyMedia m_Media;
        
        public SoundFinished(MyMedia m) {
           m_Media = m;
        }

        
        
        public void run() {
            try{
                m_Media.reload();
            }
            catch(Exception ex){
                
            }            
        }
        
    }
    //plays a sound reschedules the timer to check
    class PlaySoundTask extends TimerTask{

        MyMedia toPlay;
        MyMedia playNext;
        public PlaySoundTask(MyMedia play, MyMedia playnext){
            toPlay = play;
            playNext = playnext;
        }
        
        @Override
        public void run() {
            try{
                toPlay.getMedia().play();
                if (add > 0)
                    toPlay.getMedia().setTime(add);
                this.cancel();
                
                m_Timer.schedule(new CheckSoundTask(toPlay, playNext), CheckInterval, CheckInterval);
                toPlay = playNext = null;
                
            }
            catch(Exception ex){
                
            }
        }
    }
    //checks if sound is still playing, if almost finished schedules PlaySoundTask and cancels itself
    class CheckSoundTask extends TimerTask{

        MyMedia m_Media;
        MyMedia m_Other;
        
        public CheckSoundTask(MyMedia m, MyMedia other){
            m_Media = m;
            m_Other = other;
        }
                
        
        @Override
        public void run() {
            int timeForNextAction = getTimeForNextAction(m_Media);
            if (timeForNextAction != CheckInterval){
                this.cancel();
                
                m_Timer.schedule(new PlaySoundTask(m_Other, m_Media), timeForNextAction);     
                m_Media = m_Other = null;
            }
                
        }
        
    }
    
    int getMediaTimeToPlay(MyMedia m){
        if (!m.isPlaying()){
            return 0;
        }
        return Math.max(0, m.getDuration() - m.getTime());
    }
    

    int getTimeForNextAction(MyMedia m){
        int mTimeToPlay = getMediaTimeToPlay(m);
        if (mTimeToPlay  < CheckInterval)
            return mTimeToPlay;
        return CheckInterval;
    }
    
    
    //called when hitting a button in the application
    public void actionPerformed(ActionEvent evt) {
         try
        {
            m_1 = new MyMedia(filename,mimetype);
            m_2 = new MyMedia(filename,mimetype);
            m_1.getMedia().play();
            m_Timer.schedule(new CheckSoundTask(m_1, m_2), CheckInterval, CheckInterval);
            
            //timer.schedule(new MyTimerTask(), );

        }
        catch(Exception ex){
            Dialog.show("Warning", "Load failed", "okay", null);
        }
    }


Shai Almog

unread,
Sep 12, 2015, 12:53:29 AM9/12/15
to CodenameOne Discussions, k.ar...@googlemail.com
Hi,
notice that the implementation of the media API's is REALLY different between mobile and simulator. iOS/Android work very differently in this regard and we map directly to their native abilities (which are surprisingly superior to JavaFX's crud which doesn't even support https).
The Timer implementation in the simulator is the JavaSE timer so its 100% compatible with Java, only on iOS will you run into our Timer implementation which is indeed simpler.
There might be a small leak here or it might just be the GC getting lazy with temporary instance objects, I think Chen might be better equipped to answer that question.

k.ar...@googlemail.com

unread,
Sep 14, 2015, 2:28:20 PM9/14/15
to CodenameOne Discussions, k.ar...@googlemail.com
Hi!

Thanks for your answer.  I think I gonna retest this over a longer period of time. Maybe the GC collects at some point.  I noticed that when clicking on this force-collect button nothing happens. But this may not mean anything. Maybe there is a leak. But on the other hand timer objects are clearly not created somewhere. As I understand, all media objects are cleared once they have played to end. Or do I have to do anything with them after they completed? I am at the moment generating those Media elements on the  thread which is not EDT. I also tried to use isEDT and callSerially to create those Media elements on the EDT. But this did not change a thing. (Maybe there is also no  reason in doing so...)

So anyway, thanks for now. If you, or your colleague has any more thoughts about this, I would  be very thankful. I am well aware, that I can achieve this in another way (even with only one task) but I am thinking about bringing a huge project  to codename one. And I want be sure that those kinds of things work flawless. And (even though my system did not break) it seems that this does not work flawless. 

Regards!

Shai Almog

unread,
Sep 14, 2015, 11:14:08 PM9/14/15
to CodenameOne Discussions, k.ar...@googlemail.com
Hi,
Chen might chime in later (its vacation time around here).
As far as I recall media should collect after it finishes. But I strongly suggest testing media on devices, its one of those things where our control is very limited due to native platform opacity. So the media implementation differs a lot between device and simulator.
Reply all
Reply to author
Forward
0 new messages