GVKun编程网logo

Android MediaPlayer架构 -- MediaPlayer的创建过程 Android MediaPlayer架构 -- 前言小知识点(一)

34

本文的目的是介绍AndroidMediaPlayer架构--MediaPlayer的创建过程AndroidMediaPlayer架构--前言小知识点的详细情况,特别关注一的相关信息。我们将通过专业的研

本文的目的是介绍Android MediaPlayer架构 -- MediaPlayer的创建过程 Android MediaPlayer架构 -- 前言小知识点的详细情况,特别关注的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解Android MediaPlayer架构 -- MediaPlayer的创建过程 Android MediaPlayer架构 -- 前言小知识点的机会,同时也不会遗漏关于(Android MediaPlayer)如果MediaPlayer.create()隐式调用prepare(),我应该如何调用setAudioStreamType()?、Android MediaPlayer、android MediaPlayer API、Android Mediaplayer MediaController超时的知识。

本文目录一览:

Android MediaPlayer架构 -- MediaPlayer的创建过程 Android MediaPlayer架构 -- 前言小知识点(一)

Android MediaPlayer架构 -- MediaPlayer的创建过程 Android MediaPlayer架构 -- 前言小知识点(一)

本文系作者自己学习之所用,文章内容仅出自作者拙劣之思考,问题之处烦请不吝指教。

  MediaPlayer 能被用来控制音/视频文件或流媒体的回放。Android中以MediaPlayer类作为音视频播放的基础类,围绕着他开展了一系列的处理。学习一个新的模块,最简单的步骤就是找到一个典型的应用程序,通过它的实现,来分析整个模块的数据流和控制流。典型的MediaPlayer在Java处的接口包括视频播放类VideoView以及音频专用MediaPlayer类。

  一、 一个简单的视频播放demo app

  Android中实现视频的播放可以采用MediaPlayer+SurfaceView配合的方式,其实Android还为开发人员提供了另外一种更简单的播放视频媒体的方式,那就是VideoView。VideoView类,其实质是用MediaPlayer类来实现的,只是由于其是视频播放,不得不和Surfaceview挂上够,才将其独立出来。使得其有如下的结构:

1 public class VideoView extends SurfaceView
2         implements MediaPlayerControl,SubtitleController.Anchor {
3     private static final String TAG = "VideoView";
4     ......
5 }

  在Android中,提供了VideoView组件用于播放视频文件。想要使用VideoView组件播放视频,首先需要在布局文件中创建该组件,然后在Activity中获取该组件,并应用其setVideoPath()方法或setVideoURI()方法加载要播放的视频,最后调用start()方法来播放视频。另外,VideoView组件还提供了stop()和pause()方法,用于停止或暂停视频的播放。

  在APP中,VideoView的典型简单使用如下:

1     mMediaController =new MediaController(this);
2     mVideoView = (VideoView) findViewById(R.id.videoView); 
3     mVideoView.setVideoPath("/sdcard/1080P24FPS.mp4"); // 设置档案路径
4     mVideoView.setMediaController(mMediaController);  设置播放器的控制器
5     mVideoView.start();  开始播放

  先看看效果就是下面这个样子,短短几行代码一个播放器就做好了,还可以进行暂停,快进,快退,进度条控制。

  PS:VideoView还提供许多其他播放控制API,在此不做重点介绍,以上代码也仅仅是个人demo,难免有误,谨慎参考使用。

二、 VideoView中setVideoPath的处理

  任何华丽的语言都不如source code来的简单直接,上代码:

 1     /**
 2      * Sets video path.
 3      *
 4      * @param path the path of the video.
 5      */
 6     void setVideoPath(String path) {
 7         setVideoURI(Uri.parse(path));
 8     }
 9 
