Android——NativeActivity - C/C++ Apk开发

从接触android到现在应该差不多有3年的时间了,杂七杂八,从上到下或多或少都有所接触,虽然对Apk的开发涉猎少,但最近才知道NativeActivity这么个东西,不禁感叹android体系兼容的强大,路漫漫其修远兮~

撰写不易,转载需注明出处:http://blog.csdn.net/jscese/article/details/51005447本文来自 【jscese】的博客!

概念

android基本的四大组件之一Activity,android开发的第一个hello world 创建的就是这个继承了Activity类的类,拥有对应的生命周期,由AMS维护,只需要重写父类对应的方法即可,但这都是在java层面,如果想往C/C++层跑,就需要JNI去访问,这样基本可以满足许多性能有要求的apk开发需求。
但是那些原生是基于C/C++编写的大型软件程序,就并不是这么好抽取JNI接口让android 的java层调度了。如果想移植实现那就麻烦了, 我的理解 这个NativeActivity 机制相当于一个适配层,将android的那一套组件运行时机制 转义为C/C++开发程序所能接收处理的一层进行调度管理,不禁想起之前在 《程序员的自我修改-链接.装载.库》 中看到那句名言: 计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决


java-apk层面

NativeActivity 为Activity的子类,实现在:\frameworks\base\core\java\android\app\NativeActivity.java
主要是对Activity的一些生命周期函数的封装,贴出值得关注的:

 * {@sample development/ndk/platforms/android-9/samples/native-activity/jni/main.c all}
 */
public class NativeActivity extends Activity implements SurfaceHolder.Callback2,
        InputQueue.Callback, OnGlobalLayoutListener {
...
public static final String META_DATA_LIB_NAME = "android.app.lib_name";
...
 private native long loadNativeCode(String path, String funcname, MessageQueue queue,
            String internalDataPath, String obbPath, String externalDataPath, int sdkVersion,
            AssetManager assetMgr, byte[] savedState);
...
    private native void onStartNative(long handle);
    private native void onResumeNative(long handle);

...
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        String libname = "main";
        String funcname = "ANativeActivity_onCreate";
...
 String ln = ai.metaData.getString(META_DATA_LIB_NAME);
                if (ln != null) libname = ln;
                ln = ai.metaData.getString(META_DATA_FUNC_NAME);
                if (ln != null) funcname = ln;
...
        mNativeHandle = loadNativeCode(path, funcname, Looper.myQueue(),
                getAbsolutePath(getFilesDir()), getAbsolutePath(getObbDir()),
                getAbsolutePath(getExternalFilesDir(null)),
                Build.VERSION.SDK_INT, getAssets(), nativeSavedState);

...
    @Override
    protected void onStart() {
        super.onStart();
        onStartNative(mNativeHandle);
    }

    @Override
    protected void onStop() {
        super.onStop();
        onStopNative(mNativeHandle);
    }

封装的一堆native方法自然一目了然,关键在 onCreate 中对本地方法 loadNativeCode 的调用,传递的参数 libname funcname才是重点,是去加载对应的code lib 和本地的函数入口。
可以看到会读取apk 的 metaData 中的 android.app.lib_name 来获取libname

先不去管native 的具体实现,先看下这个NativeActivity中提到的samples/native-activity ,下载个NDK,目录在:
android-ndk-r8b\samples\native-activity
看下这个apk的AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<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> 
<!-- END_INCLUDE(manifest) -->

注意两点即可:
android:hasCode=”false” 所以 android:name=”android.app.NativeActivity” 这个apk的java源程序就是上面的NativeActivity,自己没有java code

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := native-activity
LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM
LOCAL_STATIC_LIBRARIES := android_native_app_glue

include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)

android_native_app_glue 这个才是重点,次静态库源码在:
android-ndk-r8b\sources\android\native_app_glue
如有兴趣可细看android_native_app_glue.c , 代码也不长,其实也是封了一层,详细代码就不贴了

往下简述一下 从上面的NativeActivity 怎么调用到main.c 里面的流程:


android_native_app_glue 机制封装层面

  1. loadNativeCode 调用到JNI本地方法:\frameworks\base\core\jni\android_app_NativeActivity.cpp中的:
