Android Studio下NDK开发流程
以前Android NDK开发需要在Eclipse或源码环境下,简历并配置Android.mk和Application.mk,并且还要通过java命令生产.h头文件才能编译生成so库,相当麻烦。随着AS作为官方Android开发工具,现在准备在AS上开发JNI应用,发现在AS上编译NDK非常方便,本文将介绍如何在Android Studio上实现NDK开发。
	1、简介
	JNI
JNI 是Java Native Inteface的缩写,是Java中定义的一种用于连接Java和C/C++接口的一种实现方式。
	NDK
NDK 是 Native Developmentit的缩写,是Google在Android开发中提供的一套用于快速创建native工程的一个工具。 
使用这个工具可以很方便的编写和调试JNI的代码
	Gradle
Gradle 是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置。
环境要求
- Android Studio 2.2或更高版本
 - Gradle 2.2或更高版本
 - Android NDK r10e或更高 下载地址
 - Build Tools 19.0.0或更高
 
	2、开始集成
	a) 创建一个简单的Android项目
新建一个Android测试项目,命名为:JniDemo。 

 
	b) 配置NDK路径
找到AS的状态栏,打开File->Project Structure,在Android NDK location栏选择离线NDK保存路径. 

 
	c) 配置gradle.properties文件
JNI模块构建方式有3种:
- 手动ndk-build构建。该方式需要手动编写Android.mk和Application.mk文件,然后手动调用ndk-build命令生成so文件,属于早期NDK开发方式,过程比较繁琐。
 - 自动ndk-build构建。使用ndkCompile工具自动构建so文件,需要手动生成头文件。
 - gradle-experimental方式全自动构建。
 
我们使用最简单最方便的构建方式,所以使用第二种。找到工程中的gradle.properties文件,添加以下代码:
android.useDeprecatedNdk=true
	
	d) 配置build.gradle文件
找到主module中的build.gradle文件,添加一下代码:
ndk{ 
    moduleName "JniCore"//编译生成so库的名字,注意不要lib和.so,和loadLibrary里面的参数一致
    ldLibs "log", "z", "m" //添加日志依赖库
    abiFilters "armeabi", "armeabi-v7a", "x86" //指定生成编译支持的平台so
}

 
	e) JNI编码
在java同级创建一个jni目录,相关的C/C++源码放在该目录下。我们这里以实现MD5哈希摘要为例。 
 
	
	f) JNI注册
	目前JNI注册方式有两种:静态注册和动态注册。个人偏向于使用动态注册,一方面可以防止方法名hook,另外一方面省了写.h文件。 
	JNI入口文件:main.c
/* DO NOT EDIT THIS FILE - it is machine generated */
        #include <jni.h>
        #include <android/log.h>
        #include <stdlib.h>
        #include <stdio.h>
        #include "md5.h"
        #define LOG_ENABLE
        #define  LOG_TAG "JNI_LOG"
        #define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
        #define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
        //指定注册的类,需要跟Java中native方法所在的包名一致
        #define JNIREG_CLASS "com/luoxudong/jnidemo/natives/JniCore"
        JNIEXPORT jstring JNICALL n_md5(JNIEnv * env, jobject jObj, jstring plaintext)
        {
            if (plaintext == NULL)
           {
                return NULL;
           }
           const char *pPlaintext = (*env)->GetStringUTFChars(env, plaintext, 0);
           char cMD5[32 + 1] = {0};
           MD5(pPlaintext, strlen(pPlaintext), cMD5);
         (*env)->ReleaseStringUTFChars(env, plaintext, pPlaintext);
          jstring ret = (*env)->NewStringUTF(env, (char *)cMD5);
          return ret;
        }
        static JNINativeMethod method_table[] = {
               {"md5", "(Ljava/lang/String;)Ljava/lang/String;", (void *)n_md5}
        };
        static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods)
        {
         jclass clazz;
          clazz = (*env)->FindClass(env, className);
           if (clazz == NULL)
          {
              return JNI_FALSE;
          }
          if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
          {
             return JNI_FALSE;
         }
         return JNI_TRUE;
        }
        static int registerNatives(JNIEnv* env)
        {
           if (!registerNativeMethods(env, JNIREG_CLASS, method_table, sizeof(method_table) / sizeof(method_table[0])))
          {
              return JNI_FALSE;
          }
           return JNI_TRUE;
        }
        JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
        {
          JNIEnv* env = NULL;
          jint result = -1;
         if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK)
          {
              return -1;
          }
         if (!registerNatives(env)) //注册
          {
              return -1;
          }
         result = JNI_VERSION_1_4;
          return result;
        }
	MD5头文件:md5.h
省略...
	MD5原文件:md5.c
省略...
	g) JAVA代码实现
创建类com.luoxudong.jnidemo.natives.JniCore.java文件,需要跟main.c中的”com/luoxudong/jnidemo/natives/JniCore”对应。 
 
	native文件:JniCore.java
package com.luoxudong.jnidemo.natives;
       public class JniCore {
         static {
             System.loadLibrary("JniCore");
          }
          public static native String md5(String text);
       }
	h) native方法调用
package com.luoxudong.jnidemo;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import com.luoxudong.jnidemo.natives.JniCore;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      TextView msgTxt = (TextView) findViewById(R.id.tv_msg);
      msgTxt.setText(JniCore.md5("test"));
    }
}
	i) 生成so文件
编译工程,在app/build/intermediates/ndk/debug/lib下面可以看到生成的各平台so文件。
	
	j) 其他
源码下载地址:JniDemo
转载请注明出处:http://www.luoxudong.com/?p=139 
本文首发于 东哥小栈(EastStack) · 书写不止是记录,更是思考的延伸。
      
