第一部分 MediaPlayer概述
Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的。
MediaPlayer在底层是基于OpenCore(PacketVideo)的库实现的,为了构建一个MediaPlayer程序,上层还包含了进程间通讯等内容,这种进程间通讯的基础是Android基本库中的Binder机制。
以开源的Android为例MediaPlayer的代码主要在以下的目录中:
JAVA程序的路径:
packages/apps/Music/src/com/android/music/

JAVA Framework的路径:
frameworks/base/media/java /android/media/MediaPlayer.java

JAVA本地调用部分(JNI):
frameworks/base/media/jni/android_media_MediaPlayer.cpp
这部分内容编译成为目标是libmedia_jni.so 。

主要的头文件在以下的目录中:
frameworks/base/include/media/

多媒体底层库在以下的目录中:
frameworks/base/media/libmedia/
这部分的内容被编译成库libmedia.so 。

多媒体服务部分:
frameworks/base/media/libmediaplayerservice/
文件为mediaplayerservice.h和mediaplayerservice.cpp
这部分内容被编译成库libmediaplayerservice.so 。

基于OpenCore的多媒体播放器部分
external/opencore/
这部分内容被编译成库libopencoreplayer.so 。

从程序规模上来看,libopencoreplayer.so 是主要的实现部分,而其他的库基本上都是在其上建立的封装和为建立进程间通讯的机制。

第二部分 MediaPlayer的接口与架构

2.1 整体框架图
MediaPlayer的各个库之间的结构比较复杂,可以用下图的表示:

下载 (73 KB)

2009-5-13 15:09

在各个库中,libmedia.so位于核心的位置,它对上层的提供的接口主要是MediaPlayer类,类libmedia_jni.so通过调用 MediaPlayer类提供对JAVA的接口,并且实现了android.media.MediaPlayer类。
libmediaplayerservice.so是Media的服务器,它通过继承libmedia.so的类实现服务器的功能,而libmedia.so中的另外一部分内容则通过进程间通讯和libmediaplayerservice.so进行通讯。
libmediaplayerservice.so的真正功能通过调用OpenCore Player来完成。
MediaPlayer部分的头文件在frameworks/base/include/media/ 目录中,这个目录是和libmedia.so库源文件的目录frameworks/base/media/libmedia/ 相对应的。主要的头文件有以下几个:
■IMediaPlayerClient.h
■mediaplayer.h
■IMediaPlayer.h
■IMediaPlayerService.h
■MediaPlayerInterface.h
在这些头文件mediaplayer.h提供了对上层的接口,而其他的几个头文件都是提供一些接口类(即包含了纯虚函数的类),这些接口类必须被实现类继承才能够使用。
整个MediaPlayer库和调用的关系如下图所示:

下载 (158 KB)

2009-5-13 15:09

整个MediaPlayer在运行的时候,可以大致上分成Client和Server两个部分,它们分别在两个进程中运行,它们之间使用Binder机制实现IPC通讯。从框架结构上来看,IMediaPlayerService.h、IMediaPlayerClient.h和 MediaPlayer.h三个类定义了MeidaPlayer的接口和架构,MediaPlayerService.cpp和 mediaplayer.cpp两个文件用于MeidaPlayer架构的实现,MeidaPlayer的具体功能在PVPlayer(库 libopencoreplayer.so)中的实现。

2.2 头文件IMediaPlayerClient.h
IMediaPlayerClient.h用于描述一个MediaPlayer客户端的接口,描述如下所示:

  1. class IMediaPlayerClient: public IInterface
  2. {
  3. public:
  4. DECLARE_META_INTERFACE(MediaPlayerClient);
  5. virtual void notify(int msg, int ext1, int ext2) = 0;
  6. };
  7. class BnMediaPlayerClient: public BnInterface<IMediaPlayerClient>
  8. {
  9. public:
  10. virtual status_t onTransact( uint32_t code,
  11. const Parcel& data,
  12. Parcel* reply,
  13. uint32_t flags = 0);
  14. };

