(4.1.27.1)Android studio---简单的JNI实例

1 JNI开发基本步骤

①编写.java类
②生成.class文件
③生成.h文件
③查阅.h文件
  JNIEXPORT 和 JNICALL 是jni的宏
  函数前的注释Signature: ()Ljava/lang/String;中的括号含义为:表示函数的参数为空,Ljava/lang/String表示函数的返回值是java的String对象
④编写.c/cpp文件
⑤创建 Android.mk文件(和.c文件同级目录)
⑥编译生成.so文件


2 实例:JniTest

【以下实例我是在app中直接建的,但是最好方式是自己做一个新的module(android library)专门处理JNI操作,步骤是相同的:new module-----android library----MyJni】

①在module中,编写JniTest.java类,源码如下

复制代码
package test.com.MyJni;

/**
 * 类描述:
 *
 * Created by sf on 2016/5/19.
 */
public class JniTest {

    //使用JNI的关键字native
    //这个关键字决定我们那些方法能在我们的C文件中使用
    //只须声明,不必实现
    public native void yu();
    public native double sum(double x,double y);
}
复制代码

新建一个java文件,可以看出第二行有个native,说明这个函数是java和其他语言(c/c++)协作时用的,并用其他语言实现。


可以自定义路径,默认是需要在你需要把C/C++源代码放在[module]/src/main/jni/路径下,自定义需要在步骤5.2.2当前的module中的gradle配置自定义路径

②编译生成TestJNI.class文件

编译方法:

  方法1:执行Build->Make Project  

                    这一步骤执行一下,验证工程中并无其它错误,并对工程进行了编译,生成了.class文件.

                   .class文件的生成路径是在 app_path/build/intermediates/classes/debug下的.如下图:




  方法2:运行cmd,定位到工程目录,用javac命令执行即可

③编译生成test_com_MyJni_JniTest.h文件


需要注意的是,如果你操作熟练,了解.h的生成规则,那么就不需要步骤2、3,直接手动写出.h文件并开始第4步即可,效果是一样的

利用javah -jni -classpath . HelloWorld生成对应C/C++的HelloWorld.h文件,设置classpath的目的在于告诉java环境,在哪些目录下可以找到所要执行的java程序所需要的类或者包,此处不用声明.class文件名,在对应目录下编译器会自动找到

3.1 点击"View->Tool Windows->Terminal"

       在Studio中进行终端命令行工具.执行如下命令生成c语言头文件。

3.2  进入当前文件夹  保证文件输出到可视位置

       cd app/src/main /java/test/com/MyJni 

3.3  命令javah -d jni -classpath <SDK_android.jar>;<class所在文件夹路径> class包名+全名

javah -d jni -classpath D:\Users\sf\AppData\Local\Android\sdk\platforms\android-21\android.jar;..\..\..\..\..\..\build\i
ntermediates\classes\debug test.com.MyJni.JniTest

复制代码
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class test_com_MyJni_JniTest */

#ifndef _Included_test_com_MyJni_JniTest
#define _Included_test_com_MyJni_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test_com_MyJni_JniTest
 * Method:    yu
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_test_com_MyJni_JniTest_yu
  (JNIEnv *, jobject);

/*
 * Class:     test_com_MyJni_JniTest
 * Method:    sum
 * Signature: (DD)D
 */
JNIEXPORT jdouble JNICALL Java_test_com_MyJni_JniTest_sum
  (JNIEnv *, jobject, jdouble, jdouble);

#ifdef __cplusplus
}
#endif
#endif
复制代码

native方法名称转换详解:

英语原文 我的理解… 例子

the prefix Java_

转换后的native方法全部以Java_为前缀 Java_kg_tom_MyJni_display

a mangled fully-qualified class name

接着以类的全名标识(包名+类名) Java_kg_tom_MyJni_display
  • an underscore (“_”) separator
以下划线为分隔符(“_”) Java_kg_tom_MyJni_display
mangled method name 最后就是方法的名称 Java_kg_tom_MyJni_display
  • for overloaded native methods, two underscores (“__”) followed by the mangled argument signature
这个理解不能:直译的话就是对已经读取过的native方法,用双下划线(“__”)标注 没找到

④编写.c文件。

  可以直接用记事本编写,当然如果你的Eclipse环境已集成c/c++开发环境了(方法见【Android學習專題】搭建Android NDK环境 ),可以直接在Eclipse中编写

  代码如下所示,描述见注释:

复制代码
//必须的头文件jni.h
#include <jni.h>
//导入我们需要实现的本地方法
#include "test_com_MyJni_JniTest.h"
#include <stdio.h>

/*
 * Class:     test_com_MyJni_JniTest
 * Method:    yu
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_test_com_MyJni_JniTest_yu
   (JNIEnv *env, jobject obj){
    LOGE("log string from ndk.");
    printf("Hello World tom!!");
        return;
  }

  /*
   * Class:     test_com_MyJni_JniTest
   * Method:    sum
   * Signature: (DD)D
   */
  JNIEXPORT jdouble JNICALL Java_test_com_MyJni_JniTest_sum(JNIEnv *env, jobject obj, jdouble a, jdouble b)

   return a+b;
    }
复制代码

⑤创建 Android.mk文件(和.c文件同级目录) 或配置grable

5.1 MK文件  用于表示so生成规则 [eclipse需手动配置,gradle自动生成]

我们实现好.c或者.cpp文件后,编写Android.mk文件,来生成动态库,一般使用NDK工具进行生成,首先是下载ndk包,然后设计全局变量,进入Android.mk文件夹中执行ndk编译命令即可。

ndk编译命令使用参考:

http://www.cnblogs.com/lipeil/archive/2012/08/27/2659378.html

http://blog.csdn.net/laczff21/article/details/7542236

我们进入.c,.h, Android.mk所在的文件下面,然后执行ndk编译命令:

ndk-build

信息如下:

abc@abc:~/workspace/NativeTest/jni$ndk-build

AndroidNDK: WARNING: APP_PLATFORM android-19 is larger thanandroid:minSdkVersion 8 in/home/archermind/workspace/NativeTest/AndroidManifest.xml

[armeabi]Compile++ thumb: NativeClassJni <=com_example_nativetest_NativeClass.cpp

[armeabi]StaticLibrary : libstdc++.a

[armeabi]SharedLibrary : libNativeClassJni.so

[armeabi]Install : libNativeClassJni.so =>libs/armeabi/libNativeClassJni.so


 5.2 gradle

到这里后,我们再执行一个"Build->Make Project",发现"Messages Gradle Build"会给出提示如下:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Error:Execution failed for task ':app:compileDebugNdk'.   
  2. > NDK not configured.   
  3. Download the NDK from http://developer.android.com/tools/sdk/ndk/.Then add ndk.dir=path/to/ndk in local.properties.   
  4. (On Windows, make sure you escape backslashes, e.g. C:\\ndk rather than C:\ndk)  
这里提示了NDK未配置,并且需要在工程中的local.properties文件中配置NDK路径。好了,提示很清楚了,那我们就进入配置NDK。
5.2.1 配置NDK


5.2.2 修改build.gradle配置
在 local.properties 文件中设置ndk的路径,一般会自动生成

gradle.properties中   官方已有新的方式使用,在这里true,表示还是用旧方式
android.useDeprecatedNdk=true

5.2.3 修改build.gradle配置

工程中共有两个build.gradle配置文件,我们要修改的是在<Project>\app\build.gradle这个文件。为其在defaultConfig分支中增加上
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1.      sourceSets.main {
              jni.srcDirs = []
  2.           // 默认情况下,你需要把C/C++源代码放在[module]/src/main/jni/路径下
  3.           //自定义源代码路径
  4.           jni.srcDirs 'src/main/java/test/com/MyJni'
  5.           //设置你的.so文件的实际路径,用于调用System.loadLibrary( libName );
             //默认是再app/src/main/jniLibs中
  6.          jniLibs.srcDir "src/main/libs"
            }

  7. ndk {  
  8.     moduleName "JniTest"   //生成的so名字
  9.     ldLibs "log""z""m"  
  10.     abiFilters "armeabi""armeabi-v7a""x86"  //输出指定三种abi体系结构下的so库。目前可有可无。
  11. }  
    以上配置代码指定的so库名称为JniTest,链接时使用到的库,对应android.mk文件中的LOCAL_LDLIBS,及最终输出指定三种abi体系结构下的so库。
添加后如下图:

5.2.4 这时,再执行"Build->Rebuild Project",就可以编译出so文件了。

但在Window平台上会出现一个问题:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Error:Execution failed for task ':app:compileDebugNdk'.  
  2. > com.android.ide.common.internal.LoggedErrorException: Failed to run command:  
  3.  D:\Mission\adt-bundle-windows\ndk-r10b\ndk-build.cmd NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\Android.mk APP_PLATFORM=android-21 NDK_OUT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj NDK_LIBS_OUT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\lib APP_ABI=armeabi,armeabi-v7a,x86  
  4. Error Code:  
  5.  2  
  6. Output:  
  7.  make.exe: *** No rule to make target `C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj/local/armeabi/objs/JniTest/C_\Users\sodinochen\AndroidstudioProjects\JniTest2\app\src\main\jni', needed by `C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj/local/armeabi/objs/JniTest/C_\Users\sodinochen\AndroidstudioProjects\JniTest2\app\src\main\jni\main.o'. Stop.  
出现这个错误很莫名其妙..几番折腾下,找到一个视频出来了大概原因及解决方式:
出处见Youtube视频 02:50分开始: https://www.youtube.com/watch?v=okLKfxfbz40#t=362
在Windows下NDK一个bug,当仅仅编译一个文件时出现会出现此问题,解决方法就是 再往jni文件夹加入一个空util.c文件即可。

⑥编译生成libtestjni.so文件


android Studio不需要配置MK文件,是因为可以自动生成,生成的so文件路径如图所示:



第五:使用


5.1 使用预编译好的.so动态库


你可以先用ndk-build工具先编译好.so,然后在Android Studio中引用。 这种情况比较件简单,只要把的.so文件放到相应的目录即可。如下所示:

默认路径如图所示的jniLibs


现在就可以在代码中直接载入此库了,如下:

public class JniTest {

    //加载so    static {
        System.loadLibrary( "MyJniTest" );
    }

    //使用JNI的关键字native
    //这个关键字决定我们那些方法能在我们的C文件中使用
    //只须声明,不必实现
    public native void yu();
    public native double sum(double x,double y);
}


import test.com.myjni.JniTest;

public class MainActivity extends AppCompatActivity {

    TextView tv;

    JniTest jniUtils=new JniTest();


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

        tv=(TextView)findViewById(R.id.tv);

        double a=1;
        double b=2;
        double sum=jniUtils.sum(a,b);

        tv.setText(String.valueOf(sum));
    }
}

如果你不想把.so放在上面的默认路径,可以在buid.gradle中进行如下配置:

android {  
  // .. android settings ..
  sourceSets.main {
      jniLibs.srcDir 'src/main/myCppLibraries' // <-- 你的.so库的实际路径
      }
}


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值