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

有3人对 “Android Studio下NDK开发流程”留言了

  1. Greetings from Los angeles! I’m bored to death at work so I
    decided to check out your blog on my iphone
    during lunch break. I love the information you present here
    and can’t wait to take a look when I get home. I’m amazed at how fast your blog loaded on my phone ..

    I’m not even using WIFI, just 3G .. Anyhow, very good blog!

  2. Terrific article! That is the kind of information that should be shared around the net.
    Shame on Google for now not positioning this put up
    upper! Come on over and discuss with my website
    . Thank you =)

  3. I think this is among the most vital information for me.
    And i am glad reading your article. But wanna remark on few general things, The site style is wonderful, the articles is really
    excellent : D. Good job, cheers

beritaligasatu.com进行回复 取消回复

邮箱地址不会被公开。