0

I am using this code to play a sound

     final MediaPlayer mp = MediaPlayer.create(this, R.raw.sound);
        mp.setOnCompletionListener(new OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer mp) {
                mp.release();
            }

        });  

It works fine on its own, however there was a problem after I added an animation that extends ImageView, which refreshes(by calling handler.postDelayed) the image resource at an interval about 30ms to create animation. The problem is that when the animation starts, it terminates the the playing of the sound. Here is the code for the Runnable that refreshes the ImageView.

 private Runnable runnable = new Runnable () {


public void run() {
    String name = "frame_" + frameCount;
    frameCount ++;
    int resId = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
    imageView.setImageResource(resId);  
    if(frameCount < totalFrameCount) {
        mHandler.postDelayed(runnable, interval);
    }       
}

};

I also tried to use a thread that calls the anmiationView.postInvalidate to do the animation, however it has the same problem. Please help. Thanks

Edit: It looks like the problem is due to WHEN the animation is called. Previously I called it in the onActivityResult of the activity. Looks like this is not the right place to call. Now I put the animation view in a popupWindow and play it there, it works properly. Not sure exactly why.

Ray
  • 16,025
  • 5
  • 31
  • 51
  • If adding the animation caused the problem, then post that code. – Sound Conception Dec 30 '13 at 04:01
  • added the code. Btw I am suspecting that the problem is due to the app was reading both sound file and the image files. However the image files are too big for me to load all of them into memory. – Ray Dec 30 '13 at 04:17
  • Yes.. if the system is running out of memory, it will kill off and reclaim it from elsewhere, such as the media player. – Sound Conception Dec 30 '13 at 04:30

2 Answers2

1

in handler's comments :

"A Handler allows you to send and process {@link Message} and Runnable objects associated with a thread's {@link MessageQueue}. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue."

so, the problem may be caused by both of animation and media playing operations are in the same message queue own by which thread create the handler (let's say the main thread).

if the animation loops for ever, then the media player will hardly get any chance to run.

you could take it a try with HandlerThread, the thread will contain a new looper for the handler created from it, all the runnables added to that handler will be running in another individual thread.

the animation thread and the media play thread should be running in the different threads not scheduling in the same one.

hope, it helps.

the HandlerThread usage and some discuss looks like this :

How to create a Looper thread, then send it a message immediately?

Community
  • 1
  • 1
digger.c.g
  • 11
  • 4
  • I have created 2 threads, one to post the refresh request for the animation, the other plays the sound. Also now I use the SoundPool instead of MediaPlayer, the SoundPool loads the entire audio file to memory before playing. Strangely now the animation only starts after the sound playing is finished. – Ray Dec 30 '13 at 16:11
0

maybe it is caused by your miss arranged codes, i take it a try on my nexus 4 with android version 4.4.2, even no any cache tech, the animation and music works like a charm... here is the major codes :

public class MainActivity extends Activity implements View.OnClickListener {

protected static final String TAG = "test002" ;
protected static final int UPDATE_ANI = 0x0701;
protected static final int UPDATE_END = 0x0702;
protected static final int[] ANI_IMG_IDS = {R.raw.img1, R.raw.img2, R.raw.img3, R.raw.img4,
        R.raw.img5, R.raw.img6, R.raw.img7};
protected static final int[] BTN_IDS = {R.id.btnStart, R.id.btnStop};
protected android.os.Handler aniHandler = null; // async update
protected boolean isAniRunning = false ;
protected int     aniImgIndex = 0 ;
protected ImageView aniImgView = null ;
protected MediaPlayer mediaPly = null ;

// animation timer
class AniUpdateRunnable implements Runnable {
    public void run() {
        Message msg = null ;
        while (!Thread.currentThread().isInterrupted() && isAniRunning) {
            msg = new Message();
            msg.what = UPDATE_ANI;
            aniHandler.sendMessage(msg);

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break ;
            }
        }

        msg = new Message() ;
        msg.what = UPDATE_END ;
        aniHandler.sendMessage(msg) ;
    }
}

protected void prepareMediaPlayer(MediaPlayer mp, int resource_id) {
    AssetFileDescriptor afd = getResources().openRawResourceFd(resource_id);

    try {
        mp.reset();
        mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
        afd.close();
        mp.prepare();
    } catch (IllegalArgumentException e) {
        Log.d(TAG, "IlleagalArgumentException happened - " + e.toString()) ;
    } catch(IllegalStateException e) {
        Log.d(TAG, "IllegalStateException happened - " + e.toString()) ;
    } catch(IOException e) {
        Log.d(TAG, "IOException happened - " + e.toString()) ;
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // init : buttons onclick callback
    {
        Button btn;
        int i;
        for (i = 0; i < BTN_IDS.length; i++) {
            btn = (Button) findViewById(BTN_IDS[i]);
            btn.setOnClickListener(this);
        }
    }

    // init : update animation handler callback
    {
        aniHandler = new Handler() {
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case UPDATE_ANI:
                        updateAniImages();
                        break ;
                    case UPDATE_END:
                        updateAniEnd();
                        break ;
                    default:
                        break;
                }
            }
        };
    }

    // init : prepare image view
    {
        aniImgView = (ImageView)findViewById(R.id.imgAni) ;
        mediaPly = MediaPlayer.create(this, R.raw.happyny) ;
        mediaPly.setLooping(true);
    }
}

protected void updateAniImages() {
    if(aniImgIndex >= ANI_IMG_IDS.length) {
        aniImgIndex = 0 ;
    }

    InputStream is = getResources().openRawResource(ANI_IMG_IDS[aniImgIndex]) ;
    Bitmap bmp = (Bitmap) BitmapFactory.decodeStream(is) ;
    aniImgView.setImageBitmap(bmp);

    aniImgIndex++ ;
}

protected void updateAniEnd() {
    aniImgIndex = 0 ;
    aniImgView.setImageBitmap(null);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.btnStart:
            isAniRunning = true ;
            // no re-enter protectiion, should not be used in real project
            new Thread(new AniUpdateRunnable()).start();
            mediaPly.start();
            break;
        case R.id.btnStop:
            isAniRunning = false ;
            mediaPly.stop();
            prepareMediaPlayer(mediaPly, R.raw.happyny);
            break;
        default:
            break;
    }
}
}

the major project codes and test apk should be find here :

apk installer

source code

digger.c.g
  • 11
  • 4
  • Thanks for the code, however it appears that the problem was not due to the animation logic, rather it is the time the animation and sound playing is called. I have updated my question wit more details. – Ray Jan 06 '14 at 22:17
  • yeah i suspect the similar case when the milliseconds is below 1000 then the audiocall animation etc might be broke. – gumuruh Jan 01 '22 at 07:43