android jni开发指南 - Go语言中文社区

android jni开发指南


开发环境说明

  1. Android Studio 3.3.2
  2. java version “1.8.0_201”
  3. NKD 19.2.5345600
  4. com.android.tools.build:gradle:3.3.2

工具准备

这里假设读者是一位有一定经验的android开发者,对Android开发环境的搭建有一定的经验,为此简单的说一下IDE的配置。在AndroidStudio开发工具的基础上,下载 NDK 和工具:

  1. Android 原生开发工具包 (NDK):这套工具允许您为 Android 使用 C 和 C++ 代码。
  2. CMake:一款外部构建工具,可与 Gradle 搭配使用来构建原生库。如果您只计划使用 ndk-build,则不需要此组件。
  3. LLDB:一种调试程序,Android Studio 使用它来调试原生代码。

可以使用 SDK 管理器安装这些组件,在打开的项目中,从主菜单选择 Tools > SDK Manager > Android SDK > SDK Tools,选中相应的工具,点击apply即可下载。
在这里插入图片描述
因为使用 NDK 构建代码主要有二种方法:ndk-buildCMake。因此本文分别就这两种方式展开下文。

CMake方式

在现有版本的androidStudio上,此种方式较为简单,直接New > New Project,在Phone and Tablet选择 Native C++ 项目,如图Native C++
然后一路Next,到C++ Standard选择 Toolchain Default,再点击finish,在这里插入图片描述
至此,一个采用CMake方式构建的Jni项目就已经建好了,可以直接run到手机,配置文件都是默认的。
java代码如下

package com.hai.cmake;
public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
    public native String callJni();

    /**
     * 被JNI调用的方法
     */
    public String stringFromJava() {
        return "JNIGETSTRINGFROMJAVA";
    }

相应的Jni实现,就一个native-lib.cpp文件,没有.h文件,采用Cmake方式就是如此简单

#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_hai_cmake_MainActivity_stringFromJNI(//注意方法名和MainActivity的包名的关系,JNI方法名要求如此
        JNIEnv *env, jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

/**
 * jni调用java方法
 * @param env
 * @param thiz
 * @return
 */