static jlong
loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
        jobject messageQueue, jstring internalDataDir, jstring obbDir,
        jstring externalDataDir, jint sdkVersion,
        jobject jAssetMgr, jbyteArray savedState)
{
    LOG_TRACE("loadNativeCode_native");
...
 void* handle = dlopen(pathStr, RTLD_LAZY);
...
if (handle != NULL) {
        void* funcPtr = NULL;
        const char* funcStr = env->GetStringUTFChars(funcName, NULL);
        if (needNativeBridge) {
            funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
        } else {
            funcPtr = dlsym(handle, funcStr);
        }

        code = new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr);

在lib中找的自然就是java中定义的 ANativeActivity_onCreate 这个函数入口
这个实现函数在 android_native_app_glue.c 中。

  1. android_native_app_glue中的调度到我们自身的代码入口android_main过程:
    看入口代码:
void ANativeActivity_onCreate(ANativeActivity* activity,
        void* savedState, size_t savedStateSize) {
    LOGV("Creating: %p\n", activity);
    activity->callbacks->onDestroy = onDestroy;
    activity->callbacks->onStart = onStart;
    activity->callbacks->onResume = onResume;
    activity->callbacks->onSaveInstanceState = onSaveInstanceState;
    activity->callbacks->onPause = onPause;
    activity->callbacks->onStop = onStop;
    activity->callbacks->onConfigurationChanged = onConfigurationChanged;
    activity->callbacks->onLowMemory = onLowMemory;
    activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
    activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
    activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
    activity->callbacks->onInputQueueCreated = onInputQueueCreated;
    activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;

    activity->instance = android_app_create(activity, savedState, savedStateSize);
}

这里开始把C/C++层面上的接口 去 对接上android activity的运行回调,这些个onStart onResume….

// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------

static struct android_app* android_app_create(ANativeActivity* activity,
        void* savedState, size_t savedStateSize) {
    struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
    memset(android_app, 0, sizeof(struct android_app));
    android_app->activity = activity;
 ...
  pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
...
}

细节省略,可以看到调进来这里的是主线程,也就是最开始 NativeActivity.java里面的onCreate回调函数,比较重要的一个概念:C/C++开发的程序都是运行在一个子线程,这也是必然的,不然那么大一堆程序代码的初始化之类的,主UI线程必定卡死,这也带来一个麻烦事~ 子线程中对UI 的操控,不会有像java子线程一样方便的handle message 给你调,去更新主线程UI了~ 这里是C/C++的pthread_create ,而且UI的资源也一般是在C/C++层,怎么绘制,怎么管理~ 继续往下~

static void* android_app_entry(void* param) {
    struct android_app* android_app = (struct android_app*)param;

    android_app->config = AConfiguration_new();
    AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);

    print_cur_config(android_app);

    android_app->cmdPollSource.id = LOOPER_ID_MAIN;
    android_app->cmdPollSource.app = android_app;
    android_app->cmdPollSource.process = process_cmd;
    android_app->inputPollSource.id = LOOPER_ID_INPUT;
    android_app->inputPollSource.app = android_app;
    android_app->inputPollSource.process = process_input;

    ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
    ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
            &android_app->cmdPollSource);
    android_app->looper = looper;

    pthread_mutex_lock(&android_app->mutex);
    android_app->running = 1;
    pthread_cond_broadcast(&android_app->cond);
    pthread_mutex_unlock(&android_app->mutex);

    android_main(android_app);
    ...
    }

这里的 looper 机制比较复杂~ 不去深究了,注意process_cmd process_inputandroid_app 结构体
这两个 process_xxx 是作为looper中检测到事件以及命令时触发的处理逻辑
往下进入 android_main(android_app) 也就正式进入 我们自己开发的 C/C++ 代码了~


c/c++ lib库实现层面

还是看例子:\android-ndk-r8b\samples\native-activity\jni\main.c

/**
 * 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);
    ...
    // loop waiting for stuff to do.

    while (1) {
    ...
     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);
            }
    ...
    }
 }

这个例子 sensor部分的不做关注~ 只关注机制,可以看到 :

    state->onAppCmd = engine_handle_cmd;
    state->onInputEvent = engine_handle_input;

这两行是重点,实现对上面说到过的 process_xxx 的处理了,怎么对应上的可查看 android_native_app_glue.c 的代码~
往下进入while ,ALooper_pollAll 去等待事件,调用source->process(state, source) 去处理
先转到android_native_app_glue ,再转到这里的engine_handle_cmd 或者 engine_handle_input
详细的处理就不说了~

还有上面说到过的在这个子线程里面~UI 怎么办呢~
这个例子里面是这么做的,调EGL的api绘图~所以前面的Android.mk 需要加入lib库 -lEGL -lGLESv1_CM,绘制显示的初始化函数engine_init_display

总结

主要是从上往下,理清层次关系,调用逻辑,弄清楚运行的机制,还是很有意思的~
这个例子比较简单,后面有机会分析C/C++大型软件用这种方式适配成apk的例子,除了Activity的一些生命周期,事件处理,还可以传递广播以及监听

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这些都是 Android NDK 内部的 `Android.mk` 文件。其中,`./android-ndk-r25c/sources/android/native_app_glue/Android.mk` 是用于编译 Native Activity 示例应用程序的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/support/Android.mk` 是包含一些 Android 支持库的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/ndk_helper/Android.mk` 是包含一些辅助函数和类的 `Android.mk` 文件;`./android-ndk-r25c/sources/android/cpufeatures/Android.mk` 是用于编译 `cpufeatures` 库的 `Android.mk` 文件,该库提供了一些 CPU 相关的信息和功能;`./android-ndk-r25c/sources/cxx-stl/llvm-libc++abi/Android.mk` 和 `./android-ndk-r25c/sources/cxx-stl/llvm-libc++/Android.mk` 是用于编译 C++ STL 库的 `Android.mk` 文件,分别对应 libc++abi 和 libc++ 两个 STL 库;`./android-ndk-r25c/sources/third_party/googletest/Android.mk` 是用于编译 Google Test 测试框架的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/Android.mk` 是用于编译 Shaderc 编译器的 `Android.mk` 文件,该编译器可以将 GLSL 代码编译成 SPIR-V 代码;`./android-ndk-r25c/sources/third_party/shaderc/libshaderc/Android.mk` 是用于编译 Shaderc 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/libshaderc_util/Android.mk` 是用于编译 Shaderc Util 库的 `Android.mk` 文件,该库提供了一些辅助函数和类;`./android-ndk-r25c/sources/third_party/shaderc/third_party/Android.mk` 是用于编译 Shaderc 编译器依赖的第三方库的 `Android.mk` 文件,包括 glslang 和 spirv-tools 两个库;`./android-ndk-r25c/sources/third_party/shaderc/third_party/glslang/Android.mk` 是用于编译 glslang 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/shaderc/third_party/spirv-tools/Android.mk` 是用于编译 spirv-tools 库的 `Android.mk` 文件;`./android-ndk-r25c/sources/third_party/vulkan/src/build-android/jni/Android.mk` 是用于编译 Vulkan 库的 `Android.mk` 文件。 如果您要在 Android NDK 中编写自己的 `Android.mk` 文件,可以参考这些示例文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值