Android 2.3(API level 9)和Android NDK版本5支持我们使用NativeActivity类来编写完整的活动和应用,以便能够访问Android应用的整个生命周期。
为了利用该方法,在Android manifest文件中引用android.app.NativeActivity。注意,引用中需要包含hasCode属性。如果应用中没有Java代码,该属性应该设置成false(只是NativeActivity)。但是,在这个例子中,因为包含Java代码,所以我们把该属性值设置成true:
<!-- This .apk has Java code, so set hasCode to true which is the default. --><!-- if this only had a native app (only the activity called /'android.app.NativeActivity/') --><!-- then set to false --><application android:icon=/"@drawable/icon/" android:label=/"@string/app_name/" android:hasCode=/"true/" ><activity android:name=/".NDKApp/" android:label=/"@string/app_name/"> <intent-filter><action android:name=/"android.intent.action.MAIN/" /><category android:name=/"android.intent.category.LAUNCHER/" /> </intent-filter> </activity> <activity android:name=/"android.app.NativeActivity/" android:label=/"SampleNativeActivity/" android:debuggable=/"true/" > <!-- here we declare what lib to reference --> <meta-data android:name=/"android.app.lib_name/" android: /> </activity></application>
在这个例子中,使用头文件android_native_app_glue.h而不使用native_activity.h头文件,native_activity.h接口基于一组应用的回调函数,当某些事件发生时,Activity的main线程会调用这些回调函数。这表示回调函数不应该阻塞,是强制的。android_native_app_glue.h文件给出辅助库,它包含不同的执行模式,它的方式是应用在不同的线程中实现自己的主要功能。该功能必须命名为android_main,创建应用时会调用它,并向其传递android_app对象。它提供了对应用或activity进行引用的机制,并能够监听不同的生命周期事件。
下面这个简单的nativeactivity示例构建了一个Activity并负责监听Motion事件。然后,会把Motion事件的x坐标和y坐标值发送给LogCat:
#include <jni.h>#include <android/log.h>#include <android_native_app_glue.h>// usage of log#define LOGINFO(x...) __android_log_print(ANDROID_LOG_INFO,/"SampleNativeActivity/",x)// handle commandsstatic void custom_handle_cmd(struct android_app* app, int32_t cmd) { switch(cmd) { case APP_CMD_INIT_WINDOW: LOGINFO(/"App Init Window/"); break; }}// handle inputstatic int32_t custom_handle_input(struct android_app* app, AInputEvent* event) { // we see a motion event and we log it if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { LOGINFO(/"Motion Event: x %f / y %f/", AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0)); return 1; } return 0;}// This is the function that application code must implement,// representing the main entry to the app.void android_main(struct android_app* state) { // Make sure glue isn/'t stripped. app_dummy; int events; // set up so when commands happen we call our custom handler state->onAppCmd = custom_handle_cmd; // set up so when input happens we call our custom handler state->onInputEvent = custom_handle_input; while (1) { struct android_poll_source* source; // we block for events while (ALooper_pollAll(-1, NULL, &events, (void**)&source) >= 0) { // Process this event. if (source != NULL) { source->process(state, source); } // Check if we are exiting. if (state->destroyRequested != 0) { LOGINFO(/"We are exiting/"); return; } } }}
以下是示例nativeactivity的Android.mk文件。注意它加载并指向android_native_app_glue模块:
LOCAL_PATH := $(call my-dir)# this is our sample native activityinclude $(CLEAR_VARS)LOCAL_MODULE := sample_native_activityLOCAL_SRC_FILES := sample_nativeactivity.cLOCAL_LDLIBS := -llog -landroidLOCAL_STATIC_LIBRARIES := android_native_app_glueinclude $(BUILD_SHARED_LIBRARY)$(call import-module,android/native_app_glue)
以下是当用户启动应用时会调用的main Java Android activity。单击按钮会启动我们提供的NativeActivity:
package com.oreilly.demo.android.pa.ndkdemo;import com.oreilly.demo.android.pa.ndkdemo.R;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;public class NDKApp extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); findViewById(R.id.nativeactivity).setOnClickListener( new View.OnClickListener { public void onClick(View v) { startActivity(new Intent(getBaseContext, android.app.NativeActivity.class)); // call nativeactivity } }); }}
如果你编译并运行过该示例,会注意到启动本地活动时;屏幕是空白的;如果查看LogCat,会出现各种日志信息(尤其是当在屏幕上移动手指时)。但是,这不怎么好玩。因此,为了使界面好看些,我们需要执行一些操作。接下来给这个示例使用了OpenGL ES,可以改变屏幕的颜色。
以下是OpenGL ES的本地源代码。当显示活动时,会把屏幕变成亮红色。
#include <jni.h>#include <android/log.h>#include <android_native_app_glue.h>#include <EGL/egl.h>#include <GLES/gl.h>// usage of log#define LOGINFO(x...)__android_log_print(ANDROID_LOG_INFO,/"NativeWOpenGL/",x)struct eglengine { EGLDisplay display; EGLSurface surface; EGLContext context;};// initialize the egl enginestatic int engine_init_display(struct android_app* app, struct eglengine* engine) { 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); eglChooseConfig(display, attribs, &config, 1, &numConfigs); eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); ANativeWindow_setBuffersGeometry(app->window, 0, 0, format); surface = eglCreateWindowSurface(display, config, app->window, NULL); context = eglCreateContext(display, config, NULL, NULL); if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { LOGINFO(/"eglMakeCurrent FAIL/"); return -1; } eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, EGL_HEIGHT, &h); engine->display = display; engine->context = context; engine->surface = surface; glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); glEnable(GL_CULL_FACE); glShadeModel(GL_SMOOTH); glDisable(GL_DEPTH_TEST); return 0;}// draw to the screenstatic void engine_color_screen(struct eglengine* engine) { if (engine->display == NULL) { return; } glClearColor(255, 0, 0, 1); // let/'s make the screen all red glClear(GL_COLOR_BUFFER_BIT); eglSwapBuffers(engine->display, engine->surface);}// when things need to be terminatedstatic void engine_terminate(struct eglengine* 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->display = EGL_NO_DISPLAY; engine->context = EGL_NO_CONTEXT; engine->surface = EGL_NO_SURFACE;}// handle commandsstatic void custom_handle_cmd(struct android_app* app, int32_t cmd) { struct eglengine* engine = (struct eglengine*)app->userData; switch(cmd) { // things are starting up... let/'s initialize the engine and color the screen case APP_CMD_INIT_WINDOW: if (app->window != NULL) { engine_init_display(app, engine); engine_color_screen(engine); } break; case APP_CMD_TERM_WINDOW: // things are ending...let/'s clean up the engine engine_terminate(engine); break; }}// handle inputstatic int32_t custom_handle_input(struct android_app* app, AInputEvent* event) { // we see a motion event and we log it if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { LOGINFO(/"Motion Event: x %f / y %f/", AMotionEvent_getX(event, 0), AMotionEvent_getY(event, 0)); return 1; } return 0;}// This is the function that application code must implement,// representing the main entry to the app.void android_main(struct android_app* state) { // Make sure glue isn/'t stripped. app_dummy; // here we add the eglengine to the app struct eglengine engine; memset(&engine, 0, sizeof(engine)); // set engine as userdata so we can reference state->userData = &engine; int events; // set up so when commands happen we call our custom handler state->onAppCmd = custom_handle_cmd; // set up so when input happens we call our custom handler state->onInputEvent = custom_handle_input; while (1) { struct android_poll_source* source; // we block for events while (ALooper_pollAll(-1, NULL, &events, (void**)&source) >= 0) { // Process this event. if (source != NULL) { source->process(state, source); } // Check if we are exiting. if (state->destroyRequested != 0) { LOGINFO(/"We are exiting/"); return; } } }}
sample_native_activity_opengl活动的Android.mk文件会加载EGL和GLESv1_CM库:
LOCAL_PATH := $(call my-dir) # this is our sample native activity with openglinclude $(CLEAR_VARS)LOCAL_MODULE := sample_native_activity_openglLOCAL_SRC_FILES := sample_nativeactivity_opengl.c # loading the log , android, egl, gles librariesLOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CMLOCAL_STATIC_LIBRARIES := android_native_app_glueinclude $(BUILD_SHARED_LIBRARY)$(call import-module,android/native_app_glue)