jstring stringFromJava(JNIEnv *env, jobject /* this */thiz) {
    jclass cls = env->GetObjectClass(thiz);
    jmethodID methodId = env->GetMethodID(cls, "stringFromJava", "()Ljava/lang/String;");
    jobject ob = env->AllocObject(cls);
    jstring str = static_cast<jstring>(env->CallObjectMethod(ob, methodId));
    return str;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_hai_cmake_MainActivity_callJni(JNIEnv *env, jobject instance) {
    return stringFromJava(env, instance);
}

app module的build.gradle部分配置

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    externalNativeBuild {//此处配置可能会因gradle版本不同而有差异
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
}

CMakeLists.txt的内容如下

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSI    ON 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
        native-lib
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        native-lib.cpp#此处可以写入多个cpp文件,以逗号分隔)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
        log-lib
        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

到这,CMake方式构建的Jni项目就已经讲解完毕了。写的比较简单,因为这种方式的项目是android Studio里面默认自带的Jni Template Project。

ndk-build方式

为什么要写这种方式呢?个人认为,这种方式从最基本的声明JNI方法、生成.h头文件、编写cpp方法实现,到最后编写Android.mk文件,一路下来能让JNI初学者由浅入深一步一步的了解JNI项目的开发过程,从而掌握JNI项目开发。
下面也是从New > New Project开始,选择新建一个普通的Android项目即可。新建项目的流程简单,直接滤过。项目中就一个java文件:MainActivity.java,在其中写一个native方法,
在这里插入图片描述
此时native方法为红色,不用管。
下一步利用JDK自带的javah工具生成.h头文件,比如我的java源文件目录为E:workspace_AsNdkBuildappsrcmainjava,因为javah参数必须要包含包路径,cmd模式下进入E:workspace_AsNdkBuildappsrcmainjava,然后执行命令:

E:workspace_AsNdkBuildappsrcmainjava>
E:workspace_AsNdkBuildappsrcmainjava>javah -jni com.hai.ndkbuild.MainActivity

就会在当前目录下生成.h头文件,比如我的是:com_hai_ndkbuild_MainActivity.h,内容如下

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_hai_ndkbuild_MainActivity */
#ifndef _Included_com_hai_ndkbuild_MainActivity
#define _Included_com_hai_ndkbuild_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_hai_ndkbuild_MainActivity
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_hai_ndkbuild_MainActivity_stringFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

如果生成.h头文件失败,注意检查包名、文件路径是否正确,或是clean项目然后build一下试试。
这是根据java文件生成.h头文件的一种方法,还有另一种方法是根据.class文件生成.h头文件。
比如我写了一个JniUtil.java,
在这里插入图片描述
进入到java的编译产物目录,也就是.class目录,比如我的:E:workspace_AsNdkBuildappbuildintermediatesjavacdebugcompileDebugJavaWithJavacclasses,然后:

E:workspace_AsNdkBuildappbuildintermediatesjavacdebugcompileDebugJavaWithJavacclasses>
E:workspace_AsNdkBuildappbuildintermediatesjavacdebugcompileDebugJavaWithJavacclasses>javah -jni com.hai.ndkbuild.JniUtil

这样也是可以生成com_hai_ndkbuild_JniUtil.h文件的,且内容都是一样。
下一步就是新建一个文件夹用来存放.h头文件和cpp文件,以及nkd构建文件。
项目切换到project模式,右击main > New> Folder > Jni Folder,之后会弹出对话框让选择文件位置,IDE默认给的路径是 src/main/jni/,因为JNI文件夹一般是cpp,所以这里我们也改为cpp,在这里插入图片描述
点击finish新建cpp文件夹,然后把上一步生成的com_hai_ndkbuild_MainActivity.h移动到cpp目录,复制com_hai_ndkbuild_MainActivity.h文件并且修改文件名为com_hai_ndkbuild_MainActivity.cpp,就可以编写方法实现了。
此时IDE可能还不知道此项目为JNI项目,为了让IDE能支持C++语法方便编写cpp,我们先建立JNI配置文件:
右击cpp > New > File
在这里插入图片描述
生成一个空的Android.mk文件,Android.mk内容如下

#固定写法
LOCAL_PATH:=$(call my-dir)
#固定写法
include $(CLEAR_VARS)
#生成so名称
LOCAL_MODULE := JniHelper
LOCAL_SRC_FILES := com_hai_ndkbuild_MainActivity.cpp
#固定写法
include $(BUILD_SHARED_LIBRARY)

此时build 项目应该会报错,不用管。接下来右击app module
在这里插入图片描述
build System选择nkd-build,Project Path选择Android.mk的路径,
在这里插入图片描述
ok之后,项目被自动build一次,且被IDE识别为JNI项目了。app module的build.gradle文件自动新增externalNativeBuild配置:

android {
    sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } }
    externalNativeBuild {//此处配置可能会因gradle版本不同而有差异
        ndkBuild {
            path file('src/main/cpp/Android.mk')
        }
    }
}

因为IDE支持C、C++语法,项目也被识别为JNI项目了,此时修改com_hai_ndkbuild_MainActivity.cpp的内容就方便多了,修改com_hai_ndkbuild_MainActivity.cpp内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "com_hai_ndkbuild_MainActivity.h"
JNIEXPORT jstring JNICALL Java_com_hai_ndkbuild_MainActivity_stringFromJNI
        (JNIEnv *env, jobject) {  //此处自己补了个env
    return env->NewStringUTF("I am From Native C");
};

到这,基本完成,最后就是使用了,很简单:

public class MainActivity extends AppCompatActivity {
    static {//加载so
        System.loadLibrary("JniHelper");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((TextView) findViewById(R.id.tv)).setText(stringFromJNI());
    }

    public native String stringFromJNI();
}

结语

大家都知道,现在网络上关于JNI开发的文章一搜一大把,简单的复杂的都有。但我为什么还有写一篇呢,网上搜不就行了吗?原因有二,其一:网上的文章描述的不大符合我的要求,有的缺少不要的环境说明,大家都知道开发环境不同相应的配置也可能不同;其二:自己从未记录过JNI开发的步骤,写此博文也是为了加深自己对JNI开发的理解,怕以后忘了熟悉起来也方便。
Thanks!

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/u014763302/article/details/89309406
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