Android NDK RoadMap - NDK/JNI/C脑图

902 阅读4分钟

Let’s become a better Android/NDK Developer

本文使用于

  1. 任何想要学习JNI-NDK开发但不知道从哪里开始的人。
  2. 已经学习Android开发,但想学习Android-NDK-JNI相关开发的人

本文的目标是提供适当Android-JNI-NDK RoadMap(路线图),列出常见需要注意的点,方便知识Review,以使你成为更好的Android/NDK开发人员。本文不会详情的展示如何演示脑图所展示的知识点,展开来谈,一篇的篇幅远远不够,但文章末会列出相关学习资源,给认真学习上进的你。

There is no better way to learn something than by doing.

Getting started with the MindMap

开局一张图,直接上脑图

jni-c-journey

First comes a strong core foundation

  • 简介,下载,配置,集成,编译

    • JNI可以理解层沟通应用层(java)与底层(c)之间的桥梁,可以相互通信,相互调用。
    • 下载建议下载r14以前版本,高版本,as可能会编译时找不到对应的arm
    • AS集成,配置有大体分两种种情况,一种是引用cmake.txt,一种是引用Android.mk,推荐后者,可单独ndk编译,也可放源码类编译,详情参考底部推荐资源
  • 异常-调试

    • 异常的引起大致是由于代码规范不够严谨到账,遇到异常不用慌,理性分析,google一下,你懂的!
    • ndk的调试推荐Android官网介绍的ndk-stack
  • makefile编写

    makefile 文件实质是一个编译脚本,告诉编译器改索引哪些文件,编译哪些文件,依赖哪些库,编译输出格式等。

    建议项目分包按 src(源文件),include(头文件),libs(依赖库),(test)(测试)分包设计,方便makefile索引文件

    具体编写语法参考Android官网介绍的ndk-Android.mk

    源文件或者头文件依赖比较多,建议索引文件目录

    LOCAL_PATH:= $(call my-dir)
    include $(CLEAR_VARS)
    PROJECT_PATH = $(LOCAL_PATH)
    
    LOCAL_MODULE    := libmodule
    LOCAL_LDFLAGS += -shared
    
    #添加模块使用宏定义
    LOCAL_CFLAGS += -DMODULE_FLAG
    
    #添加需要包含的头文件路径,会依次遍历向下所有目录,
    MY_HEADER_PATH += $(PROJECT_PATH)
    
    #添加需要包含的头文件路径,不会向下遍历,最后一个不要加\号
    LOCAL_C_INCLUDES += $(PROJECT_PATH)../include
    
    LOCAL_C_INCLUDES += $(shell find $(MY_HEADER_PATH) -type d)                              
    $(warning "$(LOCAL_MODULE): LOCAL_C_INCLUDES =$(LOCAL_C_INCLUDES)")   
    
    # 扫描目录下的所有源文件,会向下依次遍历
    MY_FILES_PATH  := $(PROJECT_PATH)
    # 添加指定C/CPP文件,只添加某个
    LOCAL_SRC_FILES += $(LOCAL_PATH)/test.c
    
    MY_FILES_SUFFIX := %.cpp %.c %.cc
    My_All_Files := $(foreach src_path,$(MY_FILES_PATH), $(shell find "$(src_path)" -type f) ) 
    My_All_Files := $(My_All_Files:$(MY_CPP_PATH)/./%=$(MY_CPP_PATH)%)
    MY_SRC_LIST  := $(filter $(MY_FILES_SUFFIX),$(My_All_Files)) 
    MY_SRC_LIST  := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%)
    LOCAL_SRC_FILES += $(MY_SRC_LIST)
    $(warning "$(LOCAL_MODULE): LOCAL_SRC_FILES =$(LOCAL_SRC_FILES)")   
    
    #添加需要链接的静态库
    LOCAL_STATIC_LIBRARIES  := 
    
    #添加需要链接的动态库
    LOCAL_SHARED_LIBRARIES  := 
    $(warning "$(LOCAL_MODULE): LOCAL_SHARED_LIBRARIES=$(LOCAL_SHARED_LIBRARIES)") 
    
    #添加需要链接的系统库,如ndk编译,需要链接的log/android等
    LOCAL_LDLIBS    += -llog -landroid -lc
    
    #指定编译目标,这边编译动态库
    include $(BUILD_SHARED_LIBRARY)
    

