听筒模式下播放音乐

本文介绍如何在Android设备上实现听筒模式播放音乐的功能,包括设置AudioManager属性、使用MediaPlayer播放音频文件、调整音量及播放状态监测等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内容

继续前文 MediaPlayer和SeekBar配合起来。这次讲听筒模式下播放音乐的步骤。

参考资料

修改点

先给出和上一篇文章的差异点:

difference

下面是文本模式的代码,方便拷贝:——这里省掉了对AudioManager的声明,可见后面的完整代码。

setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);  
audioManager = (AudioManager) this.getSystemService(AUDIO_SERVICE);
audioManager.setSpeakerphoneOn(false);
audioManager.setMode(AudioManager.MODE_IN_CALL);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); 
audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
        maxVolume, AudioManager.FLAG_PLAY_SOUND);

mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {

完整代码

package com.example.mediaplayerexample;

import java.io.IOException;

import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;


public class MainActivity extends Activity {
    private final String TAG = "MainActivity";

    private MediaPlayer mediaPlayer = null;
    private Button start = null;
    private Button stop = null;
    private SeekBar mediaSeekBar = null;

    private AudioManager audioManager = null;

    boolean isPlaying = false;
    private final int MONITOR_MSG_ID = 0;
    private Thread monitor = null;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (!isPlaying || msg.what != MONITOR_MSG_ID) {
                super.handleMessage(msg);
                return;
            }

            mediaSeekBar.setProgress(mediaPlayer.getCurrentPosition());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_layout);

        start = (Button) findViewById(R.id.start);
        stop = (Button) findViewById(R.id.stop);
        stop.setEnabled(false);
        start.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    mediaPlayer.prepare();
                } catch (IllegalStateException e) {
                    Log.d(TAG, "start click error", e);
                } catch (IOException e) {
                    Log.d(TAG, "start click error", e);
                }

                isPlaying = true;
                mediaPlayer.start();
                mediaSeekBar.setMax(mediaPlayer.getDuration());
                start.setEnabled(false);
                stop.setEnabled(true);
                monitor = new MonitorThread(1000);
                monitor.start();
            }
        });

        stop.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                isPlaying = false;
                mediaPlayer.stop();
                start.setEnabled(true);
                stop.setEnabled(false);
                try {
                    monitor.join();
                } catch (InterruptedException e) {
                    Log.d(TAG, "stop click error", e);
                }
            }
        });

        Log.d(TAG, "new MediaPlyer()");

        setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);  
        audioManager = (AudioManager) this.getSystemService(AUDIO_SERVICE);
        audioManager.setSpeakerphoneOn(false);
        audioManager.setMode(AudioManager.MODE_IN_CALL);
        int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); 
        audioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
                maxVolume, AudioManager.FLAG_PLAY_SOUND);

        mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
        mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
            @Override 
            public void onCompletion(MediaPlayer mp) {
                isPlaying = false;
                start.setEnabled(true);
                stop.setEnabled(false);
                try {
                    monitor.join();
                } catch (InterruptedException e) {
                    Log.e(TAG, "start thread failed.", e);
                }
            }
        });

        Log.d(TAG, "Set data source");
        AssetFileDescriptor fd = null;
        try {
            fd = getResources().openRawResourceFd(R.raw.test);
            mediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
            fd.close();
            Log.d(TAG, "set data source ok");
        } catch (IOException e) {
            Log.e(TAG, "set data source failed.", e);
            finish();
        }

        mediaSeekBar = (SeekBar) findViewById(R.id.mediaSeekBar);
        mediaSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress,
                    boolean fromUser) {
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                if (isPlaying) {
                    mediaPlayer.seekTo(seekBar.getProgress());
                }
            }

        });

        Log.d(TAG, "create ok");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private class MonitorThread extends Thread{
        private int interval;

        public MonitorThread(int interval) {
            this.interval = interval;
        }

        public void run(){
            Log.d(TAG, "MonitorThread::run()");
            while(true) {
                if (!isPlaying) return;

                try {
                    sleep(interval);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                handler.sendEmptyMessage(MONITOR_MSG_ID);
            }
        }

    }
}

补充说明

  • 权限问题

在参考文章中说需要在AndroidManifest.xml中增加权限:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> 

但本文的例子没有加这个,也能够运行。再查阅AudioManaer.class,查找MODIFY_AUDIO_SETTINGS。发现有两个方法的注释中提到要加权限:

/**
 * Start bluetooth SCO audio connection.
 * <p>Requires Permission:
 *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
 * ...
 */
public void startBluetoothSco(){
    ...
}

/**
 * Stop bluetooth SCO audio connection.
 * <p>Requires Permission:
 *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
 * ...
 * @see #startBluetoothSco()
 */
public void stopBluetoothSco(){
    ...
}
  • am和mp的初始化顺序。

设置AudioManager的属性可以在MediaPlayer的前面,也可以在后面。但都需要在setDataSource()调用之前。
简单地,可以约定先设置AudioManager的属性;再初始化MediaPlayer对象,并设置其属性。

  • mp中方法调用顺序。

MediaPlayer的setAudioStreamType()调用需要在setDataSource()的前面。否则,voice-in-call会不发声。
对于MediaPlayer先设置属性,最后设置data source,以及prepare+start+pause+stop等。

源码中的说明:

/**
 * Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
 * for a list of stream types. Must call this method before prepare() or
 * prepareAsync() in order for the target stream type to become effective
 * thereafter.
 *
 * @param streamtype the audio stream type
 * @see android.media.AudioManager
 */
public native void setAudioStreamType(int streamtype);
  • 下面两句可以调换位置

    setVolumeControlStream(streamType);
    mAudioManager.setSpeakerphoneOn(mIsSpeaker);

  • setVolumeControlStream(): 参考《Professional Android 4 Application Development》P632~P633 “Response to the Volume Control”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值