10     11      * Sets video URI.
12 13  uri the URI of the video.
14      15      setVideoURI(Uri uri) {
16         setVideoURI(uri,null17 18 
19     20      * Sets video URI using specific headers.
21 22  uri     the URI of the video.
23  headers the headers for the URI request.
24      *                Note that the cross domain redirection is allowed by default,but that can be
25      *                changed with key/value pairs through the headers parameter with
26      *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
27      *                to disallow or allow cross domain redirection.
28      29     void setVideoURI(Uri uri,Map<String,String> headers) {
30         mUri = uri;
31         mHeaders = headers;
32         mSeekWhenPrepared = 033         openVideo();  openVideo的处理,让最终的处理权交给了MediaPlayer
34         requestLayout();
35         invalidate();
36     }

  经过setVideoPath(String path) --> setVideoURI(Uri uri) --> setVideoURI(Uri uri,1)"> headers) 的调用流程,程序最终来到了openVideo()这一函数中:

 openVideo() {
            ......
 3             mMediaPlayer = new MediaPlayer();
 4 
 5             mMediaPlayer.setDataSource(mContext,mUri,mHeaders);
 6             mMediaPlayer.setdisplay(mSurfaceHolder);
 7             
            mMediaPlayer.prepareAsync();
 9             .......
10     }

  在上面的代码中可以清楚的看到,我们首先new 了一个MediaPlayer类的对象,然后去setDataSource,到这里VideoView::openVideo的处理让最终的处理权交给了MediaPlayer。接下来我们就进入MediaPlayer的世界.

三、 MediaPlayer的世界

  3.1 new MediaPlayer对象过程

  首先关注MediaPlayer对象的创建过程,这也是分析android源码的一个基本要求。依次通过Java --> JNI(libmedia_jni.so) -- > Frameworks(libmedia.so)的处理流程。

  MediaPlayer.java 构造函数,这一部分在 Android MediaPlayer架构 -- 前言小知识点(一)也有分析

    public MediaPlayer() {
        super( AudioAttributes.Builder().build());

        Looper looper;
        if ((looper = Looper.myLooper()) != ) {
            mEventHandler = new EventHandler(,looper);
        } else if ((looper = Looper.getMainLooper()) != else {
            mEventHandler = ;
        }

        mTimeProvider = new TimeProvider();
        mOpenSubtitleSources = new Vector<InputStream>();

        /* Native setup requires a weak reference to our object.
         * It's easier to create it here than in C++.
         */
        native_setup(new WeakReference<MediaPlayer>());
    }

  可以看到,在使用VideoView中到创建MediaPlayer会经过:new VideoView——> new MediaPlayer ——>native_setup 这样一个典型的对象建立过程,并传递到JNI。

  native_setup主要用于本地C++层的对象的建立,在JNI代码(frameworks\base\media\jni\android_media_MediaPlayer.cpp)中可以找到对应的native函数:

 1 void
 2 android_media_MediaPlayer_native_setup(jnienv *env,jobject thiz,jobject weak_this)
{
 4     ALOGV("native_setup" 5     sp<MediaPlayer> mp = new MediaPlayer();  实例化一个native MediaPlayer(frameworks\av\media\libmedia\mediaplayer.cpp)
if (mp == NULL) {
 7         jniThrowException(env,"java/lang/RuntimeException","Out of memory" 8         return10 
11      create new listener and give it to MediaPlayer
12     sp<JNIMediaPlayerListener> listener =  JNIMediaPlayerListener(env,thiz,weak_this);
13     mp->setListener(listener);
14 
 Stow our new C++ MediaPlayer in an opaque field in the Java object.
16     setMediaPlayer(env,mp);
17 }

  进入JNI做android_media_MediaPlayer_native_setup处理:sp<MediaPlayer> mp = new MediaPlayer() 这个native MediaPlayer会去和media service进行交互实现真正的播放功能,使得最终处理进入C++的世界。   

  3.2 setDataSource过程

  MediaPlayer java class中提供了多种setDataSource方法来设置不同的URI播放流,在此我们以播放本地档案为例来介绍处理流程:

  VideoView::setVideoURI() --> MediaPlayer::setDataSource(mContext,mHeaders); --> MediaPlayer::setDataSource(uri.toString()) --> MediaPlayer::setDataSource(path,null,null) --> MediaPlayer::setDataSource(fd) --> setDataSource(fd,0,0x7ffffffffffffffL) --> _setDataSource(fd,offset,length)

  最后会调到 _setDataSource(fd,length),看这个方法被声明为 native method

1     native  _setDataSource(MediaDataSource dataSource)
2           throws IllegalArgumentException,IllegalStateException;

  在JNI层我们找到该方法对应的JNI method实现:

1     {"_setDataSource","(Ljava/io/FileDescriptor;JJ)V",(void *)android_media_MediaPlayer_setDataSourceFD},

    android_media_MediaPlayer_setDataSourceFD()方法定义如下:


android_media_MediaPlayer_setDataSourceFD(jnienv * getMediaPlayer(env,thiz);
     NULL ) {
        jniThrowException(env,"java/lang/IllegalStateException";
    }

    if (fileDescriptor == NULL) {
        jniThrowException(env,"java/lang/IllegalArgumentException";
    }
    int fd = jniGetFDFromFileDescriptor(env,fileDescriptor);
    ALOGV("setDataSourceFD: fd %d"Failed." );
}

  上面这段代码可以看到最终调用了status_t MediaPlayer::setDataSource(int fd,int64_t offset,int64_t length)

//*********************************************************************************************************************************************************************

  MediaPlayer的C++代码位于/frameworks/av/media/libmedia/mediaplayer.cpp, 编译后形成一个libmedia.so。

  下面来看这个API的处理,接下去都只分析framework层的C++的处理流

 

 1 status_t MediaPlayer::setDataSource(int fd,int64_t offset,int64_t length)
 3     ALOGV("setDataSource(%d,%" PRId64 )" 4     status_t err = UNKNowN_ERROR;
 5     const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
 7         sp<IMediaPlayer> player(service->create(dioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
 9             (NO_ERROR != player->setDataSource(fd,length))) {
10             player.clear();
        }
12         err = attachNewPlayer(player);
14      err;
15 }

 

  典型的Binder C/S架构,获取MediaPlayerService的proxy,通过MediaPlayerService来创建一个player,然后对这个player调用setDataSource

  3.3 MediaPlayerService的工作

  启动与获取

  MediaPlayerService同其他的Binder Service一样,作为一个server对外提供服务,它是在mediaserver中启动的:

  /frameworks/av/media/mediaserver/main_mediaserver.cpp

 

int main(int argc __unused,1)">char **argv __unused)
    signal(SIGPIPE,SIG_IGN);
 5     sp<Processstate> proc(Processstate::self());
 6     sp<IServiceManager> sm(defaultServiceManager());
 7     ALOGI(ServiceManager: %p",sm.get());
    InitializeIcuOrDie();
 9     MediaPlayerService::instantiate(); 启动MediaPlayerService
    ResourceManagerService::instantiate();
    registerExtensions();
12     Processstate::self()->startThreadPool();
13     IPCThreadState::self()->joinThreadPool();
14 }

   在/frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中对instantiate()方法的定义:

 MediaPlayerService::instantiate() {
2     defaultServiceManager()->addService(
3             String16(media.player"),1)"> MediaPlayerService());
4 }

 

   在上面这段代码中我们注册了一个名为“media.player"的Binder Service,也就是MediaPlayerService,之后就可以通过 binder = sm->getService(String16("media.player"));来请求这个服务了

   Player的创建

  获取MediaPlayerService后就要去create player: sp<IMediaPlayer> player(service->create(thisdioSessionId));

  create请求处理:

  

 

(Android MediaPlayer)如果MediaPlayer.create()隐式调用prepare(),我应该如何调用setAudioStreamType()?

(Android MediaPlayer)如果MediaPlayer.create()隐式调用prepare(),我应该如何调用setAudioStreamType()?

我正在编写一个使用服务的 Android闹钟应用程序来播放闹钟.目前,我可以获得播放的音频,但播放的形式可以通过关闭设备的音量进行静音.因此,我试图添加一个调用setAudioStreamType(AudioManager.STREAM_ALARM);以防止这种情况.

我为我的onStartCommand()函数提供以下服务:

MediaPlayer mMP;    
@Override
    public int onStartCommand(Intent intent,int flags,int startId)
    {
        try
        {
            mMP = MediaPlayer.create(this,R.raw.alarm);
            mMP.setAudioStreamType(AudioManager.STREAM_ALARM);
            mMP.setLooping(true);
            //mMP.prepare(); commented out since prepare() is called in create
        }
        catch (Exception e)
        {
            e.printstacktrace();
        }
        if (mMP != null) mMP.start();

        return START_STICKY;
    }

我的问题是,通过调用setAudioStreamType(),MediaPlayer从不播放音频.如果我评论该行,音频播放.

有了行,我得到以下运行时错误:

04-10 19:32:03.115: E/MediaPlayer(3411): setAudioStream called in state 8

04-10 19:32:03.115: E/MediaPlayer(3411): error (-38,0)

04-10 19:32:03.115: E/MediaPlayer(3411): start called in state 0

04-10 19:32:03.115: E/MediaPlayer(3411): error (-38,0)

04-10 19:32:03.115: E/MediaPlayer(3411): Error (-38,0)

有些研究(现在我找不到链接)告诉我,在调用prepare()之后,setAudioStreamType()不能被调用,而create()隐含地调用prepare().

在任何方面,我应该如何设置AudioStreamType()没有这样的错误?

解决方法

您可以调用mp.reset(),然后设置流类型,数据源,然后准备.或者,只需使用默认构造函数并自己处理初始化.

编辑:

Resources res = getResources();
AssetFileDescriptor afd = res.openRawResourceFd(R.raw.alarm);

mp.reset();
mp.setAudioStreamType(AudioManager.STREAM_ALARM);
mp.setLooping(true);
mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength());
mp.prepare();
mp.start();

Android MediaPlayer

Android MediaPlayer

1.通过静态方法构造

MediaPlayer.create(Context context,int resid);

2.构造方法:         MediaPlayer();

设置媒体源:        setDataSource(String path);

3.设置是否循环:

setLooping(boolean)

 

4.播放:

a.    prepare()         预处理,调用start()前必须先调用此方法

b.    start()              如果没有正在播放,就开始播放

c.    pause()            如果palyer不为空,并且正在播放就暂停

d.    stop()              如果palyer不为空,并且正在播放就停止

e.    isPlaying()       是否正在播放

android MediaPlayer API

android MediaPlayer API

OSC 请你来轰趴啦!1028 苏州源创会,一起寻宝 AI 时代
MediaPlayer 类可用于控制音频 / 视频文件或流的播放。关于如何使用这个类的方法还可以阅读 VideoView 类的文档。


1.状态图

对播放音频 / 视频文件和流的控制是通过一个状态机来管理的。下图显示一个 MediaPlayer 对象被支持的播放控制操作驱动的生命周期和状态。椭圆代表 MediaPlayer 对象可能驻留的状态。弧线表示驱动 MediaPlayer 在各个状态之间迁移的播放控制操作。这里有两种类型的弧线。由一个箭头开始的弧代表同步的方法调用,而以双箭头开头的代表的弧线代表异步方法调用

 



通过这张图,我们可以知道一个 MediaPlayer 对象有以下的状态:

1)当一个 MediaPlayer 对象被刚刚用 new 操作符创建或是调用了 reset() 方法后,它就处于 Idle 状态。当调用了 release() 方法后,它就处于 End 状态。这两种状态之间是 MediaPlayer 对象的生命周期。

1.1) 在一个新构建的 MediaPlayer 对象和一个调用了 reset() 方法的 MediaPlayer 对象之间有一个微小的但是十分重要的差别。在处于 Idle 状态时,调用 getCurrentPosition()getDuration()getVideoHeight()getVideoWidth()setAudioStreamType(int)setLooping(boolean)setVolume(float, float)pause()start()stop()seekTo(int)prepare() 或者 prepareAsync() 方法都是编程错误。当一个 MediaPlayer 对象刚被构建的时候,内部的播放引擎和对象的状态都没有改变,在这个时候调用以上的那些方法,框架将无法回调客户端程序注册的 OnErrorListener.onError() 方法;但若这个 MediaPlayer 对象调用了 reset() 方法之后,再调用以上的那些方法,内部的播放引擎就会回调客户端程序注册的 OnErrorListener.onError() 方法了,并将错误的状态传入。

1.2) 我们建议,一旦一个 MediaPlayer 对象不再被使用,应立即调用 release() 方法来释放在内部的播放引擎中与这个 MediaPlayer 对象关联的资源。资源可能包括如硬件加速组件的单态组件,若没有调用 release() 方法可能会导致之后的 MediaPlayer 对象实例无法使用这种单态硬件资源,从而退回到软件实现或运行失败。一旦 MediaPlayer 对象进入了 End 状态,它不能再被使用,也没有办法再迁移到其它状态。

1.3) 此外,使用 new 操作符创建的 MediaPlayer 对象处于 Idle 状态,而那些通过重载的 create() 便利方法创建的 MediaPlayer 对象却不是处于 Idle 状态。事实上,如果成功调用了重载的 create() 方法,那么这些对象已经是 Prepare 状态了。 


2) 在一般情况下,由于种种原因一些播放控制操作可能会失败,如不支持的音频 / 视频格式,缺少隔行扫描的音频 / 视频,分辨率太高,流超时等原因,等等。因此,错误报告和恢复在这种情况下是非常重要的。有时,由于编程错误,在处于无效状态的情况下调用了一个播放控制操作可能发生。在所有这些错误条件下,内部的播放引擎会调用一个由客户端程序员提供的 OnErrorListener.onError() 方法。客户端程序员可以通过调用 MediaPlayer.setOnErrorListener(android.media.MediaPlayer.OnErrorListener)方法来注册 OnErrorListener.

2.1) 一旦发生错误,MediaPlayer 对象会进入到 Error 状态。

2.2) 为了重用一个处于 Error 状态的 MediaPlayer 对象,可以调用 reset() 方法来把这个对象恢复成 Idle 状态。

2.3) 注册一个 OnErrorListener 来获知内部播放引擎发生的错误是好的编程习惯。