Learning is endless, take it to the next advance level

  • Jni-c-调用java

    • 大致可以分为c调用java静态方法和类,实例方法和类这四类

    • 调用流程-大致如下:

    环境变量->找到java类->找到静态方法或者变量->设置静态方法或者变量->释放变量

    环境变量->找到java类->创建java实例或者变量->设置实例方法或者变量->释放变量

  • Jni-动态注册

    • 动态注册的好处就是可以在c文件实现函数时可以自定义函数名,另外一个好处是 解决c语言不支持函数重载(c++除外)的问题。

    • 动态注册的大体流程是jni_onload注册本地函数,建立映射表

    /**
    *native method map to java methods
    */
    static JNINativeMethod methods[] = {
    		{"Init", "()I", (void *)Init},
    		{"u java method name", "u java method sigal", u c/c++ method name},
    	};
    
    	/*
     * Register several native methods for one class.
    */
    	static int registerNativeMethods(JNIEnv *env, const char *className,
    									 JNINativeMethod *gMethods, int numMethods)
    	{
    		jclass clazz;
    		LOGI(TAG, "++++++++++%s in", __FUNCTION__);
    
    		clazz = env->FindClass(className);
    		if (clazz == NULL)
    		{
    			LOGE(TAG, "Native registration unable to find class '%s'", className);
    			return JNI_FALSE;
    		}
    
    		if (env->RegisterNatives(clazz, gMethods, numMethods) < 0)
    		{
    			LOGE(TAG, "RegisterNatives failed for '%s'", className);
    			return JNI_FALSE;
    		}
    
    		LOGI(TAG, "++++++++++%s out", __FUNCTION__);
    		return JNI_TRUE;
    	}
    
    	/*
     * Register native methods for all classes we know about.
    *
     * returns JNI_TRUE on success.
    */
    	static int registerNatives(JNIEnv *env)
    	{
    		LOGI(TAG, "++++++++++%s in", __FUNCTION__);
    
    		if (!registerNativeMethods(env, classPathName, methods, sizeof(methods) / sizeof(methods[0])))
    		{
    			LOGE(TAG, "%s err", __FUNCTION__);
    			return JNI_FALSE;
    		}
    
    		LOGI(TAG, "++++++++++%s out", __FUNCTION__);
    		return JNI_TRUE;
    	}
    
    	jint JNI_OnLoad(JavaVM *vm, void *reserved)
    	{
    		JNIEnv *env;
    		g_JavaVM = vm;
    		int result;
    		LOGI(TAG, "++++++++++%s in", __FUNCTION__);
    
    		if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK)
    		{
    			LOGE(TAG, "Failed to get the environment using GetEnv()");
    			return JNI_FALSE;
    		}
    
    		if (registerNatives(env) != JNI_TRUE)
    		{
    			LOGE(TAG, "ERROR: registerNatives failed");
    			return JNI_FALSE;
    		}
    
    		result = JNI_VERSION_1_4;
    		LOGI(TAG, "++++++++++%s out", __FUNCTION__);
    		return result;
    	}
    
  • jni-线程处理

    线程处理大致是线程加锁操作,特别注意注意锁申请,释放,加锁,解锁对称调用,以免出现内存泄漏,或者程序死锁,异常

Online Study Source

Recommended reading

管理篇

软件开发之项目管理

软件开发之团队管理

开发篇

Android-TV-开发速览

Android-NDK/JNI/C-脑图

Android-C/RoadMap-脑图

工具篇

Android-Gradle模块化构建

Vcs/Git版本控制

Android开发常用命令行

Android Helper软件开发工具集



The Page is End,Thanks for u reading

wx-laird-gzh.png