public class NativeActivity
extends Activity
implements SurfaceHolder.Callback2, InputQueue.Callback, ViewTreeObserver.OnGlobalLayoutListener
java.lang.Object | |||||
↳ | android.content.Context | ||||
↳ | android.content.ContextWrapper | ||||
↳ | android.view.ContextThemeWrapper | ||||
↳ | android.app.Activity | ||||
↳ | android.app.NativeActivity |
方便实施纯粹以本地代码实施的活动。 也就是说,一款游戏(或类似游戏的东西)。 没有必要从这个类中派生出来; 你可以简单地在清单中声明它,并从那里使用NDK API。
典型的清单看起来像:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.native_activity" android:versionCode="1" android:versionName="1.0"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="9" /> <!-- This .apk has no Java code itself, so set hasCode to false. --> <application android:label="@string/app_name" android:hasCode="false"> <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="android.app.NativeActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden"> <!-- Tell NativeActivity the name of or .so --> <meta-data android:name="android.app.lib_name" android:value="native-activity" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
NativeActivity运行的一个非常简单的本机代码示例如下。 这会从用户那里读取输入事件,并使用OpenGLES绘制到本地活动的窗口。
#include <jni.h> #include <errno.h> #include <EGL/egl.h> #include <GLES/gl.h> #include <android/sensor.h> #include <android/log.h> #include <android_native_app_glue.h> #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__)) #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__)) /** * Our saved state data. */ struct saved_state { float angle; int32_t x; int32_t y; }; /** * Shared state for our app. */ struct engine { struct android_app* app; ASensorManager* sensorManager; const ASensor* accelerometerSensor; ASensorEventQueue* sensorEventQueue; int animating; EGLDisplay display; EGLSurface surface; EGLContext context; int32_t width; int32_t height; struct saved_state state; }; /** * Initialize an EGL context for the current display. */ static int engine_init_display(struct engine* engine) { // initialize OpenGL ES and EGL /* * Here specify the attributes of the desired configuration. * Below, we select an EGLConfig with at least 8 bits per color * component compatible with on-screen windows */ const EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_NONE }; EGLint w, h, dummy, format; EGLint numConfigs; EGLConfig config; EGLSurface surface; EGLContext context; EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); /* Here, the application chooses the configuration it desires. In this * sample, we have a very simplified selection process, where we pick * the first EGLConfig that matches our criteria */ eglChooseConfig(display, attribs, &config, 1, &numConfigs); /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). * As soon as we picked a EGLConfig, we can safely reconfigure the * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format); surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); context = eglCreateContext(display, config, NULL, NULL); if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { LOGW("Unable to eglMakeCurrent"); return -1; } eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); engine->display = display; engine->context = context; engine->surface = surface; engine->width = w; engine->height = h; engine->state.angle = 0; // Initialize GL state. glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); glEnable(GL_CULL_FACE); glShadeModel(GL_SMOOTH); glDisable(GL_DEPTH_TEST); return 0; } /** * Just the current frame in the display. */ static void engine_draw_frame(struct engine* engine) { if (engine->display == NULL) { // No display. return; } // Just fill the screen with a color. glClearColor(((float)engine->state.x)/engine->width, engine->state.angle, ((float)engine->state.y)/engine->height, 1); glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(engine->display, engine->surface); } /** * Tear down the EGL context currently associated with the display. */ static void engine_term_display(struct engine* engine) { if (engine->display != EGL_NO_DISPLAY) { eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (engine->context != EGL_NO_CONTEXT) { eglDestroyContext(engine->display, engine->context); } if (engine->surface != EGL_NO_SURFACE) { eglDestroySurface(engine->display, engine->surface); } eglTerminate(engine->display); } engine->animating = 0; engine->display = EGL_NO_DISPLAY; engine->context = EGL_NO_CONTEXT; engine->surface = EGL_NO_SURFACE; } /** * Process the next input event. */ static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { struct engine* engine = (struct engine*)app->userData; if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { engine->animating = 1; engine->state.x = AMotionEvent_getX(event, 0); engine->state.y = AMotionEvent_getY(event, 0); return 1; } return 0; } /** * Process the next main command. */ static void engine_handle_cmd(struct android_app* app, int32_t cmd) { struct engine* engine = (struct engine*)app->userData; switch (cmd) { case APP_CMD_SAVE_STATE: // The system has asked us to save our current state. Do so. engine->app->savedState = malloc(sizeof(struct saved_state)); *((struct saved_state*)engine->app->savedState) = engine->state; engine->app->savedStateSize = sizeof(struct saved_state); break; case APP_CMD_INIT_WINDOW: // The window is being shown, get it ready. if (engine->app->window != NULL) { engine_init_display(engine); engine_draw_frame(engine); } break; case APP_CMD_TERM_WINDOW: // The window is being hidden or closed, clean it up. engine_term_display(engine); break; case APP_CMD_GAINED_FOCUS: // When our app gains focus, we start monitoring the accelerometer. if (engine->accelerometerSensor != NULL) { ASensorEventQueue_enableSensor(engine->sensorEventQueue, engine->accelerometerSensor); // We'd like to get 60 events per second (in us). ASensorEventQueue_setEventRate(engine->sensorEventQueue, engine->accelerometerSensor, (1000L/60)*1000); } break; case APP_CMD_LOST_FOCUS: // When our app loses focus, we stop monitoring the accelerometer. // This is to avoid consuming battery while not being used. if (engine->accelerometerSensor != NULL) { ASensorEventQueue_disableSensor(engine->sensorEventQueue, engine->accelerometerSensor); } // Also stop animating. engine->animating = 0; engine_draw_frame(engine); break; } } /** * This is the main entry point of a native application that is using * android_native_app_glue. It runs in its own thread, with its own * event loop for receiving input events and doing other things. */ void android_main(struct android_app* state) { struct engine engine; // Make sure glue isn't stripped. app_dummy(); memset(&engine, 0, sizeof(engine)); state->userData = &engine; state->onAppCmd = engine_handle_cmd; state->onInputEvent = engine_handle_input; engine.app = state; // Prepare to monitor accelerometer engine.sensorManager = ASensorManager_getInstance(); engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, ASENSOR_TYPE_ACCELEROMETER); engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, state->looper, LOOPER_ID_USER, NULL, NULL); if (state->savedState != NULL) { // We are starting with a previous saved state; restore from it. engine.state = *(struct saved_state*)state->savedState; } // loop waiting for stuff to do. while (1) { // Read all pending events. int ident; int events; struct android_poll_source* source; // If not animating, we will block forever waiting for events. // If animating, we loop until all events are read, then continue // to draw the next frame of animation. while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, (void**)&source)) >= 0) { // Process this event. if (source != NULL) { source->process(state, source); } // If a sensor has data, process it now. if (ident == LOOPER_ID_USER) { if (engine.accelerometerSensor != NULL) { ASensorEvent event; while (ASensorEventQueue_getEvents(engine.sensorEventQueue, &event, 1) > 0) { LOGI("accelerometer: x=%f y=%f z=%f", event.acceleration.x, event.acceleration.y, event.acceleration.z); } } } // Check if we are exiting. if (state->destroyRequested != 0) { engine_term_display(&engine); return; } } if (engine.animating) { // Done with events; draw next animation frame. engine.state.angle += .01f; if (engine.state.angle > 1) { engine.state.angle = 0; } // Drawing is throttled to the screen update rate, so there // is no need to do timing here. engine_draw_frame(&engine); } } }
Constants |
|
---|---|
String |
META_DATA_FUNC_NAME 可选元 - 可以在此组件的清单中,指定 |
String |
META_DATA_LIB_NAME 可选元 - 可以在此组件的清单中,指定要加载的本机共享库的名称。 |
Inherited constants |
---|
From class android.app.Activity
|
From class android.content.Context
|
From interface android.content.ComponentCallbacks2
|
Inherited fields |
---|
From class android.app.Activity
|
Public constructors |
|
---|---|
NativeActivity() |
Public methods |
|
---|---|
void |
onConfigurationChanged(Configuration newConfig) 设备配置在您的活动正在运行时更改时由系统调用。 |
void |
onGlobalLayout() 当全局布局状态或视图树中视图的可见性发生更改时调用回调方法 |
void |
onInputQueueCreated(InputQueue queue) 当给定的InputQueue现在与进行此调用的线程相关联时调用,因此它可以开始接收来自它的事件。 |
void |
onInputQueueDestroyed(InputQueue queue) 当给定的InputQueue不再与该线程关联并因此不调度事件时调用。 |
void |
onLowMemory() 这在整个系统内存不足时调用,并且主动运行的进程应该修剪内存使用情况。 |
void |
onWindowFocusChanged(boolean hasFocus) 当活动当前 |
void |
surfaceChanged(SurfaceHolder holder, int format, int width, int height) 这是在对表面进行任何结构更改(格式或大小)后立即调用的。 |
void |
surfaceCreated(SurfaceHolder holder) 这是在表面首次创建后立即调用的。 |
void |
surfaceDestroyed(SurfaceHolder holder) 这是在表面被破坏之前立即调用的。 |
void |
surfaceRedrawNeeded(SurfaceHolder holder) 当应用程序需要重新绘制其表面的内容,调整大小或出于某种其他原因时调用。 |
Protected methods |
|
---|---|
void |
onCreate(Bundle savedInstanceState) 当活动开始时调用。 |
void |
onDestroy() 在活动销毁之前执行任何最终清理。 |
void |
onPause() 作为活动生命周期的一部分,当活动进入后台但尚未(尚未)死亡时调用。 |
void |
onResume() 后调用 |
void |
onSaveInstanceState(Bundle outState) 被调用以在被杀死之前从活动中检索每个实例的状态,以便可以在 |
void |
onStart() 在 |
void |
onStop() 当用户不再可见时调用。 |
Inherited methods |
|
---|---|
From class android.app.Activity
|
|
From class android.view.ContextThemeWrapper
|
|
From class android.content.ContextWrapper
|
|
From class android.content.Context
|
|
From class java.lang.Object
|
|
From interface android.view.LayoutInflater.Factory2
|
|
From interface android.view.Window.Callback
|
|
From interface android.view.KeyEvent.Callback
|
|
From interface android.view.View.OnCreateContextMenuListener
|
|
From interface android.content.ComponentCallbacks2
|
|
From interface android.view.SurfaceHolder.Callback2
|
|
From interface android.view.InputQueue.Callback
|
|
From interface android.view.ViewTreeObserver.OnGlobalLayoutListener
|
|
From interface android.view.LayoutInflater.Factory
|
|
From interface android.content.ComponentCallbacks
|
|
From interface android.view.SurfaceHolder.Callback
|
String META_DATA_FUNC_NAME
可选元,可以在清单此组件,指定的主入口点的名称,在这个天然活性META_DATA_LIB_NAME
本地代码。 如果未指定,则使用“ANativeActivity_onCreate”。
常量值:“android.app.func_name”
String META_DATA_LIB_NAME
可选元 - 可以在此组件的清单中,指定要加载的本机共享库的名称。 如果未指定,则使用“main”。
常量值:“android.app.lib_name”
void onConfigurationChanged (Configuration newConfig)
设备配置在您的活动正在运行时更改时由系统调用。 请注意, 只有在清单中选择了想要处理configChanges
属性的配置时才会调用此选项。 如果发生任何未被该属性选择报告的配置更改,则不会报告该系统,系统将停止并重新启动活动(使其通过新配置启动)。
在调用此函数时,您的Resources对象将被更新为返回与新配置相匹配的资源值。
Parameters | |
---|---|
newConfig |
Configuration : The new device configuration. |
void onInputQueueCreated (InputQueue queue)
当给定的InputQueue现在与进行此调用的线程相关联时调用,因此它可以开始接收来自它的事件。
Parameters | |
---|---|
queue |
InputQueue
|
void onInputQueueDestroyed (InputQueue queue)
当给定的InputQueue不再与该线程关联并因此不调度事件时调用。
Parameters | |
---|---|
queue |
InputQueue
|
void onLowMemory ()
这在整个系统内存不足时调用,并且主动运行的进程应该修剪内存使用情况。 虽然没有定义它的确切位置,但通常会在所有后台进程都被终止时发生。 也就是说,在达到托管服务和前台UI的杀死进程之前,我们希望避免杀死。
你应该实现这个方法来释放你可能持有的任何缓存或其他不必要的资源。 系统将从此方法返回后为您执行垃圾回收。
最好是,您应该从ComponentCallbacks2
实施onTrimMemory(int)
,以基于不同级别的内存需求逐步卸载资源。 该API适用于API级别14和更高版本,因此,您应该只使用此onLowMemory()
方法作为旧版本的后备方案,可以将其与onTrimMemory(int)
的TRIM_MEMORY_COMPLETE
级别相同处理。
void onWindowFocusChanged (boolean hasFocus)
当活动当前Window
获得或失去焦点时调用。 这是此活动是否对用户可见的最佳指标。 默认实现会清除密钥跟踪状态,因此应该始终调用。
请注意,这提供了有关全局焦点状态的信息,该状态与活动生命周期无关地进行管理。 因此,虽然重点更改通常与生命周期更改有关(停止的活动通常不会获得窗口焦点),但您不应该依赖这里的回调和其他生命周期方法中的回调之间的任何特定顺序,例如onResume()
。
然而,作为一般规则,恢复的活动将具有窗口焦点...除非它已显示其他对话框或弹出窗口来输入焦点,在这种情况下,当其他窗口具有焦点时,活动本身将不具有焦点。 同样,系统可能会显示系统级别的窗口(例如状态栏通知面板或系统警报),该窗口将临时采用窗口输入焦点,而不会暂停前台活动。
Parameters | |
---|---|
hasFocus |
boolean : Whether the window of this activity has focus. |
void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
这是在对表面进行任何结构更改(格式或大小)后立即调用的。 此时您应该更新曲面中的图像。 此方法总是在surfaceCreated(SurfaceHolder)
之后至少调用一次。
Parameters | |
---|---|
holder |
SurfaceHolder : The SurfaceHolder whose surface has changed. |
format |
int : The new PixelFormat of the surface. |
width |
int : The new width of the surface. |
height |
int : The new height of the surface. |
void surfaceCreated (SurfaceHolder holder)
这是在表面首次创建后立即调用的。 这个实现应该启动他们想要的任何渲染代码。 请注意,只有一个线程可以绘制成Surface
,因此如果您的普通渲染将位于另一个线程中,则不应在此处绘制Surface。
Parameters | |
---|---|
holder |
SurfaceHolder : The SurfaceHolder whose surface is being created. |
void surfaceDestroyed (SurfaceHolder holder)
这是在表面被破坏之前立即调用的。 从此通话返回后,您不应再尝试访问此表面。 如果您有直接访问表面的渲染线程,则必须确保线程在从此函数返回之前不再触摸Surface。
Parameters | |
---|---|
holder |
SurfaceHolder : The SurfaceHolder whose surface is being destroyed. |
void surfaceRedrawNeeded (SurfaceHolder holder)
当应用程序需要重新绘制其表面的内容,调整大小或出于某种其他原因时调用。 在完成重画之前,如果不从这里返回,则可以确保用户不会看到表面处于不良状态(在以正确方式绘制之前以新尺寸显示)。 这通常会在surfaceChanged(SurfaceHolder, int, int, int)
。
Parameters | |
---|---|
holder |
SurfaceHolder : The SurfaceHolder whose surface has changed. |
void onCreate (Bundle savedInstanceState)
当活动开始时调用。 这是大多数初始化应该去的地方:调用setContentView(int)
来setContentView(int)
活动的UI,使用findViewById(int)
以编程方式与UI中的小部件进行交互,调用managedQuery(android.net.Uri, String[], String, String[], String)
来检索正在显示的数据的光标等。
您可以拨打 finish()
从这个函数中,在这种情况下的onDestroy()将被立即调用没有任何活动的生命周期(其余的 onStart()
, onResume()
, onPause()
执行等)。
派生类必须调用超类的这个方法的实现。 如果他们不这样做,就会抛出异常。
Parameters | |
---|---|
savedInstanceState |
Bundle : If the activity is being re-initialized after previously being shut down then this Bundle contains the data it most recently supplied in onSaveInstanceState(Bundle) . Note: Otherwise it is null. |
void onDestroy ()
在活动销毁之前执行任何最终清理。 这可能是由于活动正在完成(有人称为finish()
,或者系统暂时销毁此活动的实例以节省空间),您可以使用isFinishing()
方法区分这两种情况。
注意:不要将此方法称为保存数据的地方! 例如,如果某个活动正在编辑内容提供者中的数据,则应该在onPause()
或onSaveInstanceState(Bundle)
提交这些编辑,而不是在这里。 这种方法通常被实现为释放资源,例如与活动相关联的线程,这样一个被销毁的活动就不会离开这些东西,而其他应用程序仍在运行。 在某些情况下,系统只会在不调用该方法(或任何其他方法)的情况下终止该活动的托管过程,因此不应该将其用于在过程消失后执行那些旨在保留的事情。
派生类必须调用超类的这个方法的实现。 如果他们不这样做,就会抛出异常。
void onPause ()
作为活动生命周期的一部分,当活动进入后台但尚未(尚未)死亡时调用。 对应于onResume()
。
当活动B在活动A前面启动时,此回调将在A上被调用。在A的 onPause()
返回之前,B不会被创建,因此请确保在此处不做任何冗长的操作。
此回调主要用于保存活动正在编辑的任何持续状态,向用户呈现“就地编辑”模型,并确保没有足够的资源来启动新活动,而不必先杀死该活动。 这也是一个很好的地方,可以停止动画和其他消耗大量CPU的内容,以便尽快切换到下一个活动,或者关闭像摄像机这样的独占访问资源。
在系统需要更多内存的情况下,它可能会终止暂停的进程以回收资源。 因此,您应该确保在您从此函数返回时保存所有状态。 通常, onSaveInstanceState(Bundle)
用于保存活动中的每个实例状态,并且此方法用于存储全局持久数据(位于内容提供者,文件等中)
接到此电话后,您通常会收到以下致电 onStop()
(在下一个活动恢复并显示之后),但在某些情况下,将直接回拨至 onResume()
而不通过停止状态。
派生类必须调用超类的这个方法的实现。 如果他们不这样做,就会抛出异常。
void onResume ()
后调用onRestoreInstanceState(Bundle)
, onRestart()
,或onPause()
,为您的活动启动与用户交互。 这是开始动画,打开专用访问设备(如相机)等的好地方。
请记住,onResume并不是您的活动对用户可见的最佳指标; 系统窗口(如键盘)可能在前面。 使用onWindowFocusChanged(boolean)
可以确定您的活动对用户是可见的(例如,恢复游戏)。
派生类必须调用超类的这个方法的实现。 如果他们不这样做,就会抛出异常。
void onSaveInstanceState (Bundle outState)
在被杀之前调用以从活动中检索每个实例的状态,以便可以在 onCreate(Bundle)
或 onRestoreInstanceState(Bundle)
恢复状态(由此方法填充的 Bundle
将传递到两者)。
在一个活动可能被杀死之前调用这个方法,以便在将来它有一段时间它可以恢复它的状态。 例如,如果活动B在活动A之前启动,并且在某些时刻活动A被杀死以回收资源,则活动A将有机会通过此方法保存其用户界面的当前状态,以便当用户返回时到活动A,可以通过onCreate(Bundle)
或onRestoreInstanceState(Bundle)
恢复用户界面的状态。
不要将此方法与活动生命周期回调(如onPause()
,后者始终在活动置于后台或销毁途中调用,或在销毁前调用onStop()
。 调用onPause()
和onStop()
时的一个示例,而不是此方法是当用户从活动B返回到活动A时:不需要在B上调用onSaveInstanceState(Bundle)
,因为该特定实例永远不会被恢复,因此系统可以避免调用它。 当onPause()
而不是onSaveInstanceState(Bundle)
时的一个示例是当活动B在活动A前面启动时:如果活动A在A的用户界面的状态期间未在B的生存期中被杀死,则系统可以避免对活动A调用onSaveInstanceState(Bundle)
将保持完好。
默认实现通过在具有ID的层次结构中的每个视图上调用onSaveInstanceState()
并保存当前焦点视图的ID(所有视图都由缺省实现恢复)来为您处理大部分UI每个实例状态的onRestoreInstanceState(Bundle)
)。 如果您重写此方法以保存未被每个视图捕获的附加信息,那么您可能需要调用默认实现,否则应准备好保存每个视图的所有状态。
如果调用,此方法将在onStop()
之前onStop()
。 无法保证onPause()
之前还是之后。
Parameters | |
---|---|
outState |
Bundle : Bundle in which to place your saved state. |
void onStart ()
在onCreate(Bundle)
之后调用 - 或者在onRestart()
之后当活动已停止,但现在再次显示给用户时调用。 紧接着是onResume()
。
派生类必须调用超类的这个方法的实现。 如果他们不这样做,就会抛出异常。
void onStop ()
当用户不再可见时调用。 接下来,您将收到无论是onRestart()
, onDestroy()
,或没有,这取决于后来的用户活动。
派生类必须调用超类的这个方法的实现。 如果他们不这样做,就会抛出异常。