2.4) 在不合法的状态下调用一些方法,如 prepare()prepareAsync() setDataSource() 方法会抛出 IllegalStateException 异常。 


3) 调用 setDataSource(FileDescriptor) 方法,或 setDataSource(String) 方法,或 setDataSource(Context,Uri) 方法,或 setDataSource(FileDescriptor,long,long) 方法会使处于 Idle 状态的对象迁移到 Initialized 状态。


3.1) 若当此 MediaPlayer 处于其它的状态下,调用 setDataSource() 方法,会抛出 IllegalStateException 异常。

3.2) 好的编程习惯是不要疏忽了调用 setDataSource() 方法的时候可能会抛出的 IllegalArgumentException 异常和 IOException 异常。 


4) 在开始播放之前,MediaPlayer 对象必须要进入 Prepared 状态。

4.1) 有两种方法(同步和异步)可以使 MediaPlayer 对象进入 Prepared 状态:要么调用 prepare() 方法(同步),此方法返回就表示该 MediaPlayer 对象已经进入了 Prepared 状态;要么调用 prepareAsync() 方法(异步),此方法会使此 MediaPlayer 对象进入 Preparing 状态并返回,而内部的播放引擎会继续未完成的准备工作。当同步版本返回时或异步版本的准备工作完全完成时就会调用客户端程序员提供的 OnPreparedListener.onPrepared() 监听方法。可以调用 MediaPlayer.setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener) 方法来注册 OnPreparedListener.

