1
0
mirror of https://github.com/TeamNewPipe/NewPipe synced 2025-07-04 02:53:09 +00:00

Drop some assumptions on how PlayerService is started and reused

Read the comments in the lines changed to understand more
This commit is contained in:
Stypox 2025-02-15 17:48:13 +01:00
parent c6e1721884
commit b764ad33c4
No known key found for this signature in database
GPG Key ID: 4BDF1B40A49FDD23
4 changed files with 43 additions and 21 deletions

View File

@ -7,3 +7,16 @@ import androidx.core.os.BundleCompat
inline fun <reified T : Parcelable> Bundle.parcelableArrayList(key: String?): ArrayList<T>? { inline fun <reified T : Parcelable> Bundle.parcelableArrayList(key: String?): ArrayList<T>? {
return BundleCompat.getParcelableArrayList(this, key, T::class.java) return BundleCompat.getParcelableArrayList(this, key, T::class.java)
} }
fun Bundle?.toDebugString(): String {
if (this == null) {
return "null"
}
val string = StringBuilder("Bundle{")
for (key in this.keySet()) {
@Suppress("DEPRECATION") // we want this[key] to return items of any type
string.append(" ").append(key).append(" => ").append(this[key]).append(";")
}
string.append(" }")
return string.toString()
}

View File

@ -28,6 +28,7 @@ import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
import org.schabi.newpipe.ktx.BundleKt;
import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi; import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi;
import org.schabi.newpipe.player.notification.NotificationPlayerUi; import org.schabi.newpipe.player.notification.NotificationPlayerUi;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
@ -41,6 +42,7 @@ import java.lang.ref.WeakReference;
public final class PlayerService extends Service { public final class PlayerService extends Service {
private static final String TAG = PlayerService.class.getSimpleName(); private static final String TAG = PlayerService.class.getSimpleName();
private static final boolean DEBUG = Player.DEBUG; private static final boolean DEBUG = Player.DEBUG;
public static final String SHOULD_START_FOREGROUND_EXTRA = "should_start_foreground_extra";
private Player player; private Player player;
@ -59,35 +61,39 @@ public final class PlayerService extends Service {
assureCorrectAppLanguage(this); assureCorrectAppLanguage(this);
ThemeHelper.setTheme(this); ThemeHelper.setTheme(this);
player = new Player(this); // Note: you might be tempted to create the player instance and call startForeground here,
/* // but be aware that the Android system might start the service just to perform media
Create the player notification and start immediately the service in foreground, // queries. In those cases creating a player instance is a waste of resources, and calling
otherwise if nothing is played or initializing the player and its components (especially // startForeground means creating a useless empty notification. In case it's really needed
loading stream metadata) takes a lot of time, the app would crash on Android 8+ as the // the player instance can be created here, but startForeground() should definitely not be
service would never be put in the foreground while we said to the system we would do so // called here unless the service is actually starting in the foreground, to avoid the
*/ // useless notification.
player.UIs().get(NotificationPlayerUi.class)
.ifPresent(NotificationPlayerUi::createNotificationAndStartForeground);
} }
@Override @Override
public int onStartCommand(final Intent intent, final int flags, final int startId) { public int onStartCommand(final Intent intent, final int flags, final int startId) {
if (DEBUG) { if (DEBUG) {
Log.d(TAG, "onStartCommand() called with: intent = [" + intent Log.d(TAG, "onStartCommand() called with: intent = [" + intent
+ "], extras = [" + BundleKt.toDebugString(intent.getExtras())
+ "], flags = [" + flags + "], startId = [" + startId + "]"); + "], flags = [" + flags + "], startId = [" + startId + "]");
} }
/* // All internal NewPipe intents used to interact with the player, that are sent to the
Be sure that the player notification is set and the service is started in foreground, // PlayerService using startForegroundService(), will have SHOULD_START_FOREGROUND_EXTRA,
otherwise, the app may crash on Android 8+ as the service would never be put in the // to ensure startForeground() is called (otherwise Android will force-crash the app).
foreground while we said to the system we would do so if (intent.getBooleanExtra(SHOULD_START_FOREGROUND_EXTRA, false)) {
The service is always requested to be started in foreground, so always creating a if (player == null) {
notification if there is no one already and starting the service in foreground should // make sure the player exists, in case the service was resumed
not create any issues player = new Player(this);
If the service is already started in foreground, requesting it to be started shouldn't }
do anything
*/ // Be sure that the player notification is set and the service is started in foreground,
if (player != null) { // otherwise, the app may crash on Android 8+ as the service would never be put in the
// foreground while we said to the system we would do so. The service is always
// requested to be started in foreground, so always creating a notification if there is
// no one already and starting the service in foreground should not create any issues.
// If the service is already started in foreground, requesting it to be started
// shouldn't do anything.
player.UIs().get(NotificationPlayerUi.class) player.UIs().get(NotificationPlayerUi.class)
.ifPresent(NotificationPlayerUi::createNotificationAndStartForeground); .ifPresent(NotificationPlayerUi::createNotificationAndStartForeground);
} }

View File

@ -130,7 +130,9 @@ public final class PlayerHolder {
// and NullPointerExceptions inside the service because the service will be // and NullPointerExceptions inside the service because the service will be
// bound twice. Prevent it with unbinding first // bound twice. Prevent it with unbinding first
unbind(context); unbind(context);
ContextCompat.startForegroundService(context, new Intent(context, PlayerService.class)); final Intent intent = new Intent(context, PlayerService.class);
intent.putExtra(PlayerService.SHOULD_START_FOREGROUND_EXTRA, true);
ContextCompat.startForegroundService(context, intent);
serviceConnection.doPlayAfterConnect(playAfterConnect); serviceConnection.doPlayAfterConnect(playAfterConnect);
bind(context); bind(context);
} }

View File

@ -96,6 +96,7 @@ public final class NavigationHelper {
} }
intent.putExtra(Player.PLAYER_TYPE, PlayerType.MAIN.valueForIntent()); intent.putExtra(Player.PLAYER_TYPE, PlayerType.MAIN.valueForIntent());
intent.putExtra(Player.RESUME_PLAYBACK, resumePlayback); intent.putExtra(Player.RESUME_PLAYBACK, resumePlayback);
intent.putExtra(PlayerService.SHOULD_START_FOREGROUND_EXTRA, true);
return intent; return intent;
} }