Code Monkey home page Code Monkey logo

Comments (20)

ryanheise avatar ryanheise commented on May 29, 2024 2

I've just published 0.0.10 with the ability to await AudioService.start which completes when the background task is ready to receive messages. This may give you another option to pass data to the background task on start. For example, on the client side:

bool success = await AudioService.start(...);
if (success) {
  AudioService.customAction('url', url);
}

And in the background task:

Completer urlCompleter = Completer();
AudioServiceBackground.run(
   ...
  onCustomAction: (String name, dynamic arguments) {
    switch (name) {
      case "url":
        String url = arguments;
        ...
        break;
    },
  }
);

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024 1

To address the other part of your question, you can't directly pass any arguments to the background task function, so your background task needs to internally know what it wants to play. However, most apps typically store their state using some form of persistence (shared preferences[1] or a database[2]), so if the current URL is stored in a shared preference, for example, your background task will be able to load that shared preference and fetch the URL.

Future release may provide some alternative options, but this is the way to do it for now.

[1] https://pub.dartlang.org/packages/shared_preferences
[2] https://pub.dartlang.org/packages/sqflite

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024 1

From my perspective, the biggest challenge is not the communication between the isolates, but what to do when the main isolate is killed. The background will continue to play the current item, or even a queue of items, but what happens at the end of a queue? The music simply stops playing until a user revives the app and clicks on something else to play. Is this the correct understanding?

On reaching the end of the queue, you have implementation freedom to decide whether to keep the service alive (e.g. switch to the paused state), or complete the future returned by onStart thereby shutting down the service.

On Android, there is a third option that would be nice to support which I haven't yet, which is the option to shut down the service, but keep the notification alive, giving the illusion that your app is still alive. In this state, the service is not in fact running, and not keeping the CPU alive so your phone can truly sleep if it wants, but if the user opens the notification and presses the play button, the service gets restarted. This is a very battery-efficient option, though it would be a little more complicated to implement this within the plugin so it's an idea for further down the line.

from audio_service.

moda20 avatar moda20 commented on May 29, 2024 1

Sorry to just jump here like this, but if I don't have an already isolated way of moving/storing data ( audio files) I won't be able to use the backgroundTask implementation and therefor the library.

in my case I have a singleton object that holds all my audiophiles that I want to read and It happens that I can't import it to the background task even with DI libraries like GetIt. How do you suggest fixing this ? without having to rewrite everything to be inside the backgroundTask.

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024

The error suggests that you need to use top-level functions. Although it's complaining about handle, I think the problem actually begins with the backgroundTask parameter which the documentation of this plugin states must be a top-level function. See the included example app for how to define this as a top-level function.

So instead of:

backgroundTask: () { .... }

Try this:

backgroundTask: _myBackgroundTask

...

// This must be at the "top level"
// i.e. not nested within any other function or class
void _myBackgroundTask() {
}

from audio_service.

hacker1024 avatar hacker1024 commented on May 29, 2024

I need to pass multiple objects into my audioTask. I tried using top-level variables, but they're null in the isolate.
To my understanding, I can only pass primitive types in AudioService.customAction.

How can I do this?

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024

The main isolate in which your UI runs and the background isolate that is spawned by the audio service do not share any memory, so top-level variables will not be the same between isolates. You have to use one of the various message passing facilities and typically that involves encoding your data into primitives or lists and maps consisting of primitives. If you want to pass whole objects without encoding them, you may try send ports and receive ports via the IsolateNameServer API.

Using the customAction approach, you just need to send your data as a data structure consisting of only lists, maps and primitives. If you already have the data stored in persistence, then you can simplify the amount of data you need to communicate by just sending the keys and on the receiving end use those keys to lookup the full records in your persistence container.

In many cases, you won't need to pass anything since everything will be in persistence. For example, an MP3 player with a playlist and a current track will surely persist this data so that the playlist and current track is remembered on the next startup, and so when the background task starts within its own isolate, it can simply pull this data out of persistence and play it on its own.

from audio_service.

hacker1024 avatar hacker1024 commented on May 29, 2024

If you want to pass whole objects without encoding them, you may try send ports and receive ports via the IsolateNameServer API.

This works beautifully, thanks.
It's work noting that this method won't work with dart2js, so my app won't run on Flutter for web when it's available.

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024

Great! I'll close this issue.

from audio_service.

akdasa avatar akdasa commented on May 29, 2024

@ryanheise, thanks a lot.

from audio_service.

Mahfoud047 avatar Mahfoud047 commented on May 29, 2024

@ryanheise please can you give as a complete example of using customAction, I tried the code bellow but the player stops playing audio abruptly after few seconds.