4.2) Preparing 是一个中间状态,在此状态下调用任何具备边影响的方法的结果都是未知的!

4.3) 在不合适的状态下调用 prepare() prepareAsync() 方法会抛出 IllegalStateException 异常。当 MediaPlayer 对象处于 Prepared 状态的时候,可以调整音频 / 视频的属性,如音量,播放时是否一直亮屏,循环播放等。 


5) 要开始播放,必须调用 start() 方法。当此方法成功返回时,MediaPlayer 的对象处于 Started 状态。isPlaying() 方法可以被调用来测试某个 MediaPlayer 对象是否在 Started 状态。

5.1) 当处于 Started 状态时,内部播放引擎会调用客户端程序员提供的 OnBufferingUpdateListener.onBufferingUpdate() 回调方法,此回调方法允许应用程序追踪流播放的缓冲的状态。

5.2) 对一个已经处于 Started 状态的 MediaPlayer 对象调用 start() 方法没有影响。


6) 播放可以被暂停,停止,以及调整当前播放位置。当调用 pause() 方法并返回时,会使 MediaPlayer 对象进入 Paused 状态。注意 Started Paused 状态的相互转换在内部的播放引擎中是异步的。所以可能需要一点时间在 isPlaying() 方法中更新状态,若在播放流内容,这段时间可能会有几秒钟。

