• Android MediaPlayer Playback多媒体开发应用程序接口


    Translated from android doc:http://developer.android.com/guide/topics/media/mediaplayer.html

    ================================================================

    Media Playback

    Android框架支持各种普通media类型的编解码,因此你可以很容易地把音频,视频和图片整合到你的应用程序中。通过使用MediaPlayer接
    口,你可以播放各种音视频文件,包括应用程序中的资源(原生资源),文件系统中的独立文件,或者来自网络连接的数据流。如果硬件支持,你还可以使用MediaRecorder接口录制音视频。注意,模拟器不包含获取音视频的硬件,不过真正的移动设备可能会提供这项功能。

    本文档将告诉你如何与用户和系统交互,以开发一个高性能,和良好用户体验的媒体播放器。

    注意:你可以在标准的输出设备设播放音频数据。如移动设备上的喇叭或者蓝牙耳机。但是,在打电话时的交互音频中,你不能播放声音文件。



    Using MediaPlayer

    Media框架中的一个重要部分就是MediaPlayer类。该类的一个对象可以通过最少的设置来获取,解码和播放音视频文件。它支持多种不同的媒体源,如:
    1、本地源
    2、Internal URIs,例如你通过Content Resolver得到的URI
    3、扩展URLs(流)

    文档Android Supported Media Formats(http://developer.android.com/guide/appendix/media-formats.html)列出了Android所支持的媒
    体格式。


    下面的例子展示了如何如何播放本地原生资料音频(音频文件保存在res/raw/目录下)
    MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
    mediaPlayer.start(); // no need to call prepare(); create() does that for you

    在这个例子中,一个“原生”资源,是系统不会去做特殊解析文件。不过,该资源的内容不应该是原生音频。它应该是经过某种所支持的媒
    体文件格式编码的。

    下面的例子展示了如何播放系统本地的URI(例如,你通过Content Resolver获取的)
    Uri myUri = ....; // initialize Uri here
    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mediaPlayer.setDataSource(getApplicationContext(), myUri);
    mediaPlayer.prepare();
    mediaPlayer.start();


    通过HTTP流,播放远程的URL如下:
    String url = "http://........"; // your URL here
    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mediaPlayer.setDataSource(url);
    mediaPlayer.prepare(); // might take long! (for buffering, etc)
    mediaPlayer.start();


    注意:如果你将一个URL作为在线媒体流文件,该文件必须是可以下载的。
    警告:在使用setDataSource()是,你必须捕捉或者传递IllegalArgumentException和IOException异常,因为你引用的文件可能不存在。



    Asynchronous Preparation(异步准备)

    原则上,可以直接使用MediaPlayer。不过也要记住如果要将其与典型的Android应用程序整合,有些准备工作还是必须的。例如,调用prepare()会花费比较长的时间去执行,因为它将包括获取和解码媒体数据。因此,与调用其它费时的方法相似,你不应该在应用程序的UI线程中调用它。如果那样做的话,UI将会一直等待,知道该方法返回,这将是很差的用户体验,并可能导致ANR(Application Not Responding错误。就算你认为你的资源会很快加载,请记住UI中超过十分之一秒的任何事情都会导致明显的停顿,并给用户留下程序执行慢的印象。为了避免等待,可以孵化出另外一个线程来准备MediaPlayer,并在准备好的时候通知主线程。不过,虽然你可以自己来写线程逻辑,该模式已经非常常用,以至于在框架中已经提供了接口prepareAsync()以方面地实现该功能。该方法在后台准备媒体并迅速返回。当媒体准备好的时候,通过setOnPreparedListener()配置的MediaPlayer.OnPreparedListener的方法onPrepared()将被调用。



    状态管理
    另外一个,你需要铭记的就是MediaPlayer是基于状态的。你需要注意MediaPlayer有一个内部状态,因为有些操作只有在特定的状态下才有效。如果你在错误状态下执行了一个操作,可能会导致系统抛出一个异常,或者其他不可预知的动作。

    MeidaPlayer(http://developer.android.com/reference/android/media/MediaPlayer.html)的文档有一个完整的状态图,并各个方法导
    致的状态转移。例如,当你创建一个新的MediaPlayer时,它将处于Idle状态。此时,你需要调用setDataSource()来初始化它,以将其带到Initialized状态。那之后,你需要调用prepare()或prepareAsync()来准备它。当MediaPlayer准备好时,它将进入Prepared状态,这表明你可以调用start()来进行播放了。此时,如图中所述,你可以调用start(),pause()和seekTo()在状态Started,Paused和PlaybackCompleted之间转移。不过,如果你调用了stop(),你将不能调用start(),除非你重新准备了该MediaPlayer。

    写代码与MediaPlayer进行交互时,一定要将状态图铭记在心。因为在错误状态下调用它的方法是常见的错误和bug。


    释放MediaPlayer
    一个MediaPlayer会花费昂贵的系统资源。因此,你一定要非常注意,不要在不需要的时候长时间持有一个MediaPlayer instance。当你需要
    它做的事情完成了之后,你应该调用release()来确保创建它时分配的资源都被释放。例如,当你使用MediaPlayer时,你的activity收到了
    一个onStop()调用,你必须释放MediaPlayer,因为当你的activity不与用户交互的时候,你持有它也没有太多的意义(除非你正在后台播放

    ,这将在下节中讨论)。当你的activity醒来或者重新启动的时候,当然在你重新播放之前,你需要创建一个新的MediaPlayer并准备它。

    下面展示了如何释放并赋值为空你的MediaPlayer。

    mediaPlayer.release();
    mediaPlayer = null;

    作为例子,可以考虑当你的activity被停止的时候,没有释放MediaPlayer,但是当activity重新启动的时候又创建了一个新的,将会发生什么。你可能知道,当旋转屏幕的时候(或者通过其他方法改变设备配置),系统将重新启动activity(默认),因此当用户来回旋转设备时你将很快耗尽系统资源,因为每一次旋转你都创建了一个MediaPlayer而没有释放它(关于运行时重启的更多信息,请参考Handling Runtime 
    Changes(http://developer.android.com/guide/topics/resources/runtime-changes.html))。

    你可能像知道如果你想在用户离开你的activity时仍然在后台播放时发生了什么,就像built-in的音乐应用程序的动作。在这种情况下,你需要的是一个由Service控制的MediaPlayer,这将在下一节Using a Service with MediaPlayer中讨论。

    Using a Service with MediaPlayer
    如果你想当你的应用程序不在屏幕上时仍然播放媒体,也就是说你想在用户和其他应用程序交互时继续播放,此时你需要启动一个Service并
    使用其控制MediaPlayer。你必须小心该设置,因为用户和系统对运行一个后台服务的应用程序如何与系统交互是有预期的。如果你的应用程
    序不满足这些预期,将会是很差的用户体验。本节描述了一些主要事项,已经处理它们的建议。


    异步执行


    首先,想activity一样,默认情况下Service的所有工作也是在一个单独的线程中完成的。事实上,如果你在同一个应用程序中运行一个activity和一个service,默认情况下它们将使用同一个线程(主线程)。因此,service需要很快地处理到来的意图,并且在答复它们的时候从来不执行长时间的计算。如果需要繁重的工作或者阻塞调用,你必须异步处理那些任务:使用你自己实现的线程,或者使用框架提供的异步处理工具。

    比如,你在主线程中使用MediaPlayer时,你应该调用prepareAsync()而不是prepare(),并且需要实现个MediaPlayer.OnPreparedListener,以便接收准备完成之后到通知,并开始播放。例如:
    public class MyService extends Service implements MediaPlayer.OnPreparedListener {
        private static final ACTION_PLAY = "com.example.action.PLAY";
        MediaPlayer mMediaPlayer = null;
    
    
        public int onStartCommand(Intent intent, int flags, int startId) {
            ...
            if (intent.getAction().equals(ACTION_PLAY)) {
                mMediaPlayer = ... // initialize it here
                mMediaPlayer.setOnPreparedListener(this);
                mMediaPlayer.prepareAsync(); // prepare async to not block main thread
            }
        }
        /** Called when MediaPlayer is ready */
        public void onPrepared(MediaPlayer player) {
            player.start();
        }
    }


    处理异步错误
    在异步操作中,错误一般会被作为异常或者error code来通知。但是,当你使用异步资源的时候,你应该确保你的应用程序得到恰当的错误通知。在使用MediaPlayer的情况下,你可以实装MediaPlayer.OnErrorListener来实现,并将其设置到你的MediaPlayer instance中:
    public class MyService extends Service implements MediaPlayer.OnErrorListener {
        MediaPlayer mMediaPlayer;
        public void initMediaPlayer() {
            // ...initialize the MediaPlayer here...
    
            mMediaPlayer.setOnErrorListener(this);
        }
        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
            // ... react appropriately ...
            // The MediaPlayer has moved to the Error state, must be reset!
        }
    }

    重要的是要记住,当一个错误产生时,MediaPlayer将移动到Error状态,如果你再想使用它,就必须reset

    Using wake locks

    当设计一个在后台播放媒体的应用程序的时候,当service正在运行的时候,设备可能会sleep。因为Android系统想在设备sleep的时候保护电池,所以系统将关掉一些不需要的电话功能,包括CPU和WiFi硬件。不过,如果你的service正在播放或者streaming音乐,你将不希望系统干涉你的播放。
    为了保证你的service在那些情况下可以继续执行,你必须使用wake locks。一个wake lock是一种方式,一通知系统你的应用程序正在使用
    一些机能,它们在电话idle的时候也应该是可用的。
    注意:你需要节约地使用worke lock,并在只有在真正需要的时候才使用。因为它们会明显缩短设备的电池寿命。

    为了确保cpu在你的MediaPlayer播放的时候继续执行,你需要在初始化你的MediaPlayer的时候调用setWakeMode()。这样,MediaPlayer在播放的时候将拥有一个特殊的lock,并且在暂停或停止的时候是否它。
    mMediaPlayer = new MediaPlayer();
    // ... other initialization here ...
    mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

    不过,这儿的wake lock只能保证cpu不睡着。如果你正在使用WiFi,并且通过网络播放媒体流,你可能还需要获取一个WifiLock,你必须手动地获取和释放它。因此,当你开始准备一个使用远程URL的MediaPlayer时,你应该创建并获取WiFi Loc。例如:
    WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
        .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
    wifiLock.acquire();

    当你暂停或停止媒体,或者你不再需要网络的时候,你应该释放这个lock。
    wifiLock.release();


    Running as a foreground service

    Service经常被用于执行后台任务,如接收邮件,同步数据,下载内容等。在这些情况下,用户不知道这些服务的运行,甚至当服务被中断或
    者晚启动时也不会被通知。
    但是,考虑播放音乐的service。很明显,用户清楚地知道该service,并且这个体验将受到中断的严格影响。另外,它也是一个用户想在其执行过程中与其交互的service。在这种情况下,服务应该作为前台服务来运行。一个前台服务在系统中拥有很高的重要性----系统几乎不会杀死该服务,因为它对用户是最重要的。当运行与前台的时候,服务必须提供一个进度条通知,以保证用户知晓服务的运行,并且允许他们打开一个activity以能与之交互。

    为了将你的service变为前台service,你必须为进度条创建一个通知(Notification),并在service中调用startForeground()。例如:
    String songName;
    // assign the song name to songName
    PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0,
                    new Intent(getApplicationContext(), MainActivity.class),
                    PendingIntent.FLAG_UPDATE_CURRENT);
    Notification notification = new Notification();
    notification.tickerText = text;
    notification.icon = R.drawable.play0;
    notification.flags |= Notification.FLAG_ONGOING_EVENT;
    notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample",
                    "Playing: " + songName, pi);
    startForeground(NOTIFICATION_ID, notification);
    
    当你的服务在前台运行的时候,你配置的通知在设备的notification area中是可见的。如果用户选择了这个通知,系统叫调用你提供的
    PendingIntent。在上例中,它打开了一个activity。

    图-1 展示了你的通知书如何表现给用户的:

    图-1. 点击一个前台服务的通知,在左图中展示了状态掉,右图为展开图。
    你应该只在你的服务确实在展示用户需要知晓的东西时才持有"foreground service"。当不再需要时,你应该调用stopForeground()来释放
    它:
    stopForeground(true);
    更多信息,请参考关于Service和Status Bar Notifications的文档。

    Handling audio focus

    尽管在给定时候只能有一个activity运行,Android仍然是一个多任务的环境。这对使用audio的应用将是一个很大的挑战,因为只有一个音频输出,而很多媒体服务将为了使用它而竞争。在Android2.2之前,没有built-in机制来解决这个问题,在某些情况下这会导致很差的用户体验。例如,一个其他应用程序需要在用户正在听歌的时候通知用户一个很重要的事情,由于大声的音乐,用户可能听不到通知的声音。Android2.2之后,系统为应用程序使用音频输出设备提供了谈判机制。该机制称为Audio Focus。

    当你的应用程序需要输出音频,例如音乐或者通知,你需要请求audio focus。当它拥有了focus,它可以自由地输出声音,但它同时必须监听focus的变动。需要注意的是,当它失去了focus,它需要马上关闭音频或者将声音调低到一定的音量,并且在重获focus后重新开始播放。Audio Focus事实上是可商量的。应用程序被期望是遵守audio focus规则的,但系统并不强制该规则。如果一个应用程序想在市区focus后仍然大声播放音乐,系统对此并不会做什么。不过,用户将得到一次很差的体验,并且可能会删除这个疯子应用。

    如下,你应该调用AudioManager的requestAudioFocus()来请求focus。
    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
        AudioManager.AUDIOFOCUS_GAIN);
    if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
        // could not get audio focus.
    }
    requestAudioFocus()的第一个参数是AudioManager.OnAudioFocusChangeListener,它的onAudioFocusChange()将在audio focus发生改变的
    时候被调用。因此,你应该做service和activities中实现该接口。例如:
    class MyService extends Service
                    implements AudioManager.OnAudioFocusChangeListener {
        // ....
        public void onAudioFocusChange(int focusChange) {
            // Do something based on focus change...
        }
    }

    参数focusChange将告诉你,focus发生了什么改变,取值如下:
    AUDIOFOCUS_GAIN:你得到了focus。
    AUDIOFOCUS_LOSS:你失去了focus,并且是个可能比较长的时间。你必须停止音频播放。因为你可能长时间得不到focus。这儿,你应该    尽量清除你的资源。例如,你应该释放MediaPlayer。
    AUDIOFOCUS_LOSS_TRANSIENT:你暂时失去了focus,不过应该会很快得到它。你应该停止所有的播放,不过你可以保有你的资源。
    AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:你失去了focus,但你还被允许继续播放。你可以把音量调低到一定程度而不是关闭它。

    下面是个实现的例子:
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                // resume playback
                if (mMediaPlayer == null) initMediaPlayer();
                else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
                mMediaPlayer.setVolume(1.0f, 1.0f);
                break;
            case AudioManager.AUDIOFOCUS_LOSS:
                // Lost focus for an unbounded amount of time: stop playback and release media player
                if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
                mMediaPlayer.release();
                mMediaPlayer = null;
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                // Lost focus for a short time, but we have to stop
                // playback. We don't release the media player because playback
                // is likely to resume
                if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                // Lost focus for a short time, but it's ok to keep playing
                // at an attenuated level
                if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
                break;
        }
    }

    需要注意的是,只有在Android2.2(API level 8 )之后才支持该功能。如果你要使用该功能,你应该实现无缝的向后兼容。
    你可以通过反射调用audio focus 方法,或者自己在一个单独的类中实现audio focus功能,以实现向后兼容。下面是一个实现的例子代码。
    public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
        AudioManager mAudioManager;
        // other fields here, you'll probably hold a reference to an interface
        // that you can use to communicate the focus changes to your Service
        public AudioFocusHelper(Context ctx, /* other arguments here */) {
            mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
            // ...
        }
        public boolean requestFocus() {
            return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
                mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN);
        }
        public boolean abandonFocus() {
            return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
                mAudioManager.abandonAudioFocus(this);
        }
        @Override
        public void onAudioFocusChange(int focusChange) {
            // let your service know about the focus change
        }
    }

    你可以在系统运行在API level 8或者之前版本的时候才去创建一个AudioFocusHelper类,例如:
    if (android.os.Build.VERSION.SDK_INT >= 8) {
        mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
    } else {
        mAudioFocusHelper = null;
    }



    Performing cleanup

    如前面所述,MediaPlayer会耗费掉不少系统资源,你应该只在需要的时候才持有它,并在完成工作后调用release()去释放它。重要的是你应该明确地去清除它而不是依赖与垃圾收集器,因为垃圾收集器只关心内存的需求,而不关心媒体相关资源的短期。因此,当你使用service的时候,你应该重载函数onDestroy(),以确保你释放了MediaPlayer。
    public class MyService extends Service {
       MediaPlayer mMediaPlayer;
       // ...
       @Override
       public void onDestroy() {
           if (mMediaPlayer != null) mMediaPlayer.release();
       }
    }

    你应该经常去寻找释放MediaPlayer的机会,例如被关闭的时候。例如,如果你不想播放过长的时间,你应该清楚地释放你的MediaPlayer,并且在稍后再创建它。如果你只是暂时的停止播放,你应该保有你的MediaPlayer,以避免创建和准备是的花费。


    Handling the AUDIO_BECOMING_NOISY Intent

    很多写的好的应用程序会在收到一个将音频变为噪音的事件是自动停止播放。比如,用户正在通过耳机收听音乐,耳机从设备突然断开了连接。不过,这个动作并不是自动发生的。如果你不实现该功能,音频可能会从扩展喇叭播放出来,这可能不是用户想要的。
    你可以通过处理ACTION_AUDIO_BECOMING_NOISY intent以确保你的应用程序在这种情况下停止播放,你可以在你的manifest添加以下代码,注册一个接收器来实现:
    <receiver android:name=".MusicIntentReceiver">
       <intent-filter>
          <action android:name="android.media.AUDIO_BECOMING_NOISY" />
       </intent-filter>
    </receiver>

    该注册将类MusicIntentReceiver作为那个intent的一个广播接收器。你应该实现这个类:
    public class MusicIntentReceiver implements android.content.BroadcastReceiver {
       @Override
       public void onReceive(Context ctx, Intent intent) {
          if (intent.getAction().equals(
                        android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
              // signal your service to stop playback
              // (via an Intent, for instance)
          }
       }
    }




    媒体播放器的另外一个有用的功能是恢复用户以前的播放。你可以通过询问ContentResolver来实现:

    ContentResolver contentResolver = getContentResolver();
    Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    Cursor cursor = contentResolver.query(uri, null, null, null, null);
    if (cursor == null) {
        // query failed, handle error.
    } else if (!cursor.moveToFirst()) {
        // no media on the device
    } else {
        int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
        int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
        do {
           long thisId = cursor.getLong(idColumn);
           String thisTitle = cursor.getString(titleColumn);
           // ...process entry...
        } while (cursor.moveToNext());
    }
    将其和MediaPlayer一起使用的例子:
    long id = /* retrieve it from somewhere */;
    Uri contentUri = ContentUris.withAppendedId(
            android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
    mMediaPlayer = new MediaPlayer();
    mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    mMediaPlayer.setDataSource(getApplicationContext(), contentUri);
    // ...prepare and start...


    Playing JET content

    Android平台包含一个JET引擎,它使你可以在应用程序中添加与JET音频内容播放的交互。你可以在编写基于SDK的应用程序时,使用JetCreator创建一个用于交互的JET content。在应用程序中播放或者管理JET content,可以使用类JetPlayer。关于JET概念的描述以及类JetPlayer的使用方法,请参考JetCreator User Manual
    (http://developer.android.com/guide/topics/media/jet/jetcreator_manual.html)。该工具在Windows,OS X和Linux平台下都可用。下面是一个如何从SD卡上的.jet文件设置JET播放的例子:
    JetPlayer jetPlayer = JetPlayer.getJetPlayer();
    jetPlayer.loadJetFile("/sdcard/level1.jet");
    byte segmentId = 0;
    // queue segment 5, repeat once, use General MIDI, transpose by -1 octave
    jetPlayer.queueJetSegment(5, -1, 1, -1, 0, segmentId++);
    // queue segment 2
    jetPlayer.queueJetSegment(2, -1, 0, 0, 0, segmentId++);
    jetPlayer.play();


    SDK中包含了一个应用程序的例子JetBoy,它展示了如何用JetPlayer在你的游戏中创建一个交互声道。它还展示了如何使用JET事件以同步音
    乐和游戏逻辑。应用程序位置:<sdk>/platforms/android-1.5/samples/JetBoy。


    Performing Audio Capture

    Audio capture from the device is a bit more complicated than audio and video playback, but still fairly simple:

    Create a new instance of android.media.MediaRecorder.
    Set the audio source using MediaRecorder.setAudioSource(). You will probably want to use MediaRecorder.AudioSource.MIC.
    Set output file format using MediaRecorder.setOutputFormat().
    Set output file name using MediaRecorder.setOutputFile().
    Set the audio encoder using MediaRecorder.setAudioEncoder().
    Call MediaRecorder.prepare() on the MediaRecorder instance.
    To start audio capture, call MediaRecorder.start().
    To stop audio capture, call MediaRecorder.stop().

    When you are done with the MediaRecorder instance, call MediaRecorder.release() on it. Calling MediaRecorder.release() is always recommended to free the resource immediately.


    Example: Record audio and play the recorded audio
    The example class below illustrates how to set up, start and stop audio capture, and to play the recorded audio file.
    /*
     * The application needs to have the permission to write to external storage
     * if the output file is written to the external storage, and also the
     * permission to record audio. These permissions must be set in the
     * application's AndroidManifest.xml file, with something like:
     *
     * <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     * <uses-permission android:name="android.permission.RECORD_AUDIO" />
     *
     */
    package com.android.audiorecordtest;
    
    import android.app.Activity;
    import android.widget.LinearLayout;
    import android.os.Bundle;
    import android.os.Environment;
    import android.view.ViewGroup;
    import android.widget.Button;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.content.Context;
    import android.util.Log;
    import android.media.MediaRecorder;
    import android.media.MediaPlayer;
    import java.io.IOException;
    public class AudioRecordTest extends Activity
    {
        private static final String LOG_TAG = "AudioRecordTest";
        private static String mFileName = null;
        private RecordButton mRecordButton = null;
        private MediaRecorder mRecorder = null;
        private PlayButton   mPlayButton = null;
        private MediaPlayer   mPlayer = null;
        private void onRecord(boolean start) {
            if (start) {
                startRecording();
            } else {
                stopRecording();
            }
        }
        private void onPlay(boolean start) {
            if (start) {
                startPlaying();
            } else {
                stopPlaying();
            }
        }
        private void startPlaying() {
            mPlayer = new MediaPlayer();
            try {
                mPlayer.setDataSource(mFileName);
                mPlayer.prepare();
                mPlayer.start();
            } catch (IOException e) {
                Log.e(LOG_TAG, "prepare() failed");
            }
        }
        private void stopPlaying() {
            mPlayer.release();
            mPlayer = null;
        }
        private void startRecording() {
            mRecorder = new MediaRecorder();
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mRecorder.setOutputFile(mFileName);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            try {
                mRecorder.prepare();
            } catch (IOException e) {
                Log.e(LOG_TAG, "prepare() failed");
            }
            mRecorder.start();
        }
        private void stopRecording() {
            mRecorder.stop();
            mRecorder.release();
            mRecorder = null;
        }
        class RecordButton extends Button {
            boolean mStartRecording = true;
            OnClickListener clicker = new OnClickListener() {
                public void onClick(View v) {
                    onRecord(mStartRecording);
                    if (mStartRecording) {
                        setText("Stop recording");
                    } else {
                        setText("Start recording");
                    }
                    mStartRecording = !mStartRecording;
                }
            };
            public RecordButton(Context ctx) {
                super(ctx);
                setText("Start recording");
                setOnClickListener(clicker);
            }
        }
        class PlayButton extends Button {
            boolean mStartPlaying = true;
            OnClickListener clicker = new OnClickListener() {
                public void onClick(View v) {
                    onPlay(mStartPlaying);
                    if (mStartPlaying) {
                        setText("Stop playing");
                    } else {
                        setText("Start playing");
                    }
                    mStartPlaying = !mStartPlaying;
                }
            };
            public PlayButton(Context ctx) {
                super(ctx);
                setText("Start playing");
                setOnClickListener(clicker);
            }
        }
        public AudioRecordTest() {
            mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
            mFileName += "/audiorecordtest.3gp";
        }
        @Override
        public void onCreate(Bundle icicle) {
            super.onCreate(icicle);
            LinearLayout ll = new LinearLayout(this);
            mRecordButton = new RecordButton(this);
            ll.addView(mRecordButton,
                new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    0));
            mPlayButton = new PlayButton(this);
            ll.addView(mPlayButton,
                new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT,
                    0));
            setContentView(ll);
        }
        @Override
        public void onPause() {
            super.onPause();
            if (mRecorder != null) {
                mRecorder.release();
                mRecorder = null;
            }
            if (mPlayer != null) {
                mPlayer.release();
                mPlayer = null;
            }
        }
    }

    END!

    =============================================

    Meet so Meet. C plusplus I-PLUS....
  • 相关阅读:
    牛客网剑指offer第46题——孩子们的游戏(圆圈中最后剩下的数)
    不借助临时变量两数交换篇
    牛客网剑指offer第48题——不用加减乘除做加法
    牛客网剑指offer第44题——翻转单词顺序列
    双指针索引技术
    二叉树的下一个结点
    数组中的逆序对
    丑数
    野指针与空指针
    【转】以操作系统的角度述说线程与进程
  • 原文地址:https://www.cnblogs.com/iplus/p/4467353.html
Copyright © 2020-2023  润新知