Java Android MediaPlayer通过有线耳机播放时会出现口吃,而不是通过蓝牙
我有一个简单的音乐播放器应用程序(),当使用耳机时,它在棒棒糖中出现播放问题。音乐将正常播放30秒至5分钟,然后暂停约2-4秒,然后继续播放 这种行为似乎通常发生在屏幕关闭时,但获取CPU唤醒锁没有帮助 停顿的频率似乎随着时间的推移而加快。一开始是每小时一次,但是每次暂停之间的时间减少一半,直到几乎每分钟暂停一次 我在iTunes编码的aac文件中观察到了这种行为,其他人在MP3中也观察到了这种行为 这仅在通过有线耳机播放时观察到。我从未在蓝牙耳机上体验过这种行为 这可能是什么原因造成的?这似乎是一个过程优先级问题,但我不知道如何解决这类问题 我还没有在安卓4.x上体验过这种情况 这是这个问题的答案 以下是一些相关的源代码: 清单Java Android MediaPlayer通过有线耳机播放时会出现口吃,而不是通过蓝牙,java,android,bluetooth,Java,Android,Bluetooth,我有一个简单的音乐播放器应用程序(),当使用耳机时,它在棒棒糖中出现播放问题。音乐将正常播放30秒至5分钟,然后暂停约2-4秒,然后继续播放 这种行为似乎通常发生在屏幕关闭时,但获取CPU唤醒锁没有帮助 停顿的频率似乎随着时间的推移而加快。一开始是每小时一次,但是每次暂停之间的时间减少一半,直到几乎每分钟暂停一次 我在iTunes编码的aac文件中观察到了这种行为,其他人在MP3中也观察到了这种行为 这仅在通过有线耳机播放时观察到。我从未在蓝牙耳机上体验过这种行为 这可能是什么原因造成的?这似乎
<?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.startPlayingFile()
MusicLaybackService计时器任务
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);
}
}
}
private void onTimerTick(){
长currentTime=System.currentTimeMillis();
如果(暂停时间<当前时间){
暂停();
}
updateResumePosition();
sendUpdateToClient();
}
私有void updateResumePosition(){
长currentTime=System.currentTimeMillis();
如果(currentTime-10000>lastResumeUpdateTime){
if(mp!=null&&songFile!=null&&mp.isPlaying()){
int pos=mp.getCurrentPosition();
SharedReferences prefs=GetSharedReferences(“PrettyGoodMusicLayer”,模式\私人);
Log.i(标签,
“首选项更新成功:”
+prefs.edit()
.putString(songFile.getParentFile().getAbsolutePath(),songFile.getName()+“~”+pos)
.commit());
}
lastResumeUpdateTime=currentTime;
}
}
私有void sendUpdateToClient(){
List toRemove=new ArrayList();
已同步(McClient){
用于(Messenger客户端:mClients){
Message msg=Message.get(空,消息服务状态);
Bundle b=新Bundle();
if(songFile!=null){
b、 putString(漂亮的歌名,
getPrettySongName(songFile));
b、 putString(PRETTY_相册_名称,songFile.getParentFile()
.getName());
b、 putString(PRETTY\u ARTIST\u NAME,songFile.getParentFile()
.getParentFile().getName());
}否则{
//关闭时,songFile可以为null。
b、 putString(PRETTY_SONG_NAME,”);
b、 putString(PRETTY_相册_NAME,”);
b、 putString(PRETTY_ARTIST_NAME,”);
}
b、 putBoolean(是SHUFFLING,this.\ushuffle);
if(mp.isPlaying()){
b、 putInt(PLAYBACK_STATE,PlaybackState.PLAYING.ordinal());
}否则{
b、 putInt(播放状态,PlaybackState.PAUSED.ordinal());
}
//如果议员不同意,我们可能无法立即发送该职位
//仍在创建中
//因此,让我们发送我们知道的最后一个位置。
if(mp.isPlaying()){
lastDuration=mp.getDuration();
lastPosition=mp.getCurrentPosition();
}
b、 putInt(轨道持续时间,最后持续时间);
b、 putInt(轨道位置,最后位置);
msg.setData(b);
试一试{
client.send(msg);
}捕获(远程异常){
e、 printStackTrace();
删除。添加(客户端);
}
}
对于(通讯器移除:toRemove){
mClients.remove(移除);
}
}
}
我从开发人员那里得到了一个:
我们使用一个单独的线程提前读取当前正在播放的文件:
->线程以大约256kb/s的速度读取文件,因此它读取文件的速度比mediaserver快
->这使文件有很好的机会留在页面/磁盘缓存中
->..这将因时髦的sd卡或其他IO暂停而“退出”的机会降至最低
代码位于此处:
代码不依赖于香草音乐的任何部分:如果您想尝试一下,只需将其放入您的项目中,然后执行以下操作:
onCreate {
...
mReadaheadThread = new ReadaheadThread()
...
}
...
mMediaPlayer.setDataSource(path);
mReadaheadThread.setDataSource(path);
...
自从实施此更改以来,我没有遇到过这个问题。我想指出,我也遇到过类似的问题。这些问题发生在摩托罗拉Moto G上,其CyanogenMod build 11-20141206-NIGHTLY-falcon(4.4.4)版本。我没有在其他设备上测试。我怀疑有音频聚焦问题。您是否正在打印音频焦点丢失和丢失日志?尝试在该区域进行调试。我有一个类似的问题,这是“意外”解决了当我搞砸了我的音频焦点听众
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);
}
}
}
onCreate {
...
mReadaheadThread = new ReadaheadThread()
...
}
...
mMediaPlayer.setDataSource(path);
mReadaheadThread.setDataSource(path);
...