Android Studio NDK开发

以前接触过NDK的开发,是在Eclipse环境下开发的。今天尝试了下用Android Studio来配置,结果真是处处都是坑,现在总结一下:

一、步骤

1. 首先创建MainActivity,添加native方法:

package com.jackie.hellondk;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    public native String getStringFromNative();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
注意,我用到了v7兼容包下的AppCompatActivity,下面编译头文件时,可能会用到这点知识。然后,一定要点击下方的绿色箭头,make project,确保生成了.class文件。


2. 在main目录下创建一个jni目录,用来放头文件,可以用下面的方法:

Android Studio NDK开发_第1张图片

3. 编译头文件

在Terminal中进入到项目的根目录,对于Android Studio来说,切换到app\src\main就行,按照网上的说法,直接执行下面的命令:

javah -d jni com.jackie.hellondk.MainActivity(-d jni 指定头文件生成在jni目录下,如果没有第二步创建jni文件夹,也会自动创建)


很明显,找不到MainActivity的.class字节码文件,然后按照官网的方法,指定MainActivity.class的路径,在Android Studio中,所有的.class文件都生成在app\build\intermediates\classes\debug下面,所以,执行下面的命令:

javah -classpath ..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity

Android Studio NDK开发_第2张图片

这是什么原因呢?这是由于我们上面的MainActivity继承了AppCompatActivity,这个类是在Android v7的兼容包下,所以还需要v7的兼容包加入到classpath中:

javah -classpath D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity

Android Studio NDK开发_第3张图片

同样,也需要将v4的兼容包加入到classpath中:

javah -classpath D:\DevTools\studio_sdk\extras\android\support\v4\android-support-v4.jar;D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity

Android Studio NDK开发_第4张图片

无语了,真是处处都是坑啊,继续把android.jar添加到classpath中呗!

javah -classpath D:\DevTools\studio_sdk\platforms\android-23\android.jar;D:\DevTools\studio_sdk\extras\android\support\v4\android-support-v4.jar;D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity

终于成功生成了头文件,如下:

Android Studio NDK开发_第5张图片

注意,我上面之所以需要添加如此多的classpath,是由于我用到AppCompatActivity,一般情况下只需要将android.jar和.class文件的目录(app\build\intermediates\classes\debug)添加到classpath中就行,在实际开发中,如果大家出现找不到类的错误,请自行添加classpath就行,注意,中间用分号隔开。另外,如果觉得每次敲这么多命令太麻烦,也可以将上面的classpath配置到系统的环境变量中,至于怎么弄,相信大家都OK啦!这里不作详细讲述。

4. 实现native方法

在jni目录新建一个hello.c文件,实现头文件中的方法:

#include <stdio.h>
#include <stdlib.h>
#include "com_jackie_hellondk_MainActivity.h"

JNIEXPORT jstring JNICALL Java_com_jackie_hellondk_MainActivity_getStringFromNative
  (JNIEnv *env, jclass jclass) {

  return (*env)->NewStringUTF(env, "Hello from JNI");
}
5. 编写Android.mk文件
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello
LOCAL_SRC_FILES := hello.c

include $(BUILD_SHARED_LIBRARY)

6. 编译动态链接库so,注意要切换到jni所在的目录,执行ndk-build之前,还需要配置环境变量。

Android Studio NDK开发_第6张图片

生成目录如下:

Android Studio NDK开发_第7张图片

6. 引用

package com.jackie.hellondk;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("hello");
    }

    TextView mTextView;

    public native String getStringFromNative();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView.setText(getStringFromNative());
    }
}
编译会出现下面的错误:


在gradle.properties添加下面一句:

android.useDeprecatedNdk=true

继续编译,错误如下:


配置如下:

Android Studio NDK开发_第8张图片

编译后,安装完成后,还是会出现java.lang.UnsatisfiedLinkError couldn't find libhello.so的错误。

修改build.gradle配置,在defaultConfig里面新增一下代码:
ndk {
     moduleName "hello"
     abiFilters "armeabi", "armeabi-v7a", "x86"
 }
注意,这里的moduleName一定要和System.loadLibrary以及Android.mk中定义的名称一致。

最后一次运行,终于看到了久违的界面啊!

Android Studio NDK开发_第9张图片

走了很多弯路,真是处处都是坑,做个记录,希望对你们有所帮助。最后说一点,上面用ndk-build来编译生成动态链接库libhello.so,然后在Java中通过loadLibrary来加载。在实际开发过程中,完全可以省去ndk-build这一步,开发好jni程序后,直接运行程序,Android Studio会自动帮我们编译动态链接库,亲测成功!

二、打印log信息

相信很多人在刚开始学习Android JNI编程的时候,需要输出Log,于是按照下面的方法修改hello.c文件:

#include <stdio.h>
#include <stdlib.h>
#include "com_jackie_hellondk_MainActivity.h"
#include <android/log.h>
#define TAG "Jackie"
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)

JNIEXPORT jstring JNICALL Java_com_jackie_hellondk_MainActivity_getStringFromNative
        (JNIEnv *env, jclass jclass) {

   LOGV("log from native");
   /**
    * c语言
    */
   return (*env)->NewStringUTF(env, "Hello from JNI");
   /**
    * C++
    * return env->NewStringUTF("Hello from JNI");
    */
}
然后修改Android.mk文件:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello
LOCAL_SRC_FILES := hello.c
# for logging
LOCAL_LDLIBS    += -llog
include $(BUILD_SHARED_LIBRARY)

运行时,总是出现undefined reference to '__android_log_print'的错误,有的同学可能很奇怪,Android.mk文件和android/log.h头文件都引入了,怎么还会有这个错呢?

Android Studio的Android.mk是自动生成的,就算修改也是没用了,实际Android Studio的Android.mk是根据gradle文件生成的,那么就需要修改gradle文件。如果不修改gradle,直接使用__android_log_print就会报错。

Error:(36) undefined reference to '__android_log_print'

现在只需要在module的build.gradle中添加下面的代码即可实现输出Log:1

Android Studio NDK开发_第10张图片

添加箭头的部分,就OK啦!


你可能感兴趣的:(NDK,Studio,andnroid)