6.1) 调用 start() 方法会让一个处于 Paused 状态的 MediaPlayer 对象从之前暂停的地方恢复播放。当调用 start() 方法返回的时候,MediaPlayer 对象的状态会又变成 Started 状态。

6.2) 对一个已经处于 Paused 状态的 MediaPlayer 对象 pause() 方法没有影响。


7) 调用 stop() 方法会停止播放,并且还会让一个处于 StartedPausedPrepared PlaybackCompleted 状态的 MediaPlayer 进入 Stopped 状态。

7.1) 对一个已经处于 Stopped 状态的 MediaPlayer 对象 stop() 方法没有影响。


8) 调用 seekTo() 方法可以调整播放的位置。

8.1) seekTo(int) 方法是异步执行的,所以它可以马上返回,但是实际的定位播放操作可能需要一段时间才能完成,尤其是播放流形式的音频 / 视频。当实际的定位播放操作完成之后,内部的播放引擎会调用客户端程序员提供的 OnSeekComplete.onSeekComplete() 回调方法。可以通过 setOnSeekCompleteListener(OnSeekCompleteListener) 方法注册。

8.2) 注意,seekTo(int) 方法也可以在其它状态下调用,比如 PreparedPaused PlaybackCompleted 状态。此外,目前的播放位置,实际可以调用 getCurrentPosition() 方法得到,它可以帮助如音乐播放器的应用程序不断更新播放进度


