Ubuntu下android studio如何使用ndk-build的so库

之前有写过一篇如何导入so库的文章,那个studio0.8的时候用的,并且那个是针对百度地图so,友盟so之类的开发工具用的。如果我们自己生成了so库,那么项目如何使用so库呢?

上一篇我讲了如何在ubuntu下利用ndk,build了ndk提供的测试项目hello-jni,这里将使用这个libhello-jni.so

sudo sh studio.sh

打开studio,新建项目。
这里新建项目是有讲究的,一开始不清楚的情况下,我试过按照自己的喜好命名,发现是出现了这样的错误:

Trying to load lib /data/data/factorytest.android.com/lib/libhello-jni.so 0x42117dc0
Added shared lib /data/data/factorytest.android.com/lib/libhello-jni.so 0x42117dc0
No JNI_OnLoad found in /data/data/factorytest.android.com/lib/libhello-jni.so 0x42117dc0, skipping init
No implementation found for native Lcom/example/hellojni/hellotest;.stringFromJNI:()Ljava/lang/String;
D/AndroidRuntime(16064): Shutting down VM

这说明了so库没找到,这也是和我之前介绍导入百度地图so之类的so库的不同点之一。可能百度地图的demo里面应该做好了这些准备,不需要用户自己去设置了。

库没找到,看似和JNI_Onload()有关。这么说是有原因的,现在先不说命名问题,先说一下在一个项目中,如何导入so

首先,打开app的gradle,加入:

    sourceSets {
        main{
            jniLibs.srcDirs = ['libs']
        }
    }

完整代码:
Ubuntu下android studio如何使用ndk-build的so库_第1张图片

好了,做好这一步,sync now。
然后会发现项目中出现jniLibs文件夹
Ubuntu下android studio如何使用ndk-build的so库_第2张图片
然后在jniLibs下新建armeabi,把so库复制粘贴进去。

使用so库的时候,在使用的那个类文件里面写入代码

    public native String stringFromJNI();
    static {
        System.loadLibrary("hello-jni");
    }

为什么是stringFromJNI()而不是其他方法名呢,我们看看这个C文件的源码

#include 
#include 

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
#if defined(__arm__)
  #if defined(__ARM_ARCH_7A__)
    #if defined(__ARM_NEON__)
      #if defined(__ARM_PCS_VFP)
        #define ABI "armeabi-v7a/NEON (hard-float)"
      #else
        #define ABI "armeabi-v7a/NEON"
      #endif
    #else
      #if defined(__ARM_PCS_VFP)
        #define ABI "armeabi-v7a (hard-float)"
      #else
        #define ABI "armeabi-v7a"
      #endif
    #endif
  #else
   #define ABI "armeabi"
  #endif
#elif defined(__i386__)
   #define ABI "x86"
#elif defined(__x86_64__)
   #define ABI "x86_64"
#elif defined(__mips64)  /* mips64el-* toolchain defines __mips__ too */
   #define ABI "mips64"
#elif defined(__mips__)
   #define ABI "mips"
#elif defined(__aarch64__)
   #define ABI "arm64-v8a"
#else
   #define ABI "unknown"
#endif

    return (*env)->NewStringUTF(env, "Hello from JNI !  Compiled with ABI " ABI ".");
}

对于传统的JNI编程来说,JNI方法跟Java类方法的名称之间有一定的对应关系,要遵循一定的命名规则,如下:

1) 前缀: Java_
2) 类的全限定名,用下划线进行分隔(_):com_example__hellojni_HelloJni
(包_ 类 _方法)
3) 方法名:stringFromJNI
3) jni函数指定第一个参数: JNIEnv *
4) jni函数指定第二个参数: jobject
5) 实际Java参数: jstring, jint ….
6) 返回值的参数 : jstring, jint…. 所以对于在Java类

其对应的jni层的方法如下:

Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )

如果不这样命名,当把动态库加载进DVM的时候,通过JNIEnv *指针去查找Java Native方法对应的JNI方法的时候,就会找不到了。
注意,我们也可以利用函数注册的方法,将Java层的方法名跟JNI层的方法名的对应关系保存起来,注册到DVM中,就不需要这样的命名规范了。

如果方法要带有参数比如一个string参数,那么可以这样写

Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,jobject thiz , jstring str);

好了,如果就这样运行的话,很有可能就出现刚刚提到的错误

No JNI_OnLoad found in /data/data/
解决方法:还是和命名有关,也就是java的代码名和C的不匹配
studio默认情况下会生成MyApplication为包名的项目,这样就和刚刚那个C冲突,我们只要:

package com.example.hellojni;
public class HelloJni extends Activity

全部改成和C一样的名字就行了。
当然了,我们也可以直接改C,改成MyApplication的也行。
最后,运行结果:
Ubuntu下android studio如何使用ndk-build的so库_第3张图片

Hello From JNI !

以后会有专门介绍如何写C生成so库,以及需要哪些mk文件的文章

你可能感兴趣的:(Android,studio的使用,ubuntu,下的android和服务器)