I'm running through a slideshow in an infinite loop. Every 'x' amount of time, I switch the video playing. First I stop the video, and then I load the new video. My app is crashing when I set the video path, with an error that there is an illegal state. It only happens once every couple thousand uses, so I cannot replicate it. That is, if I leave the app open for 24 hours on 100 devices, it will happen once per day on one device.
runOnUiThread() {
if (mVideoView!!.isPlaying) {
mVideoView!!.stopPlayback()
}
mVideoView!!.setVideoPath(filePath)
mVideoView!!.setOnErrorListener { mp, what, extra ->
LOG.e("Media player error: $what extra: $extra")
// Something's wrong, try again
slideshowHandler.removeCallbacksAndMessages(null)
slideshowHandler.postDelayed(slideshowRunner, 0)
true
}
if (startTime > 0) {
mVideoView!!.seekTo(startTime)
}
mVideoView!!.start()
}
}
java.lang.IllegalStateException
at android.media.MediaPlayer.prepareAsync(Native Method)
at android.widget.VideoView.openVideo(VideoView.java:356)
at android.widget.VideoView.setVideoURI(VideoView.java:265)
at android.widget.VideoView.setVideoURI(VideoView.java:248)
at android.widget.VideoView.setVideoPath(VideoView.java:239)
Any thoughts on what this could be? What state is invalid? Am I doing things in the wrong order?
I can't find any similar issues on SO. The app is running lollipop 5.1 (has to).
Edit: I'm fixing this right now using a try/catch. Seems like there is some underlying bug in Android that is causing this.
Related
for awhile now, I am working on a media playing app. In this app, I also programmed a little 5 Band EQ using mainly this code:
try
{
AppPreferencesClass ap = new AppPreferencesClass(ctx);
if (Activity_Player.eq != null)
{
Activity_Player.eq.Dispose();
}
Activity_Player.eq = new Android.Media.Audiofx.Equalizer(0, Activity_Player.mediaPlayerSessionId);
Activity_Player.eq.SetEnabled(true);
await Task.Run(() =>
{
if (Activity_Player.EqActive)
{
if (ap.getAwesomeSound())
{
Activity_Player.eq.SetBandLevel(0, Convert.ToInt16(Activity_Equalizer.awesomesound0));
Activity_Player.eq.SetBandLevel(1, Convert.ToInt16(Activity_Equalizer.awesomesound1));
Activity_Player.eq.SetBandLevel(2, Convert.ToInt16(Activity_Equalizer.awesomesound2));
Activity_Player.eq.SetBandLevel(3, Convert.ToInt16(Activity_Equalizer.awesomesound3));
Activity_Player.eq.SetBandLevel(4, Convert.ToInt16(Activity_Equalizer.awesomesound4));
}
else
{
Activity_Player.eq.SetBandLevel(0, Convert.ToInt16(ap.getEQ0()));
Activity_Player.eq.SetBandLevel(1, Convert.ToInt16(ap.getEQ1()));
Activity_Player.eq.SetBandLevel(2, Convert.ToInt16(ap.getEQ2()));
Activity_Player.eq.SetBandLevel(3, Convert.ToInt16(ap.getEQ3()));
Activity_Player.eq.SetBandLevel(4, Convert.ToInt16(ap.getEQ4()));
}
}
});
}
catch
{
}
For many days, this worked just fine but out of NO WHERE, the catch block sometimes gets activated. But only occasionally.On other times, try works fine but there are just no more changes to the audio being played. This is odd enough, since I never changed anything on this code after it starting working.
I then tried another phone (Samsung S4) on my code and the eq worked just perfectly.
So this got me googleing and I think I might have heard that there can only be as many audiosession IDs after you just would run out. I tested and the audio session ID used here is somewhere at 74,000.
So this could be an issue I thought but this would easialy be tested because I already had this very app running in the google play store just an older version of it. I am 100 percent positive, that in this version the EQ worked on my phone. Otherwise I would have not uploaded that version.
Anyway, I downloaded my old app from the play store and here we go:
It doesnt work anymore. The EQ in the old version also has simply NO effect on the audio. While ofcourse on my other phones this old version works perfectly.
Before I am going to reset my own personal phone I wanted to ask you guys if this could be infact the case.
Another thing is, that I am using many static variables in order to get the EQ to work right. Actually, the variable EQ itself is static. Do maybe static variables sort of leave a "trace" behind and maybe I have set the eq up just "too" many times? Although I am disposing of the object before intialising it again (see in my code).
Summing up:
1.) Can there maybe be a maxmium number of EQ or AudioSessionIDs and I have passed those?
2.) Can creating static variables over and over again in my code cause a memory leak so big, even deinstalling the app doesnt do anything?
Thank you!
This is the error log:
11-20 12:16:43.736 E/AudioEffect(16990): set(): AudioFlinger could not create effect, status: -38
11-20 12:16:43.736 E/AudioEffects-JNI(16990): Error setting AudioEffect
11-20 12:16:43.737 E/AudioEffect-JAVA(16990): Error code -19 when initializing AudioEffect.
Thread started: #311-20 12:16:43.745 V/MediaPlayerNative(16990): unrecognized message: (6, 0, 0)
After 2 days of googeling and trying evetything out, here is the issue:
NOT CALLING RELEASE() will have you eventually have to REBOOT your phone. It wont allow too many instances of an EQ to be set.
Solution:
if (eq != null)
{
eq.Release();
}
I'm making an Audio Player app that will load some predefined audios from inside its raw resource folder.
I've sent the test APK for some people to test it, and it worked on 99% of the cases. But then, I started to get some crash reports on Crashlytics for a single device, the OnePlus A5000, running Android 8.1.0.
The stacktrace on crashlytics is as follows:
Fatal Exception: java.lang.IllegalStateException
at android.media.MediaPlayer._setDataSource(MediaPlayer.java)
at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1270)
at com.example.home.MediaPlayerHolder.loadMedia(MediaPlayerHolder.kt:56)
at com.example.home.HomePresenter.playSound(HomePresenter.kt:26)
at com.example.home.HomeFragment.onPlaySelected(HomeFragment.kt:178)
at com.example.home.SoundItemAdapter$ViewHolder$bind$1.onClick(SoundItemAdapter.kt:30)
package names were changed for privacy reasons
This is happening almost every time he clicks on the sound item play button. It also happens on the first time he clicks on any sound item.
I've tried to reproduce this crash on some devices, all of then have API <= 25, but I had no success doing so. All of them ran without an issue. I'm clueless of the probable cause of this error, or even if it's an edge case problem, or if there's indeed something wrong with my code.
This is how I'm loading the audio on the MediaPlayer:
if (mediaPlayer?.isPlaying == true) {
mediaPlayer?.release()
mediaPlayer = null
}
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer()
}
val fileDescriptor = context.resources.openRawResourceFd(resource)
mediaPlayer?.setDataSource(fileDescriptor.fileDescriptor, fileDescriptor.startOffset, fileDescriptor.length)
fileDescriptor.close()
mediaPlayer?.prepare()
mediaPlayer?.start()
The reason was you can't start services in the background anymore after API 26. So you have to start ForegroundService above API 26.
I am using cordova 6.4.0 with cordova-plugin-media for streaming radio-stations in an Android Application. Unfortunately there is a case, where the application is not responding properly anymore.
Let's say the user wants to stream a radiostation, but while the stream is loading, he wants to abort it (for example because the stream is down, or taking very long to load).
In this case I am not able to cancel the process!
media = new Media("http://direct.franceinfo.fr/live/franceinfo-midfi.mp3?ID=f9fbk29m84", mediaPlayerSuccess, mediaPlayerFail, mediaPlayerStatus);
media.play();
Now I want to cancel the process of buffering the stream, but I'm not able to. The functions:
media.pause();
media.stop();
are throwing error messages in the ADB-log and are calling the mediaPlayer-onError callback.
D/AudioPlayer( 3362): AudioPlayer Error: pausePlaying() called during invalid state: 1
...
D/AudioPlayer( 3362): AudioPlayer Error: stopPlaying() called during invalid state: 1
The media.release() command stops the loading of the stream! However just releasing the stream without stopping it, causes other, rather big problems:
Most of the times the system reacts just very slow and hangs a few seconds, if you call media.release() on a media-object. But if you do this often, the system completly freezes. Meaning it does not accetp remote-control commands anymore.
The Adb-log is still working, but does not show any errors in this case. Only the POWER-Button is still working (it locks and unlocks the screen). The only way to recover from this screwed-up state, is to reboot the device.
How am I supposed to cancel a Media-stream if it is not playing, yet? Is this a bug in the plugin?
Attached is the code-snippet, that I use to handle the media-streaming-logic. Like described above... it basically works, but it slows down or even freezes device, if you call it multiple times.
function radioControl(action, media_src){
//media_src is a webradio-streamurl.
if(action == 'play') {
// Initial Play
if(media === null){
mediaCreateObject(media_src);
}
// If we get PLAY but on antoher station
else if(media.src != media_src){
mediaReleaseRessources();
mediaCreateObject(media_src);
}
//interrupt_timer = false;
if(media === null){
mediaCreateObject(media_src);
}
media.play();
}
else if (action === 'pause') {
//If we get "pause", but it didn't even start yet
if(media._duration == -1){
mediaReleaseRessources();
}
else{
media.pause();
}
}
}
function mediaCreateObject(media_src){
media = new Media(media_src, mediaPlayerSuccess, mediaPlayerFail, mediaPlayerStatus);
}
function mediaReleaseRessources(){
media.release();
}
I found out, that this is not a cordova issue, but an 8 year-old (!) android-bug, that was never fixed. See here:
https://code.google.com/p/android/issues/detail?id=959
MediaPlayer "crash" (deadlocks the calling thread) when resetting or releasing an unused MediaPlayer
Basically the problem is: If you try to "release" a media-object that is not playing (yet), it will deadlock the calling thread, which causes the freezing that I have mentioned in the question. Unfortunately they never fixed this bug, but just marked it as "obsolete". In Android 5.1.1. the bug apparently is still there. Maybe they fixed it in later versions.
I have made a rather ugly workaround for this problem, but it is working. Basically what I did is:
We save every media-object in a javaScript-object. If the user stops it, while it plays, we can just stop and delete the object. But if it is not playing, we leave this media-object in this javaScript-object media_objects = {};
Also we save the currently active_media stream in a variable.
If cordova calls the mediaPlayerStatusChange-callback we loop through the media_objects and check if the status of one of the "pending"-objects has now changed to "running". - Cordova justs calls the media-status-change-callback without any indictation what media-object exactly just changed the state. That is unfortunate, so we have to check if one of the pending-"obsolete" objects now started playing. If so, we can stop and release it. (If the object is actually playing, stop and release works like intended - only if it's not playing, it causes the crash)
function mediaPlayerStatusChange(status){
mediaReleaseRessources();
// handle status change....
// ......
}
function mediaReleaseRessources(){
for(var key in media_objects) {
// We can only stop-and release an object, if it is playing
// If an object started playing, the "_duration"-value is != -1
if(key !== active_media && media_objects[key]._duration != -1) {
media_objects[key].stop();
media_objects[key].release();
delete media_objects[key];
}
}
}
This solution works for me, however I am still interested in a better and cleaner way to handle multiple media-streams in cordova.
I turned off all animations on developer options.
But I still get this exception when trying to click on one of the buttons.
My app is indeed active and not idle entirely, but I can't change it.
android.support.test.espresso.AppNotIdleException: Looped for 6930
iterations over 60 SECONDS. The following Idle Conditions failed .
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
at java.lang.Thread.getStackTrace(Thread.java:580)
at android.support.test.espresso.base.DefaultFailureHandler.getUserFriendlyError(DefaultFailureHandler.java:92)
at android.support.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:56)
at android.support.test.espresso.ViewInteraction.runSynchronouslyOnUiThread(ViewInteraction.java:184)
at android.support.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:115)
at android.support.test.espresso.ViewInteraction.perform(ViewInteraction.java:87)
I have been struggling with this problem for the last few days.
Here is a method that I used to identify "violators":
private void dumpThreads() {
int activeCount = Thread.activeCount();
Thread[] threads = new Thread[activeCount];
Thread.enumerate(threads);
for (Thread thread : threads) {
System.err.println(thread.getName() + ": " + thread.getState());
for (StackTraceElement stackTraceElement : thread.getStackTrace()) {
System.err.println("\t" + stackTraceElement);
}
}
}
In my case, Facebook SDK was using the AsyncTask thread pool.
In my case this problem was happening with AnimatedVectorDrawable and caused by an objectAnimator that was set to repeat the animation infinitely (android:repeatCount="infinite"). .
The problem was also present only on older platform versions. Tests were perfectly working on Android 9 while the problem was reproducible on Android 5 and 6 (not sure about 7 and 8 at the moment).
I believe, the root cause of the problem is the same as for indeterminate progress bars (covered in this SO question). However, I haven't found any nice solution, only workarounds.
One of the workarounds is to detect that the animation is turned off (animator duration is 0) in the setting and don't start the animation. Of course, this only works for platform versions where the animation does not autostart.
private fun startIconAnimation(imageView: ImageView) {
if (areAnimationsEnabled()) {
(imageView.drawable as Animatable).start()
}
}
private fun areAnimationsEnabled(): Boolean {
val animatorDurationScale = Settings.Global.getFloat(
requireContext().contentResolver,
Settings.Global.ANIMATOR_DURATION_SCALE,
1.0f
)
return animatorDurationScale != 0.0f
}
Note: API level 26 introduced a static method ValueAnimator.areAnimatorsEnabled() which would have been handy if the problem was not happening only on the older platform versions.
As per answer by MaciejGórski to the similar question:
It was a bug in my app code, where SwipeRefreshLayout animated
itself indefinitely. Due to a bug in this
component,
the refresh state was not even showing.
In my case I clicked "save" button running long request in a test. To complete the test I had to add idle() after save() method. But often it was not enough, a loader continued drawing and the test failed. Then I increased a duration: idle(10000) and got the error: androidx.test.espresso.AppNotIdleException: Looped for 238 iterations over 10 SECONDS. The following Idle Conditions failed DELAY_HAS_PAST..
I wrote: idle(5000). Instead of Espresso I use Kakao and Kaspresso.
I have being upgrading an application to use the new Mobile Android GNSK but I have noticed that using the new MusicID-Stream is a little bit tricky. If the "identifyAlbumAsync" method get executed before the "audioProcessStart" method(since this need to be executed in a different thread), the application just crashes. In the Gracenote Demo application, the "audioProcessStart" method is continuously running so there is no need to synchronize its execution with the "identifyAlbumAsync" method call. Is it the way it is supposed to be used? It will be convenient if the application didn't crashed at least when the methods are not executed in order. Also in our application, we don't want to have the "audioProcessStart" method continuously like it is done in the demo application. We only want to run the "audioProcessStart" method when the user request identification and when the song playing gets identified , we want to stop the audio processing by calling "audioProcessStop". Is there an easy way to do this? Right now, we are getting the Thread where "identifyAlbumAsync" is running to sleep for 2 seconds in order to make sure that the Thread where the "audioProcessStart" method is supposed to run has time to get executed. Thank you in advance for your prompt response
In the upcoming 1.2 release, IGnMusicIdStreamEvents includes a callback that signals audio-processing has started, and an ID can be synced with this, e.g.:
#Override
public void musicIdStreamProcessingStatusEvent( GnMusicIdStreamProcessingStatus status, IGnCancellable canceller ) {
if (GnMusicIdStreamProcessingStatus.kStatusProcessingAudioStarted.compareTo(status) == 0) {
try {
gnMusicIdStream.identifyAlbumAsync();
} catch (GnException e) { }
}
}
Thanks for the feedback, you're right about this issue. Unfortunately right now sleeping is the best solution. But we are adding support for an explicit sync event in an upcoming release, please stay tuned.