我的Android NDK之旅(一),不使用ndk-build命令来创建jni

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35071078/article/details/70338637

转载请注明出处:(http://blog.csdn.net/qq_35071078/article/details/70338637

最近闲来无事,想摸索下一下ndk,可是ndk不是块好啃的骨头,但作为一名程序员,什么都要了解下,对吧╮( ̄▽ ̄)╭。首先我想吐槽一下,网上有些博客写的很乱,一上来就贴一段代码,也不告诉是要干什么,代码一写完就完事,这让初学者很难理解jni到底是个什么东西。每次按照他们的方法敲,敲着敲着就不知道要干什么了,然后程序也运行不了。不过有些大神确实总结的很好。所以我想通过这几天的摸索,总结一下。
对于jni,ndk到底是什么,我就不多说了,百度讲的很清楚。对于如何创建jni,其实很简单:

  1. 在.java文件中声明native方法 。
  2. 编译这个.java文件得到.class文件 。
  3. 用javah命令作用于这个.class文件生成.h文件。
  4. 创建一个.c或者.cpp文件,在代码中include刚生成的.h文件,然后再实现里面的方法 ,注意方法的命名是有特定的要求的,后面会讲到。
  5. 最后就是生成.so文件了。目前博主知道的三种方法:

    • 通过ndk-build(这个需要Android.mk文件和Application.mk文件)生成。
    • cmake(需要CMakeLists.txt)方式生成。
    • 直接通过android studio编译(这篇文章讲的)来生成。

不使用ndk-build命令,直接通过android studio编译的方式来创建jni

首先在android studio上创建一个项目,

刚创建的项目的结构图

接下来创建一个类 MyJni,这个类用来实现native方法:

这里创建了一个类,用来实现native方法

接下来编译项目:

这里写图片描述

编译完成之后,就会在app/build/../com/..下生成相应的classes文件了,接下来就是用 javah 这个指令,将刚才编译好过了的MyJni.class编译成 .h 文件,首先打开终端,点击android studio下方的Terminal,这里默认路径是在当前的项目目录下。

这里写图片描述

在这里输入 cd app/build/intermediates/classes/debug,进入debug目录,然后在这里输入javah指令来生成.h文件。输入 javah com.chenxin.MyJni ,之后就会在当前目录(../debug)下生成一个com.chenxin.MyJni.h文件。为什么要在debug目录下输入这段指令呢?因为我在其他地方试了,不行啊⊙﹏⊙‖∣° 。javah后面就是要接完整的包名+类名,包名是从com.开始的,所以我们要到com.的父级目录下,进行javah操作。

这里写图片描述

.h文件里面的代码如下,其他的不用管,只需要知道JNIEXPORT jstring JNICALL Java_com_chenxin_MyJni_getString
(JNIEnv *, jclass);这个就是我们在MyJni.java里面声明的native方法。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_chenxin_MyJni */

#ifndef _Included_com_chenxin_MyJni
#define _Included_com_chenxin_MyJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_chenxin_MyJni
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_chenxin_MyJni_getString
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

在app/src/main下创建一个jni文件夹(Directory),因为studio默认的c和c++文件是在这里面,将刚才的那个.h文件剪切到这里面来,然后在jni文件夹里创建一个c/c++ source file,随便叫什么,我这里就叫test,后缀名是都.c或者.cpp都可以,只不过函数的实现上稍微有点差异。在这个.c或者.cpp文件中,首先得导入jni.h,然后导入com_chenxin_MyJni.h,就是刚才用javah生成的.h文件。如果是.cpp的话:

#include "jni.h"
#include "com_chenxin_MyJni.h"

JNIEXPORT jstring JNICALL Java_com_chenxin_MyJni_getString
  (JNIEnv *env, jclass jz){

    return env->NewStringUTF("this is jni , this is my new life!");
  }

如果是.c的话:

#include "jni.h"
#include "com_chenxin_MyJni.h"

JNIEXPORT jstring JNICALL Java_com_chenxin_MyJni_getString
        (JNIEnv *env, jclass jz){

    return (*env)->NewStringUTF(env,"this is jni , this is my new life!");
}

对了,还得在gradle.properties里面添加一行代码,不然会报错,因为这种方法谷歌现在已经不提倡了。

android.useDeprecatedNdk=true

接下来回到MyJni.java文件,添加代码来加载so文件,此时还没有生成,等下编译项目时会自动生成,自动生成的.so的默认名字是app。

public class MyJni {

    static {
        System.loadLibrary("app");
    }

    public native static String getString();

}

不过如果想自定义生成的so的名字,可以在build.gradle中加入:

        ndk{
            moduleName "myLib"
        }

我这里将生成的so的名字改成了myLib,所以将上面的 “app” 改成 “myLib” ,例如:

build.
android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
       ......

        ndk{
            moduleName "myLib"
        }

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

MyJni.java文件中:
    static {
        System.loadLibrary("myLib");
    }

也可以设置指定生成什么类型的so文件,这里我就不详细讲解了。想加的话直接在modulueName下面添加:

abiFilters "armeabi", "armeabi-v7a"

然后回到MainActivity,使用Log打印以下,看下是否调用成功了:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e("info","调用jni的结果  :"+TestJni.sayHello());
    }
}

编译运行
结果:
这里写图片描述

而且这时在打开app/build/intermediates下就可以看到对应的so文件了

这里写图片描述

也可以使用 Analyze 来看当前项目的结构:

这里写图片描述

这里写图片描述

这里写图片描述

ok,大功告成。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页