Android MediaPlayer的播放会通过有线耳机结束,而不是通过蓝牙

我有一个简单的音乐播放器应用程序( 源 ),使用耳机时有棒棒糖播放问题。 音乐可以正常播放30秒到5分钟,然后暂停2-4秒,然后恢复。

行为似乎通常发生在屏幕closures时,但获取CPU唤醒锁没有帮助。

暂停的频率似乎随着时间的推移而加速。 起初每小时一次,但是暂停之间的时间每次减less约一半,直到几乎每分钟都在暂停。

我已经观察到这种行为与iTunes编码的AAC文件,其他人已经观察到它与MP3。

只有在使用有线耳机时才能观察到这一点。 我从来没有经历过蓝牙耳机的这种行为。

什么可能导致这个? 这似乎是一个stream程优先问题,但我不知道如何解决这类问题。

我在Android 4.x上没有遇到过这个问题。

这是这个问题的Github票 。

以下是一些相关的源代码位:

performance

<?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's 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's Looper and use it for our Handler mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); // https://stackoverflow.com/questions/19474116/the-constructor-notification-is-deprecated // https://stackoverflow.com/questions/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's 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); } } } 

Solutions Collecting From Web of "Android MediaPlayer的播放会通过有线耳机结束,而不是通过蓝牙"

我从Vanilla音乐播放器的开发者得到了非常有帮助的回应 :

我们使用一个单独的线程来读取当前正在播放的文件:

– >线程以大约256kb / s的速度读取文件,所以它比文件服务器读取文件的速度要快 – >这样文件就有很好的机会留在页面/磁盘caching中 – > ..这样可以最小化机会由于时髦的SD卡或其他的IO暂停“退出”。

代码位于: https : //github.com/vanilla-music/vanilla/blob/master/src/ch/blinkenlights/android/vanilla/ReadaheadThread.java代码不依赖于任何部分的香草音乐:if你想尝试一下,把它放到你的项目中,做一些事情:

 onCreate { ... mReadaheadThread = new ReadaheadThread() ... } ... mMediaPlayer.setDataSource(path); mReadaheadThread.setDataSource(path); ... 

由于实施这个变化,我没有遇到这个问题。