分析template project之proj.android(2)
1、深入阅读org.cocos2dx.cpp.AppActivity
主Activity “AppActivity” 继承自 Cocos2dxActivity
。Cocos2dxActivity
是一个继承自 Activity
并实现了 Cocos2dxHelperListener
接口的抽象类。
抽象类相关知识
抽象类是不允许实例化的类,使用关键字 abstract
修饰。如果一个类中含有 abstract
方法,那么这个类必须使用 abstract
修饰;反之,abstract
类中可以没有 abstract
方法。抽象类不能有抽象构造函数或抽象静态方法。Abstract
类的子类需要为其父类中的所有抽象方法提供实现,否则它们也是抽象类。虽然不能创建 abstract
类的实例,但可以创建一个类型为抽象类的变量,并让它指向具体子类的一个实例。
Cocos2dxHelperListener 接口
Cocos2dxHelperListener
接口是一个 public static interface
,定义了三个 public
函数:showDialog
、showEditTextDialog
、runOnGLThread
。接口必须由具体类实现才有意义,所以是 public
;接口中的数据对所有实现类只有一份,所以是 static
,即单例。
Cocos2dxActivity 类的变量定义
1.1.1、private final static String TAG = Cocos2dxActivity.class.getSimpleName();
该变量用于打 log 时起到标示作用。getSimpleName()
方法返回源代码中给出的底层类的简称。
1.1.2、private Cocos2dxGLSurfaceView mGLSurfaceView;
Cocos2dxGLSurfaceView
继承自 GLSurfaceView
。GLSurfaceView
是 OpenGL 绘制所在的容器。为了在 Android 应用中使用 OpenGLES 绘画,必须创建一个 view
作为容器,最直接的方式是从 GLSurfaceView
派生一个类。使用 GLSurfaceView
几乎是整合 OpenGLES 到应用的唯一方式,对于全屏或近全屏的 graphicsview
是最好的选择。如果只是在某个小部分显示 OpenGLES 图形,可以考虑 TextureView
,也可以使用 SurfaceView
创建一个 OpenGLES view
。GLSurfaceView
中实际的绘制任务在 GLSurfaceView.Renderer
中,所以其代码较少,但通常需要扩展这个类来响应触摸事件。
Cocos2dxGLSurfaceView 类的变量定义
1.2.1、private static Handler sHandler;
Handler
在 Android 中主要负责发送和处理消息,其主要用途有两个:一是按计划发送消息或执行某个 Runnable
;二是将从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新 UI 线程)。
1.2.2、private static Cocos2dxGLSurfaceView mCocos2dxGLSurfaceView;
1.2.3、private static Cocos2dxTextInputWraper sCocos2dxTextInputWraper;
Cocos2dxTextInputWraper
是实现了 TextWatcher
接口和 OnEditorActionListener
接口的类。
Cocos2dxTextInputWraper 类的变量和函数
- 变量:
private final Cocos2dxGLSurfaceView mCocos2dxGLSurfaceView;
- 函数:构造函数
public Cocos2dxTextInputWraper(final Cocos2dxGLSurfaceView pCocos2dxGLSurfaceView)
,将输入参数定义赋值给内部变量。
1.2.4、private Cocos2dxRenderer mCocos2dxRenderer;
Cocos2dxRenderer
是实现了 GLSurfaceView.Renderer
接口的类,实际的绘图动作都在 GLSurfaceView.Renderer
里面发生。
Cocos2dxRenderer 类的函数
public void onSurfaceCreated(final GL10 pGL10, final EGLConfig pEGLConfig)
- 作用:通过 JNI 调用 native 层的
Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit
函数。进行一系列的初始化操作,包括获取(或者创建)单例s_SharedDirector
(Director
的子类,Ref
的孙子类)、单例Configuration
对象(Ref
的子类),实例化多个对象和变量,如scence
、Node
、Scheduler
、ActionManager
、EventDispatcher
等,并进行相关的初始化设置。创建一个新的glview
为GLViewImpl
(GLView
的子类,Ref
的孙子类)的实例,并进行一系列的配置。最后开始运行(调用真正的 Cocos2d-x 开发者的入口函数AppDelegate::applicationDidFinishLaunching()
),进行一些场景和层的创建、添加操作,以及设置动画延迟等。 - 详细初始化过程:
- 调用
Director
类的getInstance
函数,创建(或者获取)s_SharedDirector
单例,初始化相关变量,然后调用其父类Director
的init
函数。 - 实例化多个对象,如
Scene
、Camera
、EventCustom
等,并进行相关的初始化设置。 - 初始化多个变量,如
accumDt
、_frameRate
等。 - 实例化
Scheduler
、ActionManager
等,并进行关联。 - 实例化
TextureCache
、Renderer
、Console
等对象。 - 创建
GLViewImpl
实例,并进行初始化和配置。 - 设置
director
的glview
,开始运行应用。 public void onSurfaceChanged(final GL10 pGL10, final int pWidth, final int pHeight)
- 该方法在
surface
大小改变时被调用,是设置 OpenGL 视图端的好地方。如果相机是固定的,不会围着场景移动,也可以在这里设置相机。 public void onDrawFrame(final GL10 gl)
- 作用:在这个函数中加入了延迟动画时间的限制,在限制之内会直接通过 JNI 层调用 native 层的
Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender
。然后调用drawScene
函数,根据延迟时间判断是否需要跳过一帧,若没有暂停,更新_scheduler
和_eventDispatcher
,进行清屏、设置矩阵、绘制、交换缓冲区等操作。 - 每帧的时候该方法都会被调用,用于绘制场景是可靠的。可以通过调用
glClear
方法清除帧缓存,接着通过其他的 OpenGL ES 调用绘制当前的场景。 public void handleOnResume()
public void handleOnPause()
1.2.5、private Cocos2dxEditText mCocos2dxEditText;
Cocos2dxEditText
是继承自 EditText
的类。
Cocos2dxGLSurfaceView 类的函数
1.2.6、构造函数
调用父类构造函数,并调用 initView
函数。
1.2.7、protected void initView()
设置 GL
版本、SurfaceView
等变量以及消息处理方法。
setEGLContextClientVersion(2)
:当使用 OpenGLES 2.0 时,必须在GLSurfaceView
构造器中调用该函数,说明将要使用 2.0 版的 API。setFocusableInTouchMode(true)
:确保能接收到触屏事件。
1.2.8、public void onResume()
调用父类的 onResume
,设置渲染模式,使用线程通信控制渲染线程恢复。
setRenderMode(RENDERMODE_CONTINUOUSLY)
:渲染器会持续被调用以重新渲染场景。queueEvent(Runnable)
方法是一种相对简单的操作。由于渲染器在独立的渲染线程里,需要使用 Java 的跨线程机制跟渲染器通讯。在 UI 线程里调用渲染器的方法可能会收到 “call to OpenGL ES API with no current context” 的警告,甚至可能导致程序崩溃。
1.2.9、public void onPause()
设置渲染模式,使用线程通信控制渲染线程暂停。
setRenderMode(RENDERMODE_WHEN_DIRTY)
:渲染器仅在surface
创建或调用requestRender()
时渲染。停止持续渲染,当调用GLSurfaceView.requestRender()
时,程序再渲染屏幕。
Cocos2dxActivity 类的其他变量
1.1.3、private Cocos2dxHandler mHandler;
1.1.4、private static Cocos2dxActivity sContext = null;
1.1.5、private Cocos2dxVideoHelper mVideoHelper = null;
1.1.6、private Cocos2dxWebViewHelper mWebViewHelper = null;
Cocos2dxActivity 类的函数
1.1.7、protected void onCreate(final Bundle savedInstanceState)
- 作用:
- 调用
protected void onLoadNativeLibraries()
加载 JNI 层的libcocos2dcpp.so
库。 - 使用
getPackageManager()
方法获得已安装的应用程序信息。 - 通过
getApplicationInfo
方法获取ApplicationInfo
对象,进而获取AndroidManifest.xml
中的MetaData
标签值。 - 实例化
Cocos2dxHandler
,在其构造函数中使用弱引用的方式,将该对象的成员变量mActivity
定义为一个Cocos2dxActivity
的实例。 - 调用
Cocos2dxHelper
的init
函数,进行相关成员变量的初始化。 - 实例化
Cocos2dxAccelerometer
,从系统服务中获得传感器管理器,获取加速重力感应的Sensor
对象,获取屏幕介质信息和屏幕方向。 - 实例化
Cocos2dxMusic
和Cocos2dxSound
对象,进行相关初始化。 - 调用
Cocos2dxHelper
的nativeSetContext
函数,将 Java 层的AssetManager
传入 native 层。 - 通过 JNI 调用
getGLContextAttrs()
方法,初始化GLContext
。 - 调用
this.init()
方法,进行界面元素的创建和设置。 - 实例化
Cocos2dxVideoHelper
和Cocos2dxWebViewHelper
对象。
C++ 相关知识补充
auto
关键字
auto
用来声明自动变量,是存储类型标识符,表明变量(自动)具有本地范围,块范围的变量声明(如 for
循环体内的变量声明)默认为 auto
存储类型。大多普通声明方式声明的变量都是 auto
变量,不需要明确指定 auto
关键字,默认就是 auto
的。auto
变量在离开作用域时会被程序自动释放,不会发生内存溢出情况(除了包含指针的类)。
using namespace std
using namespace std
包含标准库中的一切,使用该语句后,标准库中的元素就好像被声明为全局变量一样。但这会带来兼容性问题,因此有了不同的头文件,一个是为了兼容以前的 C++ 代码,一个是为了支持新的标准。
static
关键字
static
修饰的变量为单例。
friend
关键字
在类 A
里面加 friend class B;
,使得 B
类可以访问 A
类里面的 protected
和 private
的成员函数或成员变量。但注意,在 A
里面声明了这句,只能让 B
类内部访问 A
类的保护或私有成员,反之 A
类不能访问 B
类保护或私有成员。
nullptr
如果编译器支持 nullptr
,应该直接使用 nullptr
来替代 NULL
的宏定义。在正常使用过程中它们是完全等价的,但在重载或模板推导时,nullptr
能避免编译器给出错误结果。
new(std::nothrow)
new(std::nothrow)
即不抛出异常,当 new
一个对象失败时,默认设置该对象为 NULL
,可以方便地通过 if(p == NULL)
来判断 new
操作是否成功。建议在 C++ 代码中,凡是涉及到 new
操作,都采用 new(std::nothrow)
然后 if(p==NULL)
的方式进行判断。
mutable
关键字
mutable
是为了突破 const
的限制而设置的。被 mutable
修饰的变量,将永远处于可变的状态,即使在一个 const
函数中。
vector
的 reserve
和 resize
vector
的 reserve
增加了 vector
的 capacity
,但它的 size
没有改变。reserve
是容器预留空间,但在空间内不真正创建元素对象,所以在没有添加新的对象之前,不能引用容器内的元素。加入新的元素时,要调用 push_back()
或 insert()
函数。而 resize
改变了 vector
的 capacity
同时也增加了它的 size
。resize
是改变容器的大小,且会创建对象,因此调用这个函数之后,就可以引用容器内的对象了,加入新元素时可以用 operator[]
操作符或迭代器来引用元素对象,此时再调用 push_back()
函数,是加在这个新的空间后面的。
System.nanoTime()
System.nanoTime()
用于获取当前时间,精度为纳米级。