在定义中,IMediaPlayerClient类继承IInterface,并定义了一个MediaPlayer客户端的接口,BnMediaPlayerClient继承了BnInterface<IMediaPlayerClient>,这是为基于 Android的基础类Binder机制实现在进程通讯而构建的。事实上,根据BnInterface类模版的定义 BnInterface<IMediaPlayerClient>类相当于双继承了BnInterface和 ImediaPlayerClient。这是Android一种常用的定义方式。

2.3 头文件mediaplayer.h
mediaplayer.h是对外的接口类,它最主要是定义了一个MediaPlayer类:

  1. class MediaPlayer : public BnMediaPlayerClient
  2. {
  3. public:
  4. MediaPlayer();
  5. ~MediaPlayer();
  6. void onFirstRef();
  7. void disconnect();
  8. status_t setDataSource(const char *url);
  9. status_t setDataSource(int fd, int64_t offset, int64_t length);
  10. status_t setVideoSurface(const sp<Surface>& surface);
  11. status_t setListener(const sp<MediaPlayerListener>&listener);
  12. status_t prepare();
  13. status_t prepareAsync();
  14. status_t start();
  15. status_t stop();
  16. status_t pause();
  17. bool isPlaying();
  18. status_t getVideoWidth(int *w);
  19. status_t getVideoHeight(int *h);
  20. status_t seekTo(int msec);
  21. status_t getCurrentPosition(int *msec);
  22. status_t getDuration(int *msec);
  23. status_t reset();
  24. status_t setAudioStreamType(int type);
  25. status_t setLooping(int loop);
  26. status_t setVolume(float leftVolume, float rightVolume);
  27. void notify(int msg, int ext1, int ext2);
  28. static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels);
  29. static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels);
  30. //……
  31. }

从接口中可以看出MediaPlayer类刚好实现了一个MediaPlayer的基本操作,例如播放(start)、停止(stop)、暂停(pause)等。
另外的一个类DeathNotifier在MediaPlayer类中定义,它继承了IBinder类中的DeathRecipient类:

  1. class DeathNotifier: public IBinder::DeathRecipient
  2. {
  3. public:
  4. DeathNotifier() {}
  5. virtual ~DeathNotifier();
  6. virtual void binderDied(const wp<IBinder>& who);
  7. };

事实上,MediaPlayer类正是间接地继承了IBinder,而MediaPlayer::DeathNotifier类继承了IBinder::DeathRecipient,这都是为了实现进程间通讯而构建的。
这个库和主要类之间的关系如下所示:

2.4 头文件IMediaPlayer.h
IMediaPlayer.h主要的的内容是一个实现MediaPlayer功能的接口,它的主要定义如下所示:

  1. class IMediaPlayer: public IInterface
  2. {
  3. public:
  4. DECLARE_META_INTERFACE(MediaPlayer);
  5. virtual void disconnect() = 0;
  6. virtual status_t setVideoSurface(const sp<ISurface>& surface) = 0;
  7. virtual status_t prepareAsync() = 0;
  8. virtual status_t start() = 0;
  9. virtual status_t stop() = 0;
  10. virtual status_t pause() = 0;
  11. virtual status_t isPlaying(bool* state) = 0;
  12. virtual status_t getVideoSize(int* w, int* h) = 0;
  13. virtual status_t seekTo(int msec) = 0;
  14. virtual status_t getCurrentPosition(int* msec) = 0;
  15. virtual status_t getDuration(int* msec) = 0;
  16. virtual status_t reset() = 0;
  17. virtual status_t setAudioStreamType(int type) = 0;
  18. virtual status_t setLooping(int loop) = 0;
  19. virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
  20. };
  21. class BnMediaPlayer: public BnInterface<IMediaPlayer>
  22. {
  23. public:
  24. virtual status_t onTransact( uint32_t code,
  25. const Parcel& data,
  26. Parcel* reply,
  27. uint32_t flags = 0);
  28. };

