android--使用NDK-build生成so

    xiaoxiao2021-03-28  26

    环境: linux:Deepin15.4rc java:openJDK1.8

    1、将ndk添加至环境变量

    至添加一次即可,即下面语句使用一次之后以后就不用再使用了

    echo 'export PATH=~/Desktop/Android/android-sdk-linux/ndk-bundle/:$PATH' >> ~/.bashrc

    echo ‘export PATH=你的具体ndk路径/ndk-bundle/:$PATH’ >> ~/.bashrc 让环境变量生效的方法:

    重启若不重启,要使用之前先用source ~/.bashrc

    完成以上操作你就可以随处使用NDK-build这个命令了

    2、创建native类声明

    建议单独写一个native的java类,理论上MainActivity上写也是可以的,但是每次改动,你都要重新生成。 新建一个native包,里面放native的java类

    package com.example.myapplication.natives; /** * Created by hui on 17-4-4. */ public class Test { static { System.loadLibrary("test"); } public native String get(); public native void set(String str); }

    现在你肯定看到get和set方法红色吧。因为还没生成对应的.h头文件

    1、生成对应的class文件

    这步必须做,.class文件,在后面生成.h头文件要用到 对着那个Test.java,右键show in file manager,自动打开文件管理器

    javac Test.java

    生成了Test.class文件了

    2、 生成com_example_myapplication_natives_Test.h文件

    这个头文件是自动生成的,你可以看出上面声明包package com.example.myapplication.natives;这句与那个.h文件的命名规则了么?

    在文件管理器中,我们回退到这个java目录下 看到这个包声明的打头目录,就在这里打开命令行

    javah com.example.myapplication.natives.Test

    格式:javah 包声明.Test 因为javah要从包声明最顶层寻找Test.class,所以要在这个相对路径使用命令

    Android Studio项目上右键New->Folder->JNI Folder将.h头文件放进去gradle.properties添加android.useDeprecatedNdk=true

    android.useDeprecatedNdk=true这个很重要!!! 此时我们写的Test.java没有红色提示了。

    左边还多了两个红绿箭头。恩,Android Studio找到了.h头文件了。

    重点是那个.h的命名方式

    3、实现Native方法(函数)

    test.cpp和test.c的实现很类似,但是它们对env的操作方式有所不同,因此用C++和C来实现同一个JNI方法,它们的区别主要集中在对env的操作上,其他都是类似的,如下所示。 C++: env->NewStringUTF(“Hello from JNI !”); C:(*env)->NewStringUTF(env,”Hello from JNI !”); 摘自Android开发艺术探索

    这里我们编写c语言

    #include "com_example_myapplication_natives_Test.h" #include <stdio.h> JNIEXPORT jstring JNICALL Java_com_example_myapplication_natives_Test_get (JNIEnv *env, jobject thiz){ printf("start c get "); return (*env)->NewStringUTF(env,"hello from jni"); } JNIEXPORT void JNICALL Java_com_example_myapplication_natives_Test_set (JNIEnv * env, jobject thiz, jstring string){ printf("start c set"); char * str = (char *)(*env)->GetStringUTFChars(env , string , NULL); printf("%s\n",str); (*env)->ReleaseStringUTFChars(env , string ,str); } 要使用printf所以导入stdio.h,要实现.h中的方法,所以导入com_example_myapplication_natives_Test.h直接复制.h文件中的声明,给声明添加变量名env , thiz (取谐音thiz,clazz等,因为重复了关键字,所以一般用z代替s), string ,接着给个大括号,写逻辑吧。 JNIEnv *: 表示一个指向JNI环境的指针,可以通过它来访问JNI提供的接口方法jobject: 表示java对象中的thisJNIEXPORT和JNICALL: 它们是JNI中所定义的宏,可以在jni.h这个头文件中找到 摘自Android开发艺术探索

    4、生成对应so

    编译前的准备:

    1、编写Android.mk

    LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := test LOCAL_SRC_FILES := test.c include $(BUILD_SHARED_LIBRARY)

    其中 LOCAL_MODULE表示模块名称 LOCAL_SRC_FILES表示需要参与编译的源文件 除了这两个,其他照搬即可

    2、编写Application.mk

    APP_ABI := armeabi

    用作配置要编译的CPU架构平台的类型

    3、生成so库文件

    1、直接编译(想想是不可以的,因为没有指定平台(arm、x86等),当扩展知识吧)

    PS:如果看不懂我括号写的意思,直接跳过这个3.1就好了,3.2也是生成so的方法

    切换到jni文件夹中,命令行使用命令 小插曲:

    hui@hui-PC:~/AndroidStudioProjects/MyApplication/myapplication2/src/main/jni$ gcc -shared -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -fPIC test.c -o libtest.so In file included from com_example_myapplication_natives_Test.h:2:0, from test.c:1: /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/jni.h:45:20: fatal error: jni_md.h: 没有那个文件或目录 #include "jni_md.h" ^ compilation terminated.

    找不到jni_md.h,文件管理器,一找,jni_md.h在下一级目录的linux文件夹中,所以正确命令是

    gcc -shared -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/ -I /usr/lib/jvm/java-1.8.0-openjdk-amd64/include/linux/ -fPIC test.c -o libtest.so

    2、NDK-build

    在jni的上一级目录,使用命令

    hui@hui-PC:~/AndroidStudioProjects/MyApplication/myapplication2/src/main$ ndk-build [armeabi] Compile thumb : test <= test.c [armeabi] SharedLibrary : libtest.so [armeabi] Install : libtest.so => libs/armeabi/libtest.so

    自动生成了,libs目录

    4、使用so

    新建一个jniLibs,将libs下的文件夹全部复制到jniLibs 并在build.gradle(记得这是module的,不是project的)下添加如下

    android { ... sourceSets{ main{ //jniLibs.srcDirs = ['src/main/jniLibs'] jni.srcDirs = [] } } }

    新版的gradle语法变成sourceSets{main{ }}了,不是以前的sourceSets.main{ }了。

    其实网上都有,感觉自己写得复杂了。 重点有三个吧。

    令native的java类报错消失:添加android.useDeprecatedNdk=true,用于兼容新版dnk移动so:需要把libs下的全部复制到jniLibs,不能仅仅复制一个so文件,接着libs下面的文件你想删了也行,让app识别so路径: android { ... sourceSets{ main{ //jniLibs.srcDirs = ['src/main/jniLibs'] jni.srcDirs = [] } } }

    不这么做会提示找不到so,奇怪了,so在jniLibs下面,却要写jni.srcDirs

    PS:如果出现奇怪的错误,那么clear一下project

    最后简单调用即可,这里简单的在onreate里面调用toast就算了

    Toast.makeText(MainActivity.this , "" + new Test().get(), Toast.LENGTH_SHORT).show();
    转载请注明原文地址: https://ju.6miu.com/read-664600.html

    最新回复(0)