9) 当播放到流的末尾,播放就完成了。

9.1) 如果调用了 setLooping(boolean) 方法开启了循环模式,那么这个 MediaPlayer 对象会重新进入 Started 状态。

9.2) 若没有开启循环模式,那么内部的播放引擎会调用客户端程序员提供的 OnCompletion.onCompletion() 回调方法。可以通过调用 MediaPlayer.setOnCompletionListener(OnCompletionListener) 方法来设置。内部的播放引擎一旦调用了 OnCompletion.onCompletion() 回调方法,说明这个 MediaPlayer 对象进入了 PlaybackCompleted 状态。

9.3) 当处于 PlaybackCompleted 状态的时候,可以再调用 start() 方法来让这个 MediaPlayer 对象再进入 Started 状态。 

Android Mediaplayer MediaController超时

Android Mediaplayer MediaController超时

香港专业教育学院实现了流媒体的MP3 URL的mediaplayer和mediacontroller.但是,我在TMobile网络上的设备无法获得很好的3G信号,因此可以在EDGE上运行.我假设媒体播放器因流太慢或不完整而崩溃,是否可以设置超时?

解决方法:

MediaPlayer中没有超时方法,但是您可以自己实现-有多种方法可以执行.
我建议其中之一,我用了自己,对我有用-broadcastReceiver
代码如下所示:

public class ConnectivityCheckingReceiver extends WakefulbroadcastReceiver
{
  private AlarmManager alarmManager;
  private PendingIntent pendingIntent;

  @Override
  public void onReceive(Context context, Intent intent)
  {
    if (MusicService.mediaPlayer != null)
    {
        if (!MusicService.mediaPlayer.isPlaying())
            Log.v("Music", "Music is NOT playing"); 
            //stop service and notify user
        else
            Log.v("Music", "Music is playing");
    }
    else
    {
        Log.v("Music", "User stopped player");
    }
  }
  public void setAlarm (Context context, int hour, int minute, int second)
  {
    alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, ConnectivityCheckingReceiver.class);
    pendingIntent = PendingIntent.getbroadcast(context, 0, intent, 0);
    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.HOUR_OF_DAY, hour);
    calendar.set(Calendar.MINUTE, minute);
    calendar.set(Calendar.SECOND, second);
    alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);      
  }
}

在活动/服务/片段中添加以下行:

ConnectivityCheckingReceiver conCheck = new ConnectivityCheckingReceiver();
conCheck.setAlarm(context, hour, min, second);

您将需要自己实现小时/分钟/秒检查逻辑,但是使用Joda Time之类的库就可以轻松实现.
并且不要忘记添加到清单中:

<uses-permission android:name="android.permission.WAKE_LOCK" />
<receiver android:name=".receivers.ConnectivityCheckingReceiver" />

附言:我的解决方案不是完美的,但是我没有看到关于此问题的任何好答案,因此,如果找到一个,请分享.

关于Android MediaPlayer架构 -- MediaPlayer的创建过程 Android MediaPlayer架构 -- 前言小知识点的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于(Android MediaPlayer)如果MediaPlayer.create()隐式调用prepare(),我应该如何调用setAudioStreamType()?、Android MediaPlayer、android MediaPlayer API、Android Mediaplayer MediaController超时等相关知识的信息别忘了在本站进行查找喔。

本文标签: