Android媒体播放器MediaPlayer架构介绍教程

 更新时间:2016年9月20日 19:58  点击:1679
MediaPlayer是Android中很重要也最为复杂的媒体播放器,本文我们来介绍一下MediaPlayer架构,其他一些Android的应用程序也使用类似的架构。

本文主要介绍的是Android中很重要也最为复杂的媒体播放器(MediaPlayer)部分的架构。对于Android这样一个完整又相对复杂的系统,一个MediaPlayer功能的实现不在其具体的功能,而是具体功能如何适应Android系统Android MediaPlayer的主要具体实现在OpenCore的Player中,这部分不是本文的关注点。本文关注的是MediaPlayer系统的架构,其他的一些Android的应用程序也使用类似的架构。

第一部分 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类的路径:

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的各个库之间的结构比较复杂,可以用下图的表示

Android媒体播放器MediaPlayer架构介绍教程

 

在各个库中,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库和调用的关系如下图所示:

Android媒体播放器MediaPlayer架构介绍教程

 

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

2.2 头文件IMediaPlayerClient.h

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

class IMediaPlayerClient: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaPlayerClient);
    virtual void notify(int msg, int ext1, int ext2) = 0;
};

class BnMediaPlayerClient: public BnInterface<IMediaPlayerClient>
{
public:
    virtual status_t  onTransact( uint32_t code,
                                  const Parcel& data,
                                  Parcel* reply,
                                  uint32_t flags = 0);
};

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

2.3 头文件mediaplayer.h

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

class MediaPlayer : public BnMediaPlayerClient
{
public:
    MediaPlayer();
    ~MediaPlayer();
    void onFirstRef();
    void disconnect();
    status_t    setDataSource(const char *url);
    status_t    setDataSource(int fd, int64_t offset, int64_t length);
    status_t    setVideoSurface(const sp<Surface>& surface);
    status_t    setListener(const sp<MediaPlayerListener>& listener);
    status_t    prepare();
    status_t    prepareAsync();
    status_t    start();
    status_t    stop();
    status_t    pause();
    bool        isPlaying();
    status_t    getVideoWidth(int *w);
    status_t    getVideoHeight(int *h);
    status_t    seekTo(int msec);
    status_t    getCurrentPosition(int *msec);
    status_t    getDuration(int *msec);
    status_t    reset();
    status_t    setAudioStreamType(int type);
    status_t    setLooping(int loop);
    status_t    setVolume(float leftVolume, float rightVolume);
    void    notify(int msg, int ext1, int ext2);
    static    sp<IMemory>    decode(const char* url, uint32_t *pSampleRate, int* pNumChannels);
    static    sp<IMemory>    decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels);
//……
}

从接口中可以看出MediaPlayer类刚好实现了一个MediaPlayer的基本操作,例如播放(start)、停止(stop)、暂停(pause)等。

另外的一个类DeathNotifier在MediaPlayer类中定义,它继承了IBinder类中的DeathRecipient类:

class DeathNotifier: public IBinder:: DeathRecipient
{
public:
    DeathNotifier() {}
    virtual ~DeathNotifier();
    virtual void binderDied(const wp<IBinder>& who);
};

事实上,MediaPlayer类正是间接地继承了IBinder,而MediaPlayer:: DeathNotifier类继承了IBinder:: DeathRecipient,这都是为了实现进程间通讯而构建的。

2.4 头文件IMediaPlayer.h

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

class IMediaPlayer: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaPlayer);
    virtual void    disconnect() = 0;
    virtual status_t    setVideoSurface(const sp<ISurface>& surface) = 0;
    virtual status_t    prepareAsync() = 0;
    virtual status_t    start() = 0;
    virtual status_t    stop() = 0;
    virtual status_t    pause() = 0;
    virtual status_t    isPlaying(bool* state) = 0;
    virtual status_t    getVideoSize(int* w, int* h) = 0;
    virtual status_t    seekTo(int msec) = 0;
    virtual status_t    getCurrentPosition(int* msec) = 0;
    virtual status_t    getDuration(int* msec) = 0;
    virtual status_t    reset() = 0;
    virtual status_t    setAudioStreamType(int type) = 0;
    virtual status_t    setLooping(int loop) = 0;
    virtual status_t    setVolume(float leftVolume, float rightVolume) = 0;
};
class BnMediaPlayer: public BnInterface<IMediaPlayer>
{
public:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,   
                                    Parcel* reply,
                                    uint32_t flags = 0);
};

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

2.5 头文件IMediaPlayerService.h

class IMediaPlayerService: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaPlayerService);
    virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;
    virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
    virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels) = 0;
    virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels) = 0;
};
class BnMediaPlayerService: public BnInterface<IMediaPlayerService>
{
public:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);
};

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

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

3.1 JAVA程序部分

在packages/apps/Music/src/com/android/music/目录的MediaPlaybackService.java文件中,包含了对MediaPlayer的调用。

在MediaPlaybackService.java中包含对包的引用:

import android.media.MediaPlayer;

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

    private class MultiPlayer {
        private MediaPlayer mMediaPlayer = new MediaPlayer();
    }

 

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

  mMediaPlayer.reset();
  mMediaPlayer.setDataSource(path);
  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,如下所示:

static JNINativeMethod gMethods[] = {
    {"setDataSource", "(Ljava/lang/String;)V", (void *)android_media_MediaPlayer_setDataSource},
    {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
    {"prepare", "()V", (void *)android_media_MediaPlayer_prepare},
    {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
    {"_start", "()V", (void *)android_media_MediaPlayer_start},
    {"_stop", "()V", (void *)android_media_MediaPlayer_stop},
    {"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
    {"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},
    {"seekTo", "(I)V", (void *)android_media_MediaPlayer_seekTo},
    {"_pause", "()V", (void *)android_media_MediaPlayer_pause},
    {"isPlaying", "()Z", (void *)android_media_MediaPlayer_isPlaying},
    {"getCurrentPosition", "()I", (void *)android_media_MediaPlayer_getCurrentPosition},
    {"getDuration", "()I", (void *)android_media_MediaPlayer_getDuration},
    {"_release", "()V", (void *)android_media_MediaPlayer_release},
    {"_reset", "()V", (void *)android_media_MediaPlayer_reset},
    {"setAudioStreamType", "(I)V", (void *)android_media_MediaPlayer_setAudioStreamType},
    {"setLooping", "(Z)V", (void *)android_media_MediaPlayer_setLooping},
    {"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
    {"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
    {"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
    {"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
}

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

其中android_media_MediaPlayer_reset函数的实现如下所示:

static void android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
{
    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
    if (mp == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException", NULL);
        return;
    }
    process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
}

在android_media_MediaPlayer_reset的调用中,得到一个MediaPlayer指针,通过对它的调用实现实际的功能。

register_android_media_MediaPlayer用于将gMethods注册为的类"android/media/MediaPlayer",其实现如下所示。

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

3.3 mediaplayer的核心库libmedia.so

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

const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()
{
    Mutex::Autolock _l(mServiceLock);
    if (mMediaPlayerService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
    do {
        binder = sm->getService(String16("media.player"));
        if (binder != 0)
        break;
        LOGW("MediaPlayerService not published, waiting...");
        usleep(500000); // 0.5 s
        } while(true);
        if (mDeathNotifier == NULL) {
        mDeathNotifier = new DeathNotifier();
    }
        binder->linkToDeath(mDeathNotifier);
        mMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
    }
    LOGE_IF(mMediaPlayerService==0, "no MediaPlayerService!?");
    return mMediaPlayerService;
}

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

一个具体的函数setDataSource如下所示:

status_t MediaPlayer::setDataSource(const char *url)
{
    LOGV("setDataSource(%s)", url);
    status_t err = UNKNOWN_ERROR;
    if (url != NULL) {
        const sp<IMediaPlayerService>& service(getMediaPlayerService());
        if (service != 0) {
            sp<IMediaPlayer> player(service->create(getpid(), this, url));
            err = setDataSource(player);
        }
    }
    return err;
}

在函数setDataSource函数中,调用getMediaPlayerService得到了一个IMediaPlayerService,又从 IMediaPlayerService中得到了IMediaPlayer类型的指针,通过这个指针进行着具体的操作。

其他一些函数的实现也与setDataSource类似。

libmedia.so中的其他一些文件与头文件的名称相同,它们是:

libs/media/IMediaPlayerClient.cpp

libs/media/IMediaPlayer.cpp

libs/media/IMediaPlayerService.cpp

为了实现Binder的具体功能,在这些类中还需要实现一个BpXXX的类,例如IMediaPlayerClient.cpp的实现如下所示:l

class BpMediaPlayerClient: public BpInterface<IMediaPlayerClient>
{
public:
    BpMediaPlayerClient(const sp<IBinder>& impl)
    : BpInterface<IMediaPlayerClient>(impl){}
    virtual void notify(int msg, int ext1, int ext2)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IMediaPlayerClient::getInterfaceDescriptor());
        data.writeInt32(msg);
        data.writeInt32(ext1);
        data.writeInt32(ext2);
        remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
    }
};

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

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

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

3.4 media服务libmediaservice.so

frameworks/base/medialibmediaplayerservice目录中的MediaPlayerService.h和MediaPlayerService.cpp用于实现一个

servers/media/的服务,MediaPlayerService是继承BnMediaPlayerService的实现,在这个类的内部又定义了类Client,MediaPlayerService::Client继承了BnMediaPlayer。

    class MediaPlayerService : public BnMediaPlayerService
    {
        class Client : public BnMediaPlayer
    }

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

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

在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函数如下所示:

static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, notify_callback_f notifyFunc)
{
    sp<MediaPlayerBase> p;
    switch (playerType) {
        case PV_PLAYER:
            LOGV(" create PVPlayer");
            p = new PVPlayer();
            break;
        case SONIVOX_PLAYER:
            LOGV(" create MidiFile");
            p = new MidiFile();
            break;
        case VORBIS_PLAYER:
            LOGV(" create VorbisPlayer");
            p = new VorbisPlayer();
            break;
    }
    //……
    return p;
}

在这里根据playerType的类型建立不同的播放器:对于大多数情况,类型将是PV_PLAYER,这时会调用了new PVPlayer()建立一个PVPlayer,然后将其指针转换成MediaPlayerBase来使用;对于Mini文件的情况,类型为 SONIVOX_PLAYER,将会建立一个MidiFile;对于Ogg Vorbis格式的情况,将会建立一个VorbisPlayer。

(OGG Vobis是一种音频压缩格式,与MP3等的音乐格式类似,它具有完全免费、开放和没有专利限制的特点。)

值得注意的是PVPlayer、MidiFile和VorbisPlayer三个类都是继承MediaPlayerInterface得到的,而 MediaPlayerInterface又是继承MediaPlayerBase得到的,因此三者具有相同接口类型。只有建立的时候会调用各自的构造函数,在建立之后,将只通过MediaPlayerBase接口来MediaPlayerBase控制它们。

在frameworks/base/media/libmediaplayerservice目录中,MidiFile.h和MidiFile.cpp 的实现MidiFile,VorbisPlayer.h和VorbisPlayer.cpp实现一个VorbisPlayer。

3.5 OpenCorePlayer的实现libopencoreplayer.so

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

Activity是Android组件中最基本也是最为常见用的四大组件(Activity,Service服务,Content Provider内容提供者,BroadcastReceiver广播接收器)之一。Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务。

首先需要明白任务栈:

栈是先进后出,activity才用不同的启动模式,当每次访问他时会采取完全不同的操作。

1、标准模式

android:launchMode=”standard” />

每次访问activity都会再栈内创建一个实例

2、栈顶模式--singleTop

如果访问一个activity时,该activity的时候刚好在栈顶,那么不会再次实例化对象,而是访问这个栈顶已经存在的实例,比如一个activity设置的是栈顶模式,当在他自己界面再往自己界面跳转的时候就不会再实例化一个自己实例出来。

3、singleTask

如果你访问的这个activity,已经在栈中有了一个实例,那么就不会再次生成activity的实例,而是访问这个已经在栈中存在的实例,并且将所有在该栈上面的activity全部移除,这样它就是在栈顶了。

4、单例模式--singleInstance

比如浏览器,如果一个程序打开了浏览器,另外一个程序又要打开浏览器,那么就会跳转到已经打开的浏览器界面,这样可以避免资源的浪费。

在windows 7 64 bit下下载安装Android Stuido后打不开开发环境,经常一翻努力解决把问题解决,现在整理出来分享给大家学习参考。

1.win7 64 bit,下载安装后打不开,一般是因为需要一个jdk的环境变量,在我的计算机-->属性-->高级-->系统环境变量新建一个JAVA_HOME的环境变量,然后value=你的JDK安装路径;

2.安装好以后运行stuido,新建工程如果会提示缺少或需要更新到22更高的版本的话,首先将eclipse下adt更新到最新的version22,然后点android studio设置-->默认工程-->工程结构-->配置(大概是这个),然后会看到一个jDK与android SDK的配置,点击编辑,将之前eclipse对应的sandroid sdk的路径映射到自己的开发环境,android target 选择google4.2.2的版本。

3.然后新建工程应该没有什么问题,可能创建的过程中会遇到下载一些文件失败的提示,不用管,也可以去设置里面配置一些网络代理。基本就会进入android studio界面;

4.关于代码自动提示功能,感觉这个版本一般,eclipse配置一些也可以自动提示,不过有一个设配多种分辨率与多语言编辑倒是很方便。

5.单元测试功能做的用户体验很棒;

6.很多快捷键与一般常用的都差不多,有些区别,编译时ctrl+F9,运行是shift+F10.希望早点做个基于android系统的androidstudio,这样就可以彻底放弃windows了。

7.最后我有一个重大发现,上次win7自动更新后居然支持了shell命令?ls,mv,mkdir很多命令居然可以用了。。

google ,人类的希望!!!!!!!!!!!!

经过一段时间的使用,总结了android studio打不开等问题的6种解决方法及android studio注意事项,希望对大家有所帮助。

1 首次运行,建立好项目需要下载一些东西,如果提示一些错误,请核实错误再次打开下载,即可运行,可能是下载的时候没有下载完全

2 检查jdk路径是否配置,1.6和1.7都没有问题。

3 win7的话用管理员身份运行试试

4 在bin目录中找到studio.exe.vmoptions去掉里面包含UseCodeCacheFlushing的那行

5 用记事本打开android-studiobin 目录下的studio.bat

将 SET VM_OPTIONS_FILE=%IDE_BIN_DIR%studio%BITS%.exe.vmoptions

改为 SET VM_OPTIONS_FILE=%IDE_BIN_DIR%studio%BITS%.exe

就可以使用了

6 用文本工具打开

studio.bat

line25 to line 28:

SET JRE=%JDK%

IF EXIST "%JRE%jre" SET JRE=%JDK%jre

SET BITS=

IF EXIST "%JRE%libamd64" SET BITS=64

可以看到里面设置软件支持系统位数是64位,而我自己所用电脑是32位的 jre/lib目录下只有i386文件

尝试把

IF EXIST "%JRE%libamd64" SET BITS=64 改为

IF EXIST "%JRE%libi386" SET BITS=32

google i/o大会发布了开发工具, Android Studio。

早上下载完毕,尝试一把,无奈网速实在不给力,先看官网,列举几条关于她的几个事项

一 几个特点

1 该开发工具基于 IntelliJ IDEA.,来个 IntelliJ IDEA的截图。是不是和eclipse很像?

2 对android更好的支持编译修复

3 可以更好的对程序性能、兼容性和其他问题进行控制捕捉

4 混淆和签名功能

5创建常见的Android的设计和组件的向导

6 自带布局编辑器,允许你拖动和拖放用户界面组件,预览多个屏幕配置上的布局

7 还有更多

二 注意事项

1 Android Studio 只是发布了初步的版本,还不稳定,有的功能还没有开发完毕,用的时候可能会导致一些问题。

三 官网工具包包含

地址:http://developer.android.com/sdk/installing/studio.html#download

1 初步的studio工具

2 所有的android sdk 设计、测试工具

3最新的android编译的插件

4最新的android系统镜像文件

一个Android Studio,大家的热情都很高涨嘛。本人之前使用过一段时间的IDEA,所以对于上手Android Studio还是比较容易的,现在就介绍一下常用的设置

特别说明一下,Android Studio 要求的Android SDK的版本不低于Version22,同时还需要Build Tools的版本不低于Version17,否则在创建新工程的时候会出现错误。但是导入工程是没有问题的。

先贴个File菜单的截图,以供大家查看

1.中文乱码

在打开的窗口中,找到IDE Settings下的Appearance,在右侧勾选上“Override default fonts by”,然后在第一个下拉框中选择字体为“simsun”,然后apply,重启IDE,就好了。

2.设置快捷键

依然是在打开的settings窗口中,找到IDE Settings下的keymap,右侧打开的就是快捷键了

右键单击要修改的快捷键,会弹出一个菜单,选择“Add keyboard shortcut”就可以修改快捷键了。删除的话,在弹出的菜单中选择remove XXX就行了

3.修改主题

找到IDE Settings下的Appearance,右侧的Theme选择自己喜欢的主体就好了。个人比较喜欢Darcula这个主题

4.导入Eclipse工程

选择File->Import Project,在弹出的菜单中选择要导入的工程就好了,选择好以后就直接next,在第二个窗口中也选择默认的第一个选项就可以。

需要注意的是,在Android Studio中,有两种工程,一个是Project,一个是Module,其中Project相当于Eclipse的workspace,Module相当于Eclipse的project

5.导入jar

选择File->Projcet Structure,在弹出的窗口中左侧找到Libraries并选中,然后点击“+”,并选择Java就能导入Jar包了

6.删除项目

好吧,这个东西貌似用到的人比较多,其实很简单,Android Studio把删除叫做“Remove”。。。。菜单中显示“Remove XXX”,就是删除的选项

7.修改工程目录

在创建项目的时候,在Project Location中选好工程目录后,要自己输入一个文件夹的名字用来保存工程(至少我第一次使用自己的工程目录时,是这么做的),然后就能使用自己的工程目录了。

8.字体大小

点设置按钮,然后找到 Editor-colors&font-font

默认的不让修改 所以先点击save as  随便点个名字  ,然后 size就可以修改了,其它颜色什么的也类似....

9 更改工作空间说明

好多人问android studio怎么更改工作空间,我这里统一说明一下,

android studio是基于idea,而idea和eclipse有大的区别,有好处也有不好的地方,在一段时间里,idea被认为是开发java最好用强大的ide工具,

大家在用的时候也发现了,新建的时候有new application和new module开发。

idea没有工作空间这样的说法。

android studio是单工程的开发方式

android studio中的application相当于eclipse里的workspace

android studio中的module相当于eclipse里的project

如果你要更改你项目的目录,可以在新建的时候更改。

[!--infotagslink--]

相关文章

  • Android子控件超出父控件的范围显示出来方法

    下面我们来看一篇关于Android子控件超出父控件的范围显示出来方法,希望这篇文章能够帮助到各位朋友,有碰到此问题的朋友可以进来看看哦。 <RelativeLayout xmlns:an...2016-10-02
  • 如何使用JavaScript实现无缝滚动自动播放轮播图效果

    这篇文章主要介绍了如何使用JavaScript实现“无缝滚动 自动播放”轮播图效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-08-20
  • Android开发中findViewById()函数用法与简化

    findViewById方法在android开发中是获取页面控件的值了,有没有发现我们一个页面控件多了会反复研究写findViewById呢,下面我们一起来看它的简化方法。 Android中Fin...2016-09-20
  • Android模拟器上模拟来电和短信配置

    如果我们的项目需要做来电及短信的功能,那么我们就得在Android模拟器开发这些功能,本来就来告诉我们如何在Android模拟器上模拟来电及来短信的功能。 在Android模拟...2016-09-20
  • 夜神android模拟器设置代理的方法

    夜神android模拟器如何设置代理呢?对于这个问题其实操作起来是非常的简单,下面小编来为各位详细介绍夜神android模拟器设置代理的方法,希望例子能够帮助到各位。 app...2016-09-20
  • android自定义动态设置Button样式【很常用】

    为了增强android应用的用户体验,我们可以在一些Button按钮上自定义动态的设置一些样式,比如交互时改变字体、颜色、背景图等。 今天来看一个通过重写Button来动态实...2016-09-20
  • Android WebView加载html5页面实例教程

    如果我们要在Android应用APP中加载html5页面,我们可以使用WebView,本文我们分享两个WebView加载html5页面实例应用。 实例一:WebView加载html5实现炫酷引导页面大多...2016-09-20
  • 深入理解Android中View和ViewGroup

    深入理解Android中View和ViewGroup从组成架构上看,似乎ViewGroup在View之上,View需要继承ViewGroup,但实际上不是这样的。View是基类,ViewGroup是它的子类。本教程我们深...2016-09-20
  • Android自定义WebView网络视频播放控件例子

    下面我们来看一篇关于Android自定义WebView网络视频播放控件开发例子,这个文章写得非常的不错下面给各位共享一下吧。 因为业务需要,以下代码均以Youtube网站在线视...2016-10-02
  • Android用MemoryFile文件类读写进行性能优化

    java开发的Android应用,性能一直是一个大问题,,或许是Java语言本身比较消耗内存。本文我们来谈谈Android 性能优化之MemoryFile文件读写。 Android匿名共享内存对外A...2016-09-20
  • Android设置TextView竖着显示实例

    TextView默认是横着显示了,今天我们一起来看看Android设置TextView竖着显示如何来实现吧,今天我们就一起来看看操作细节,具体的如下所示。 在开发Android程序的时候,...2016-10-02
  • android.os.BinderProxy cannot be cast to com解决办法

    本文章来给大家介绍关于android.os.BinderProxy cannot be cast to com解决办法,希望此文章对各位有帮助呀。 Android在绑定服务的时候出现java.lang.ClassCastExc...2016-09-20
  • Android 实现钉钉自动打卡功能

    这篇文章主要介绍了Android 实现钉钉自动打卡功能的步骤,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下...2021-03-15
  • Android 开发之布局细节对比:RTL模式

    下面我们来看一篇关于Android 开发之布局细节对比:RTL模式 ,希望这篇文章对各位同学会带来帮助,具体的细节如下介绍。 前言 讲真,好久没写博客了,2016都过了一半了,赶紧...2016-10-02
  • Android中使用SDcard进行文件的读取方法

    首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
  • Android开发之PhoneGap打包及错误解决办法

    下面来给各位简单的介绍一下关于Android开发之PhoneGap打包及错误解决办法,希望碰到此类问题的同学可进入参考一下哦。 在我安装、配置好PhoneGap项目的所有依赖...2016-09-20
  • 用Intel HAXM给Android模拟器Emulator加速

    Android 模拟器 Emulator 速度真心不给力,, 现在我们来介绍使用 Intel HAXM 技术为 Android 模拟器加速,使模拟器运行度与真机比肩。 周末试玩了一下在Eclipse中使...2016-09-20
  • Android判断当前屏幕是全屏还是非全屏

    在安卓开发时我碰到一个问题就是需要实现全屏,但又需要我们来判断出用户是使用了全屏或非全屏了,下面我分别找了两段代码,大家可参考。 先来看一个android屏幕全屏实...2016-09-20
  • Android开发中布局中的onClick简单完成多控件时的监听的利与弊

    本文章来为各位介绍一篇关于Android开发中布局中的onClick简单完成多控件时的监听的利与弊的例子,希望这个例子能够帮助到各位朋友. 首先在一个控件加上这么一句:and...2016-09-20
  • vue+video.js实现视频播放列表

    这篇文章主要为大家详细介绍了vue+video.js实现视频播放列表,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-29