У меня есть простое приложение для музыкального плеера (источник), у которого были проблемы с воспроизведением в Lollipop при использовании наушников. Музыка будет воспроизводиться в нормальном режиме от 30 секунд до 5 минут, затем пауза в течение ~ 2-4 секунд, затем возобновление.
Поведение, как правило, происходит, когда экран выключен, но приобретение процессора wakelock не помогло.
Частота пауз, по-видимому, ускоряется с течением времени. Сначала это раз в час, но затем время между паузами уменьшается примерно на половину каждый раз, пока оно не остановится почти каждую минуту.
Я наблюдал это поведение с iTunes закодированными файлами aac, другие наблюдали это с помощью mp3.
Это наблюдается только при прослушивании проводных наушников. Я никогда не испытывал такого поведения на Bluetooth-гарнитуре.
Что может быть причиной этого? Это похоже на проблему с приоритетом процесса, но я не знаю, как решить эту проблему.
Я не испытывал этого на Android 4.x.
Здесь билет Github для этой проблемы.
Вот некоторые соответствующие биты исходного кода:
манифеста
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.smithdtyler.prettygoodmusicplayer"
android:versionCode="65"
android:versionName="3.2.14" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_pgmp_launcher"
android:label="@string/app_name"
android:theme="@style/AppBaseTheme" >
<!-- Set the artist list to launch mode single task to prevent multiple instances -->
<!-- This fixes an error where exiting the application just brings up another instance -->
<!-- See https://developer.android.com/guide/topics/manifest/activity-element.html#lmode -->
<activity
android:name="com.smithdtyler.prettygoodmusicplayer.ArtistList"
android:label="@string/app_name"
android:launchMode="singleTask" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.CATEGORY_APP_MUSIC " />
</intent-filter>
</activity>
<activity
android:name="com.smithdtyler.prettygoodmusicplayer.SettingsActivity"
android:label="@string/title_activity_settings" >
</activity>
<activity
android:name="com.smithdtyler.prettygoodmusicplayer.AlbumList"
android:label="@string/title_activity_album_list" >
</activity>
<activity
android:name="com.smithdtyler.prettygoodmusicplayer.SongList"
android:label="@string/title_activity_song_list" >
</activity>
<activity
android:name="com.smithdtyler.prettygoodmusicplayer.NowPlaying"
android:exported="true"
android:label="@string/title_activity_now_playing" >
</activity>
<!--
The service has android:exported="true" because that needed for
control from the notification. Not sure why it causes a warning...
-->
<service
android:name="com.smithdtyler.prettygoodmusicplayer.MusicPlaybackService"
android:exported="true"
android:icon="@drawable/ic_pgmp_launcher" >
</service>
<receiver
android:name="com.smithdtyler.prettygoodmusicplayer.MusicBroadcastReceiver"
android:enabled="true" >
<intent-filter android:priority="2147483647" >
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
</application>
</manifest>
MusicPlaybackService.onCreate()
@Override
public synchronized void onCreate() {
Log.i(TAG, "Music Playback Service Created!");
isRunning = true;
sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
powerManager =(PowerManager) getSystemService(POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"PGMPWakeLock");
random = new Random();
mp = new MediaPlayer();
mp.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
Log.i(TAG, "Song complete");
next();
}
});
// https://developer.android.com/training/managing-audio/audio-focus.html
audioFocusListener = new PrettyGoodAudioFocusChangeListener();
// Get permission to play audio
am = (AudioManager) getBaseContext().getSystemService(
Context.AUDIO_SERVICE);
HandlerThread thread = new HandlerThread("ServiceStartArguments");
thread.start();
// Get the HandlerThread Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
// https://stackoverflow.com/info/19474116/the-constructor-notification-is-deprecated
// https://stackoverflow.com/info/6406730/updating-an-ongoing-notification-quietly/15538209#15538209
Intent resultIntent = new Intent(this, NowPlaying.class);
resultIntent.putExtra("From_Notification", true);
resultIntent.putExtra(AlbumList.ALBUM_NAME, album);
resultIntent.putExtra(ArtistList.ARTIST_NAME, artist);
resultIntent.putExtra(ArtistList.ARTIST_ABS_PATH_NAME, artistAbsPath);
// Use the FLAG_ACTIVITY_CLEAR_TOP to prevent launching a second
// NowPlaying if one already exists.
resultIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
resultIntent, 0);
Builder builder = new NotificationCompat.Builder(
this.getApplicationContext());
String contentText = getResources().getString(R.string.ticker_text);
if (songFile != null) {
contentText = Utils.getPrettySongName(songFile);
}
Notification notification = builder
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_pgmp_launcher)
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.setContentTitle(
getResources().getString(R.string.notification_title))
.build();
startForeground(uniqueid, notification);
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
onTimerTick();
}
}, 0, 500L);
Log.i(TAG, "Registering event receiver");
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// Apparently audio registration is persistent across lots of things...
// restarts, installs, etc.
mAudioManager.registerMediaButtonEventReceiver(cn);
// I tried to register this in the manifest, but it doesn't seen to
// accept it, so I'll do it this way.
getApplicationContext().registerReceiver(receiver, filter);
headphoneReceiver = new HeadphoneBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.HEADSET_PLUG");
registerReceiver(headphoneReceiver, filter);
}
MusicPlaybackService.startPlayingFile()
private synchronized void startPlayingFile(int songProgress) {
// Have we loaded a file yet?
if (mp.getDuration() > 0) {
pause();
mp.stop();
mp.reset();
}
// open the file, pass it into the mp
try {
fis = new FileInputStream(songFile);
mp.setDataSource(fis.getFD());
mp.prepare();
if(songProgress > 0){
mp.seekTo(songProgress);
}
wakeLock.acquire();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Задача Таймера MusicPlaybackService
private void onTimerTick() {
long currentTime = System.currentTimeMillis();
if (pauseTime < currentTime) {
pause();
}
updateResumePosition();
sendUpdateToClients();
}
private void updateResumePosition(){
long currentTime = System.currentTimeMillis();
if(currentTime - 10000 > lastResumeUpdateTime){
if(mp != null && songFile != null && mp.isPlaying()){
int pos = mp.getCurrentPosition();
SharedPreferences prefs = getSharedPreferences("PrettyGoodMusicPlayer", MODE_PRIVATE);
Log.i(TAG,
"Preferences update success: "
+ prefs.edit()
.putString(songFile.getParentFile().getAbsolutePath(),songFile.getName() + "~" + pos)
.commit());
}
lastResumeUpdateTime = currentTime;
}
}
private void sendUpdateToClients() {
List<Messenger> toRemove = new ArrayList<Messenger>();
synchronized (mClients) {
for (Messenger client : mClients) {
Message msg = Message.obtain(null, MSG_SERVICE_STATUS);
Bundle b = new Bundle();
if (songFile != null) {
b.putString(PRETTY_SONG_NAME,
Utils.getPrettySongName(songFile));
b.putString(PRETTY_ALBUM_NAME, songFile.getParentFile()
.getName());
b.putString(PRETTY_ARTIST_NAME, songFile.getParentFile()
.getParentFile().getName());
} else {
// songFile can be null while we're shutting down.
b.putString(PRETTY_SONG_NAME, " ");
b.putString(PRETTY_ALBUM_NAME, " ");
b.putString(PRETTY_ARTIST_NAME, " ");
}
b.putBoolean(IS_SHUFFLING, this._shuffle);
if (mp.isPlaying()) {
b.putInt(PLAYBACK_STATE, PlaybackState.PLAYING.ordinal());
} else {
b.putInt(PLAYBACK_STATE, PlaybackState.PAUSED.ordinal());
}
// We might not be able to send the position right away if mp is
// still being created
// so instead let send the last position we knew about.
if (mp.isPlaying()) {
lastDuration = mp.getDuration();
lastPosition = mp.getCurrentPosition();
}
b.putInt(TRACK_DURATION, lastDuration);
b.putInt(TRACK_POSITION, lastPosition);
msg.setData(b);
try {
client.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
toRemove.add(client);
}
}
for (Messenger remove : toRemove) {
mClients.remove(remove);
}
}
}