I am using stopWithApp: true
When I launch my app the first time, TrackPlayer.registerPlaybackService() gets called with the function to add listeners.
If I close my app while audio is playing, it does stop as expected. However, it appears the listeners do NOT get removed.
When I re-launch my app, TrackPlayer.registerPlaybackService() is NOT called again, but it seems the function that adds listeners DOES. From this point on each event is handled twice. If I continue to repeat the process, each event gets handled 3, 4, 5 etc times.
Since I am using stopWithApp: true it is unclear if I need to manually call destroy(). But, I have tried with and without with similar results. Interestingly, if I DO call destroy, my function to add listeners is called right after, when the app is closing.
I have also tried to safe guard the call the TrackPlayer.setupPlayer(). I set my own flag to see if it has already been setup, and only call it if it has not. This is also curious, because I would expect my app to not remember this when it is re-launched, but it DOES. So it seems like somehow some part of the app is not really getting destroyed.
What else can I do to make sure events are only handled once per event?
Possibly related to #423 , although I haven't noticed this happening on iOS. Will test tomorrow.
I have re-created this in the example, with only logging added to detect it.
index.js
//Added code
console.log('TrackPlayer:', 'App Index');
//---
AppRegistry.registerComponent('example', () => App);
TrackPlayer.registerPlaybackService(() => require('./service'));
service.js
module.exports = async function() {
//Added code
console.log('TrackPlayer:', 'Playback Service');
TrackPlayer.addEventListener('playback-state', (data) => {
console.log('TrackPlayer:', 'playback-state', data);
})
//---
...
}
Filtered Output:
react-native run-android --variant=release
App builds, installs, and runs. Lands on initial screen
2019-02-06 08:56:25.332 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'App Index'
Select Playlist Example
2019-02-06 08:58:14.315 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'Playback Service'
2019-02-06 08:58:14.380 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 1 }
Press Play
Audio plays after a couple seconds
2019-02-06 08:58:52.287 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 0 }
2019-02-06 08:58:52.294 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 1 }
2019-02-06 08:58:52.312 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 0 }
2019-02-06 08:58:52.372 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 6 }
2019-02-06 08:58:54.264 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 3 }
While audio plays, press Android multitasking menu button.
Close app by swiping away.
Music stops.
No further logs yet.
Relaunch app from home screen.
Still no further logs, meaning neither App Index or Playback Service were called again yet.
Select Playlist Example
2019-02-06 09:02:05.372 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'Playback Service'
2019-02-06 09:02:05.416 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 1 }
2019-02-06 09:02:05.422 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 1 }
Note Playback Service logged again, even though App index never was. Results in doubled event handling
Press Play
2019-02-06 09:02:38.967 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 0 }
2019-02-06 09:02:38.984 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 0 }
2019-02-06 09:02:38.985 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 1 }
2019-02-06 09:02:38.993 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 1 }
2019-02-06 09:02:38.994 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 0 }
2019-02-06 09:02:39.002 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 0 }
2019-02-06 09:02:39.036 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 6 }
2019-02-06 09:02:39.040 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 6 }
2019-02-06 09:02:40.290 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 3 }
2019-02-06 09:02:40.306 28579-28620/? I/ReactNativeJS: 'TrackPlayer:', 'playback-state', { state: 3 }
Note all events are doubled.
As a further test I added this, which DID resolved the issue.
service.js
import TrackPlayer from 'react-native-track-player';
let didAddListeners = false;
module.exports = async function() {
if (didAddListeners) {
console.log('TrackPlayer:', 'Listeners have already been added. Do not add again.');
return;
}
didAddListeners = true;
console.log('TrackPlayer:', 'Playback Service');
TrackPlayer.addEventListener('playback-state', (data) => {
console.log('TrackPlayer:', 'playback-state', data);
})
...
};
My gut tells me this is a hack and probably has other issues... But, I guess I will do this for now.
Semi-related, can someone clarify if we need to use destroy() at all if we are using stopWithApp: true, and also do NOT have a stop button on our notifications?
No, the module will self-destroy when you have stopWithApp set to true.
It seems the Javascript Context remains in memory even after the app is closed and no services are running, that's why it registers the event twice.
To fix it, I think we'll have to implement a playback-destroy event that runs when the service stops. Here are some options I can think of:
eventEmitter.removeAllListeners('event-name'), but that might cause issues too (e.g. the service is destroyed manually and the app still has a few listeners bound to React components, if the service starts again, those listeners will be gone).destroy callback. Breaking change.playback-destroy event. If an event listener is added asynchronously, it wouldn't be removed.What do you think?
I am also finding this to be an issue in 1.1.2. Going to try the workaround presented above.
Additionally, I'm finding that on Android only, the playback-queue-ended event is being triggered when the player events are registered.
So many options! 馃槵
I鈥檓 honestly not sure what would be best overall. But this option seems logical to me.
Store all event listener references being created by the service and remove them using the playback-destroy event. If an event listener is added asynchronously, it wouldn't be removed.
If I understand correctly, any listeners added in the service function, would be automatically removed, which feels natural. And then any listeners added within components would need to be manually cleaned up, as is already documented.
This made me think of another option, that perhaps would only need documented instead of any real change.
I can鈥檛 test this until tomorrow, but would it work to have the user (developer) keep track of references to all the listeners added in the service function, and remove them all manually (if they exist) right before adding them?
I didn鈥檛 think to do this until I looked at the docs for adding listeners in components instead of in the service.
@curiousdustin That sounds like it would work but it doesn't seem possible to remove listeners currently. Have you had any luck?
I have not tried this within the service function, but my idea was to do it similar to how the component example in the docs does it.
this.onTrackChange = TrackPlayer.addEventListener('playback-track-changed', async (data) => { });
this.onTrackChange.remove();
If anyone is still having an issue with this, I just dealt with a similar (if not the same issue). TrackPlayer.setupPlayer() was not being called in the root of app and multiple instances were being created, there was nothing wrong with the listeners. Where are you calling TrackPlayer.setupPlayer() ?
We have just resolved this issue, we write as below.
https://github.com/react-native-kit/react-native-track-player/issues/683
Hope you will be fine.
Issue still exists in version 1.1.8.
Even with TrackPlayer.destroy() or use stopWithApp: true, listeners won't completely removed.
Using packages like react-native-exit-app does this for you
@Karstagg TrackPlayer.setupPlayer() should called once in root of the app or multiple instances should called in component?
Hi guys,
I got the same problem for two days before to see this issue.
The listeners in question came from the services file so I cannot use componentWillUnmount (for example).
The little 'hack' I did for others with the same problem:
`let listeners: EmitterSubscription[] = [];
module.exports = async function () {
listeners.map(listener => { listener.remove() });
listeners = [
TrackPlayer.addEventListener(Event.RemotePlay, async () => {
// Do what you want
}),
...You can add all your listeners here
];
};`
Thanks for the lib btw !