zoukankan      html  css  js  c++  java
  • Android studio 下JNI编程实例并生成so库

        最近需要使用JNI编程,学了下JNI,并且在Android Studio下实现了一个小demo。这期间有一些坑,还好都解决了,想分享出来,希望大家少走弯路。本文中采用的平台是Windows,NDK环境已经搭建好,这方面资料很多,大家可以自行百度。

    本文分为两个部分:

    1.如何通过编写Jni实现native方法的调用。

    2.怎样生成.so动态库提供给第三方使用。

    以下是正文:

    一.编写jni文件,实现本地方法

    1. 建立一个新工程,只有一个MainActivity,里面加载库文件并且声明和调用若干本地方法,然后build-makeProject生成MainActivity对应的.class文件。

    public class MainActivity extends Activity {
    private final String TAG = "JNITEST"
    @Override
    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String s=returnString(); Log.d(TAG,s); int a=1000; Log.d(TAG,sayhello(a)); }
      //加载jni
    static { System.loadLibrary("nativeTest"); }
      //声明native方法
    private native int sayhello(int t); private native String returnString(); }

    2. 生成.h头文件,该文件即连接java和c(c++)的桥梁,里面有Android工程里本地方法的声明。这个文件可以在控制台用javah命令自动生成。注意,这里可能会碰到问题,比如我第一次就出现了找不到app.activity ,即找不到类文件,这种问题一般是没有理解javah的用法造成的。可以采用以下两种方法:

    方法1:   cd到       E:\shijue\JniHello\app\src\main

    然后输入      javah -d jni -classpath I:\Andriod\AndroidSDK\platforms\android-15\android.jar;
    E:\shijue\JniHello\app\build\intermediates\classes\debug com.example.machenike_pc.jnihello.MainActivity

    说明:javah是生成头文件的命令,深绿色为生成文件夹jni,紫红色为android.jar所在的位置,浅绿色为class文件的路径+类全名(路径最后一个文件夹是debug之后空格+类全名)

    (这里补充下-classpath的含义:javah操作是针对类文件,-bootclasspath和-classpath就是指定在哪里进行类文件搜索。JDK搜索类文件先后顺序如下:Bootstrap classes,User classes。Bootstrap默认的是JDK自带的jar或zip文件,它包括jre\lib下rt.jar等文件,JDK首先搜索这些文件.可以通过-bootclasspath来设置它。文件之间用分号";"进行分割。User classes搜索顺序为当前目录、环境变量 CLASSPATH、-classpath。它们用于告知JDK搜索类文件根目录名、jar文档名、zip文档名,用分号";"进行分隔。)

    方法2:   cd到E:\shijue\JniHello\app\build\intermediates\classes\debug目录下,直接javah -d jni com.example.machnike_pc.jnihello.MainActivity 即可

    3. 在生成的jni目录下写一个c或者c++文件,文件名随意,实现本地方法 ,之后需要在该路径下再加一个空的cpp或c文件(估计是软件的bug,不加的话很可能出ndk错误),比如我加了个util.cpp的文件,里面什么都不写。

     下面是我的c++文件代码

    #include<jni.h>
    #include<stdio.h>
    #include<com_example_machenike_pc_jnitest2_MainActivity.h>
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    JNIEXPORT jint JNICALL Java_com_example_machenike_1pc_jnitest2_MainActivity_sayhello
      (JNIEnv *, jobject, jint);
    
    JNIEXPORT jstring JNICALL Java_com_example_machenike_1pc_jnitest2_MainActivity_returnString
      (JNIEnv *, jobject);
    
    #ifdef __cplusplus
    }
    #endif
    
    
    JNIEXPORT jint JNICALL Java_com_example_machenike_1pc_jnitest2_MainActivity_sayhello
        (JNIEnv * env, jobject jobj, jint jnumber)
        {
            int modify=jnumber+1;
            return modify;
    
    
        }
    JNIEXPORT jstring JNICALL Java_com_example_machenike_1pc_jnitest2_MainActivity_returnString
        (JNIEnv *env, jobject jobj)
        {
           return env->NewStringUTF("I'm comes from to Native Function!");
       }

    4,如果ndk版本不是最新的,需要在gradle.properties文件下加入:

    android.useDeprecatedNdk=true  

    5,配置ndk路径,这里也可以在AS的设置里面配置。我采用的方法是在local.properties文件最后一行加入:

    ndk.dir=I\:\\Andriod\\NDK\\android-ndk-r10b

    6,build.gradle(app下):文件下加入:(defaultconfig里面)

    ndk{
    moduleName "nativeTest"
    }

    此时运行程序已经可以实现本地方法了,之后可以再生成so库文件,方便使用。

    二,生成.so动态库

    (这里说一下,貌似Android studio已经写好了.mk文件,上面的步骤完成后,直接rebuild一下就自动生成为了.so动态库,下面的方法也能生成,可以看一下,很有用)

    1,在jni文件夹下新建Android.mk文件,写入以下内容:

    LOCAL_PATH := $(call my-dir)           //固定写法,把路径赋给LOCAL_PATH变量
    include $(CLEAR_VARS)                  //清除其他LOCAL变量
    LOCAL_MODULE := nativeTest             //这个模块的名字,最后生成的.so的名字就是它,要跟java里面的loadLibray的名字一样。
    LOCAL_SRC_FILES := nativeTest.cpp\     //这里是要编译的文件,\ 符号是换行
                util.cpp
    include $(BUILD_SHARED_LIBRARY)        //SHARED_LIBRARY就是动态库,即.so文件

     这里的写法是最简单的一个例子,用的时候把注释去掉。每一行都是很关键,不能省略。至于makefile怎么编写内容比较多,此处不赘述。

    2,在工程根目录下新建application.make文件,写入以下内容:

    APP_PROJECT_PATH := $(call my-dir)
    APP_MODULES := nativeTest

    3,在命令行下,cd到jni目录(就是之前javah -d jni生成的那个文件夹)下,输入指令: ndk-build,等一会即可生成.so文件。位于lib目录下,将其放到app/src/main/jniLibs目录下就能用了。

    FAQ:

    1,生成的so文件在使用时需要注意:包名不能变,拿上文举例,本地方法位于com_example_machenike_pc_jnitest2_MainActivity这个类下,如果在别的地方用,需要完整的建立这个包名和类。

    2,c和cpp文件均可以用来写jni,写法上略有不同。

    3,需要注意java里面成员方法和静态方法通过javah生成的头文件略有不同,一个参数是jclass,另一个是jobject。

    4,不用javah生成头文件也行,推荐第一次写的时候用javah生成,后面修改的时候(比如参数改变)可以直接在c文件里手动修改。

  • 相关阅读:
    牛客练习赛83题解
    1525 F. Goblins And Gnomes (最小顶点覆盖输出方案)
    hash表
    欧拉回路输出方案
    dp优化
    1
    fwt原理学习和一些拓展
    SpringBoot在IDEA中的配置
    ES: memory locking requested for elasticsearch process but memory is not locked
    Prometheus监控大数据
  • 原文地址:https://www.cnblogs.com/jymblog/p/5526865.html
Copyright © 2011-2022 走看看