首页 » Android程序设计:第2版 » Android程序设计:第2版全文在线阅读

《Android程序设计:第2版》本地Activity

关灯直达底部

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)