void _backgroundAudioPlayerTask() async {
  String url = '';

  CustomAudioPlayer player = CustomAudioPlayer(url); // changed the code inside CustomAudioPlayer
  AudioServiceBackground.run(
    onCustomAction: (String name, dynamic arguments) {
      switch (name) {
        case "url":
          url = arguments;
          break;
      }
    },
    onStart: player.run,
    onPlay: player.play,
    onPause: player.pause,
    onStop: player.stop,
    onClick: (MediaButton button) => player.playPause(),
  );
}

Otherwise how can I use methods like AudioService.addQueueItem() and AudioService.currentMediaItem to pass the MediaItem to the backgroundAudioPlayerTask and play audio from url which is the id of the MediaItem. And in this case I will not need the customAction.

Please give us a complete working example.

from audio_service.

themobilecoder avatar themobilecoder commented on May 29, 2024

The main isolate in which your UI runs and the background isolate that is spawned by the audio service do not share any memory, so top-level variables will not be the same between isolates. You have to use one of the various message passing facilities and typically that involves encoding your data into primitives or lists and maps consisting of primitives. If you want to pass whole objects without encoding them, you may try send ports and receive ports via the IsolateNameServer API.

Using the customAction approach, you just need to send your data as a data structure consisting of only lists, maps and primitives. If you already have the data stored in persistence, then you can simplify the amount of data you need to communicate by just sending the keys and on the receiving end use those keys to lookup the full records in your persistence container.

In many cases, you won't need to pass anything since everything will be in persistence. For example, an MP3 player with a playlist and a current track will surely persist this data so that the playlist and current track is remembered on the next startup, and so when the background task starts within its own isolate, it can simply pull this data out of persistence and play it on its own.

Hi Ryan,
Thanks for creating this library. At the moment I'm still trying to understand how to integrate your library with my project because I'm having problems creating my own media notification for both iOS and Android, and yours has already solved it!

However, I disagree with the thing where passing arguments is not really necessary for most cases. I guess for local audio files, but not remotely.

I am trying to build a radio app and it seems a bit hacky when, for example a user selects a Radio station and the app needs to save the URL first(shared_pref/database) just so the background task can read it before playing it.
I do understand that it's a technical limitation due to the nature of it being a background task and has to be a top level function, but there must be a better way to pass data (in this case, the URL to play)

For now, I will use the customAction API that you have provided.

Thanks!

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024

The persistence approach would make more sense for a podcast or music player which maintains a playlist that is saved locally on the device, and I agree with you that in a radio app where you don't persist playlists, passing arguments at runtime with customAction makes more sense. Instead of customAction, you could also consider using playFromMediaId with the URL as the parameter, if that's the only parameter you need.

from audio_service.

themobilecoder avatar themobilecoder commented on May 29, 2024

The persistence approach would make more sense for a podcast or music player which maintains a playlist that is saved locally on the device, and I agree with you that in a radio app where you don't persist playlists, passing arguments at runtime with customAction makes more sense. Instead of customAction, you could also consider using playFromMediaId with the URL as the parameter, if that's the only parameter you need.

Thanks Ryan. I haven't really noticed the playFromMediaId

Does that mean that if a user wants to play another radio station when another station is already playing, then I can use addQueueItem() and programmatically skip to the next queue then use playFromMediaId() ?

If I understand it correctly, then there's actually no need to pass any arguments and instead just use the queue.

from audio_service.

volgin avatar volgin commented on May 29, 2024

From my perspective, the biggest challenge is not the communication between the isolates, but what to do when the main isolate is killed. The background will continue to play the current item, or even a queue of items, but what happens at the end of a queue? The music simply stops playing until a user revives the app and clicks on something else to play. Is this the correct understanding?

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024

Thanks Ryan. I haven't really noticed the playFromMediaId

Does that mean that if a user wants to play another radio station when another station is already playing, then I can use addQueueItem() and programmatically skip to the next queue then use playFromMediaId() ?

If I understand it correctly, then there's actually no need to pass any arguments and instead just use the queue.

I don't think the concept of a "queue" really maps onto a radio app since items in the queue don't have a finite duration (although you're free to interpret the queue how you like). But while listening to one radio station, you can issue another playFromMediaId request with the new URL and implement your callback to stop playing the previous station and switch to the new station.

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024

@moda20 You can pass data between the isolates using custom actions, or standard send/receive ports (search past issues for a discussion).

from audio_service.

moda20 avatar moda20 commented on May 29, 2024

@ryanheise I am trying to pass dependency but it keeps getting flagged as invalid argument type. can't we pass objects, pointers to objects in a customAction ?

from audio_service.

ryanheise avatar ryanheise commented on May 29, 2024

Custom actions pass data over method channels and are limited by the standard codec which supports simple data types. For anything more complex, you need to use send/receive ports (search past issues for a discussion, in particular IsolateNameServer). Note that either way, isolates do not share memory and what is being passed is being passed by value, not by reference.

from audio_service.

github-actions avatar github-actions commented on May 29, 2024

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs, or use StackOverflow if you need help with audio_service.

from audio_service.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.