在 IMediaPlayer类中,主要定义MediaPlayer的功能接口,这个类必须被继承才能够使用。值得注意的是,这些接口和 MediaPlayer类的接口有些类似,但是它们并没有直接的关系。事实上,在MediaPlayer类的各种实现中,一般都会通过调用 IMediaPlayer类的实现类来完成。

2.5 头文件IMediaPlayerService.h

2.5 头文件IMediaPlayerService.h
IMediaPlayerService.h用于描述一个MediaPlayer的服务,定义方式如下所示:

  1. class IMediaPlayerService: public IInterface
  2. {
  3. public:
  4. DECLARE_META_INTERFACE(MediaPlayerService);
  5. virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;
  6. virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
  7. virtual sp<IMemory>decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0;
  8. virtual sp<IMemory>decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0;
  9. };
  10. class BnMediaPlayerService: public BnInterface<IMediaPlayerService>
  11. {
  12. public:
  13. virtual status_tonTransact( uint32_t code,
  14. const Parcel& data,
  15. Parcel* reply,
  16. uint32_t flags = 0);
  17. };

由于具有纯虚函数,IMediaPlayerService 以及BnMediaPlayerService必须被继承实现才能够使用,在IMediaPlayerService定义的create和decode等接口,事实上是必须被继承者实现的内容。注意,create的返回值的类型是sp<IMediaPlayer>,这个 IMediaPlayer正是提供实现功能的接口。

2.6 头文件MediaPlayerInterface.h
MediaPlayerInterface.h定义的是一个MeidaPlayer底层的接口,这个类的实现者是最终实现媒体播放功能的。
MediaPlayerBase是一个基础的接口类,其主要的接口如下所示:

  1. class MediaPlayerBase : public RefBase
  2. {
  3. public:
  4. // ……
  5. MediaPlayerBase() : mCookie(0), mNotify(0) {}
  6. virtual ~MediaPlayerBase() {}
  7. virtual status_t initCheck() = 0;
  8. virtual bool hardwareOutput() = 0;
  9. virtual status_t setSigBusHandlerStructTLSKey(pthread_key_t key) { return 0; }
  10. virtual status_t setDataSource(const char *url) = 0;
  11. virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0;
  12. virtual status_t setVideoSurface(const sp<ISurface>& surface) = 0;
  13. virtual status_t prepare() = 0;
  14. virtual status_t prepareAsync() = 0;
  15. virtual status_t start() = 0;
  16. virtual status_t stop() = 0;
  17. virtual status_t pause() = 0;
  18. virtual bool isPlaying() = 0;
  19. virtual status_t getVideoWidth(int *w) {return 0;}
  20. virtual status_t getVideoHeight(int *h) {return 0;}
  21. virtual status_t seekTo(int msec) = 0;
  22. virtual status_t getCurrentPosition(int *msec) = 0;
  23. virtual status_t getDuration(int *msec) = 0;
  24. virtual status_t reset() = 0;
  25. virtual status_t setLooping(int loop) = 0;
  26. virtual player_type playerType() = 0;
  27. virtual void setNotifyCallback(void* cookie, notify_callback_f notifyFunc) {
  28. mCookie = cookie; mNotify = notifyFunc; }
  29. // ……
  30. };

MediaPlayerInterface继承了类MediaPlayerBase:

  1. class MediaPlayerInterface : public MediaPlayerBase
  2. {
  3. public:
  4. virtual ~MediaPlayerInterface() { }
  5. virtual bool hardwareOutput() { return false; }
  6. virtual void setAudioSink(const sp<AudioSink>& audioSink) { mAudioSink = audioSink; }
  7. protected:
  8. sp<AudioSink> mAudioSink;
  9. };

MediaPlayerInterface本身还是一个纯虚类,只作为接口使用。MediaPlayerBase和MediaPlayerInterface是底层的接口,这些接口被MediaPlayer的服务所调用,用于实现MediaPlayer的具体功能。

第三部分 MediaPlayer的主要实现分析

第三部分 MediaPlayer的主要实现分析

3.1 JAVA程序部分
在packages/apps/Music/src/com/android/music/ 目录的MediaPlaybackService.java文件中,包含了对MediaPlayer的调用。
在MediaPlaybackService.java中包含对包的引用:

  1. import android.media.MediaPlayer;

在MediaPlaybackService类的内部,定义了MultiPlayer类:

  1. private class MultiPlayer {
  2. private MediaPlayer mMediaPlayer = new MediaPlayer();
  3. }

MultiPlayer类中使用了MediaPlayer类,其中有一些对这个MediaPlayer的调用,调用的过程如下所示:

  1. mMediaPlayer.reset();
  2. mMediaPlayer.setDataSource(path);
  3. mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

reset、setDataSource和setAudioStreamType等接口就是通过JAVA本地调用(JNI)来实现的。

3.2 MediaPlayer的JAVA本地调用部分
MediaPlayer的JAVA本地调用部分在目录frameworks/base/media/jni/的android_media_MediaPlayer.cpp中的文件中实现。
android_media_MediaPlayer.cpp之中定义了一个JNINativeMethod(JAVA本地调用方法)类型的数组gMethods,如下所示:

  1. static JNINativeMethod gMethods[] = {
  2. {"setDataSource", "(Ljava/lang/String;)V",(void *)android_media_MediaPlayer_setDataSource},
  3. {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V",(void *)android_media_MediaPlayer_setDataSourceFD},
  4. {"prepare", "()V",(void *)android_media_MediaPlayer_prepare},
  5. {"prepareAsync","()V",(void *)android_media_MediaPlayer_prepareAsync},
  6. {"_start","()V",(void *)android_media_MediaPlayer_start},
  7. {"_stop", "()V",(void *)android_media_MediaPlayer_stop},
  8. {"getVideoWidth", "()I",(void *)android_media_MediaPlayer_getVideoWidth},
  9. {"getVideoHeight","()I",(void *)android_media_MediaPlayer_getVideoHeight},
  10. {"seekTo","(I)V", (void *)android_media_MediaPlayer_seekTo},
  11. {"_pause","()V",(void *)android_media_MediaPlayer_pause},
  12. {"isPlaying", "()Z",(void *)android_media_MediaPlayer_isPlaying},
  13. {"getCurrentPosition","()I",(void *)android_media_MediaPlayer_getCurrentPosition},
  14. {"getDuration", "()I",(void *)android_media_MediaPlayer_getDuration},
  15. {"_release","()V",(void *)android_media_MediaPlayer_release},
  16. {"_reset","()V",(void *)android_media_MediaPlayer_reset},
  17. {"setAudioStreamType","(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
  18. {"setLooping","(Z)V", (void *)android_media_MediaPlayer_setLooping},
  19. {"setVolume", "(FF)V",(void *)android_media_MediaPlayer_setVolume},
  20. {"getFrameAt","(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
  21. {"native_setup","(Ljava/lang/Object;)V",(void *)android_media_MediaPlayer_native_setup},
  22. {"native_finalize", "()V",(void *)android_media_MediaPlayer_native_finalize},

JNINativeMethod的第一个成员是一个字符串,表示了JAVA本地调用方法的名称,这个名称是在JAVA程序中调用的名称;第二个成员也是一个字符串,表示JAVA本地调用方法的参数和返回值;第三个成员是JAVA本地调用方法对应的C语言函数。
其中android_media_MediaPlayer_reset函数的实现如下所示:

  1. static void
  2. android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
  3. {
  4. sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
  5. if (mp == NULL ) {
  6. jniThrowException(env, "java/lang/IllegalStateException", NULL);
  7. return;
  8. }
  9. process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
  10. }

在android_media_MediaPlayer_reset的调用中,得到一个MediaPlayer指针,通过对它的调用实现实际的功能。
register_android_media_MediaPlayer用于将gMethods注册为的类"android/media/MediaPlayer",其实现如下所示。

  1. static int register_android_media_MediaPlayer(JNIEnv *env)
  2. {
  3. jclass clazz;
  4. clazz = env->FindClass("android/media/MediaPlayer");
  5. // ......
  6. return AndroidRuntime::registerNativeMethods(env, "android/media/MediaPlayer", gMethods, NELEM(gMethods));
  7. }

"android/media/MediaPlayer"对应JAVA的类android.media.MediaPlayer。

3.3 mediaplayer本地库libmedia.so
libs/media/mediaplayer.cpp文件用于实现mediaplayer.h提供的接口,其中一个重要的片段如下所示:

  1. const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()
  2. {
  3. Mutex::Autolock _l(mServiceLock);
  4. if (mMediaPlayerService.get() == 0) {
  5. sp<IServiceManager> sm = defaultServiceManager();
  6. sp<IBinder> binder;
  7. do {
  8. binder = sm->getService(String16("media.player"));
  9. if (binder != 0)
  10. break;
  11. LOGW("MediaPlayerService not published, waiting...");
  12. usleep(500000); // 0.5 s
  13. } while(true);
  14. if (mDeathNotifier == NULL) {
  15. mDeathNotifier = new DeathNotifier();
  16. }
  17. binder->linkToDeath(mDeathNotifier);
  18. mMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
  19. }
  20. LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");
  21. return mMediaPlayerService;
  22. }

其中最重要的一点是binder = sm->getService(String16("media.player"));这个调用用来得到一个名称为"media.player"的服务,这个调用返回值的类型为IBinder,根据实现将其转换成类型IMediaPlayerService使用。
一个具体的函数setDataSource如下所示:

  1. status_t MediaPlayer::setDataSource(const char *url)
  2. {
  3. LOGV("setDataSource(%s)", url);
  4. status_t err = UNKNOWN_ERROR;
  5. if (url != NULL) {
  6. const sp<IMediaPlayerService>& service(getMediaPlayerService());
  7. if (service != 0) {
  8. sp<IMediaPlayer> player(service->create(getpid(), this, url));
  9. err = setDataSource(player);
  10. }
  11. }
  12. return err;
  13. }

在函数setDataSource函数中,调用getMediaPlayerService得到了一个IMediaPlayerService,又从IMediaPlayerService中得到了IMediaPlayer类型的指针,通过这个指针进行着具体的操作。
其他一些函数的实现也与setDataSource类似。
libmedia.so中的其他一些文件与头文件的名称相同,它们是:
libs/media/IMediaPlayerClient.cpp
libs/media/IMediaPlayer.cpp
libs/media/IMediaPlayerService.cpp
为了实现Binder的具体功能,在这些类中还需要实现一个BpXXX的类,例如IMediaPlayerClient.cpp的实现如下所示:

  1. class BpMediaPlayerClient: public BpInterface<IMediaPlayerClient>
  2. {
  3. public:
  4. BpMediaPlayerClient(const sp<IBinder>& impl)
  5. : BpInterface<IMediaPlayerClient>(impl)
  6. {
  7. }
  8. virtual void notify(int msg, int ext1, int ext2)
  9. {
  10. Parcel data, reply;
  11. data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor());
  12. data.writeInt32(msg);
  13. data.writeInt32(ext1);
  14. data.writeInt32(ext2);
  15. remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
  16. }
  17. };

还需要实现定义宏IMPLEMENT_META_INTERFACE,这个宏将被展开,生成几个函数:

  1. IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.hardware.IMediaPlayerClient");

以上的实现都是基于Binder框架的实现方式,只需要按照模版实现即可。其中BpXXX的类为代理类(proxy),BnXXX的类为本地类(native)。代理类的transact函数和本地类的onTransact函数实现对应的通讯。

3.4 media服务libmediaservice.so

3.4 media服务libmediaservice.so
frameworks/base/media/libmediaplayerservice目录中的MediaPlayerService.h和MediaPlayerService.cpp用于实现一个
servers/media/的服务,MediaPlayerService是继承BnMediaPlayerService的实现,在这个类的内部又定义了类Client,MediaPlayerService::Client继承了BnMediaPlayer。

  1. class MediaPlayerService : public BnMediaPlayerService
  2. {
  3. class Client : public BnMediaPlayer
  4. }

在MediaPlayerService中具有如下一个静态函数instantiate:

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

在instantiate函数中,调用IServiceManager的一个函数addService,向其中增加了一个名为"media.player"的服务。
这个名为"media.player"的服务和mediaplayer.cpp中调用getService中得到的使用一样名称。因此,在这里调用 addService增加服务在mediaplayer.cpp中可以按照名称"media.player"来使用。这就是使用Binder实现进程间通讯的(IPC)的作用,事实上这个MediaPlayerService类是在服务中运行的,而mediaplayer.cpp调用的功能在应用中运行,二者并不是一个进程。但是在mediaplayer.cpp却像一个进程的调用一样调用MediaPlayerService的功能。
在MediaPlayerService.cpp中的createPlayer函数如下所示:

  1. static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
  2. notify_callback_f notifyFunc)
  3. {
  4. sp<MediaPlayerBase> p;
  5. switch (playerType) {
  6. case PV_PLAYER:
  7. LOGV(" create PVPlayer");
  8. p = new PVPlayer();
  9. break;
  10. case SONIVOX_PLAYER:
  11. LOGV(" create MidiFile");
  12. p = new MidiFile();
  13. break;
  14. case VORBIS_PLAYER:
  15. LOGV(" create VorbisPlayer");
  16. p = new VorbisPlayer();
  17. break;
  18. }
  19. //……
  20. return p;
  21. }

在这里根据playerType的类型建立不同的播放器:对于大多数情况,类型将是PV_PLAYER,这时会调用了new PVPlayer()建立一个PVPlayer,然后将其指针转换成MediaPlayerBase来使用;对于Mini文件的情况,类型为 SONIVOX_PLAYER,将会建立一个MidiFile;对于Ogg Vorbis格式的情况,将会建立一个VorbisPlayer。
值得注意的是PVPlayer、MidiFile和VorbisPlayer三个类都是继承MediaPlayerInterface得到的,而 MediaPlayerInterface又是继承MediaPlayerBase得到的,因此三者具有相同接口类型。只有建立的时候会调用各自的构造函数,在建立之后,将只通过MediaPlayerBase接口来MediaPlayerBase控制它们。
在frameworks/base/media/libmediaplayerservice目录中,MidiFile.h和MidiFile.cpp的实现
MidiFile,VorbisPlayer.h和VorbisPlayer.cpp实现一个VorbisPlayer。

3.5 OpenCore Player的实现libopencoreplayer.so
OpenCore Player在external/opencore/中实现,这个实现是一个基于OpenCore的Player的实现。具体实现的文件为 playerdriver.cpp。其中实现了两个类:PlayerDriver和PVPlayer。PVPlayer通过调用PlayerDriver 的函数实现具体的功能。

Android的MediaPlayer架构介绍相关推荐

  1. Android音视频入门及架构介绍

    一.多媒体基础 音视频格式 一个视频文件的格式由封装格式+音频码流格式+视频码流格式+字幕流组成. 如下为一个视频文件的信息,是asf封装的wmav2音频+mpeg4视频. 常用的视频格式 MPEG系 ...

  2. Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程

    关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本:基于android4.2之前版本 bluez内核:linux/linux3.08 系统:an ...

  3. Android bluetooth介绍(两): android 蓝牙源架构和uart 至rfcomm过程

    关键词:蓝牙blueZ  UART  HCI_UART H4  HCI  L2CAP RFCOMM  版本号:基于android4.2先前版本 bluez内核:linux/linux3.08 系统:a ...

  4. Android基础-系统架构分析,环境搭建,下载Android Studio,AndroidDevTools,Git使用教程,Github入门,界面设计介绍

    系统架构分析 Android体系结构 安卓结构有四大层,五个部分,Android分四层为: 应用层(Applications),应用框架层(Application Framework),系统运行层(L ...

  5. android cordova原理,cordova架构介绍

    1 cordova 基础介绍 1.1 cordova架构介绍 1.2 cordova环境搭建及通过cordova cli创建cordova app 1.2.1 npm环境搭建 1.2.2 生成hell ...

  6. 2018年android常用的框架介绍

    转载地址:http://blog.csdn.net/RuingMan/article/details/73546718 http://www.cnblogs.com/jincheng-yangchao ...

  7. 【转】 实时Android语音对讲系统架构

    本文属于Android局域网内的语音对讲项目系列,<通过UDP广播实现Android局域网Peer Discovering>实现了局域网内的广播及多播通信,本文将重点说明系统架构,音频信号 ...

  8. 采用Android的MediaPlayer+SurfaceView设计视频播放器

    前言 android视频播放有很多方式(自带videoView等),这里简单说其中一种:MediaPlayer+SurfaceView,一个播放音频,一个播放视频(图像). 大体结构图 BaseMed ...

  9. Android的MediaPlayer

    为什么80%的码农都做不了架构师?>>>    MediaPlayer类可用于控制音频/视频文件或流的播放.关于如何使用这个类的方法还可以阅读VideoView类的文档. 1.状态图 ...

最新文章

  1. 首次成功实施 XSS 攻击,盗取目标网站大量 VIP 帐号
  2. linux-awk的简单应用
  3. 调用函数,整数逆序输出
  4. MOSS Content Types 概述
  5. boost::log::parse_formatter用法的测试程序
  6. 夜视模式,多少猥琐相机假汝之名
  7. 聊聊 computed 影响性能的场景
  8. android中实现简单的播放
  9. LeetCode 984. 不含 AAA 或 BBB 的字符串(贪心)
  10. java动态语言_探秘Java 7:JVM动态语言支持详解
  11. Spring下没有配置事务导致hibernate session报错
  12. 解决微信小程序的video元素层级太高无法遮盖问题
  13. 破产姐妹第六季/全集2 Broke Girls迅雷下载
  14. r语言 月度消费频次_R语言基础-数据分析及常见数据分析方法
  15. word从第三页插入页码
  16. python爬微博超话图片_Python爬虫:《庆余年》人物图谱和微博传播路径
  17. java 嘻嘻哈哈聊天室
  18. 网站的养站(养蜘蛛)技巧
  19. python解常微分方程龙格库_数值常微分方程-欧拉法与龙格-库塔法
  20. unicloud操作数据库(一)——clientDb

热门文章

  1. win11在dev渠道升级怎么保留原来的文件 windows11渠道升级保留原文件的方法步骤
  2. php的toast,Windows通知服务:401尝试在PHP中创建Toast通知时无效令牌
  3. Git:add多个文件或者目录的方式
  4. 微服务配置中心是干啥的_微服务化改造系列之三:配置中心
  5. 洛谷——P1867 【Mc生存】经验值
  6. DOM事件流三个阶段
  7. 2D转换中心点transform-origin(CSS3)
  8. 简约好看导航栏(HTML、CSS)
  9. 学完Java后可从事的十大领域!
  10. OpenCV3.1.0+VS2013测试程序