NDK 调用第三方库及系统库的分析

~为了模拟一个完整的第三方库调用方案,我们创建一个简单的带有ndk support的项目,实现一个简单的加法。然后单独调出这个项目生成的库文件并在另外的项目中执行调用。
本文分为4块,分别为:
1. 编写基础库 libndktest.so,实现一个加法(a+b)
2. 编写一个基本的android程序在 MainActivity 中调用 libndktest.so 并实现其中的加法
3. 对原有的外部库进行进一步封装,并实现其中的加法
4. 对系统库的调用

    首先贴出我的开发环境:
        adt-bundle-windows-x86_64-20140702 ->eclipse+sdk
        android-ndk-r10e
        jdk-1.6
    ndk的安装与配置我在这里就不在赘述了,网上资料很多。

1,编写基础库 libndktest.so,实现一个加法(a+b)。
~ 新建一个android项目ndktest,右键点击项目选择 Android Tools -> Add Native Support,这时候在项目根目录下就会多出来3个文件夹jni,obj,libs。
~jni下面放的是库的源码包含c,c++,Android.mk,Applications.mk等 ;
~obj下面存放着编译时生成的.o .a文件实际上并不会用到;
~libs下面存放的有 android-support-v4具体作用度娘,主要是编译过后生成的 armeabi 下会有我们需要用到的库文件libndktest.so。
这里写图片描述

~在 src/com.mrabbits.ndktest 右击New -> Class -> Name:FuncAdd;这时候在包下面就会生成一个新的Java文件 FuncAdd.java。并加入一个实现a+b的方法add();

package com.mrabbits.ndktest;
public class FuncAdd {
 static{
  System.loadLibrary("ndktest");
 }
  public static native int add(int a , int b);
}

~ 在jni/ndktest.cpp中加入如下代码:

#include <jni.h>
JNIEXPORT jint JNICALL Java_com_mrabbits_ndktest_FuncAdd_add
  (JNIEnv *, jclass, jint a , jint b ){
 return a+b;
}

~命令行界面进入项目根目录执行 javah -classpath bin/classes -d jni -jni com.mrabbits.ndktest.FuncAdd,将会在jni目录下生成com_mrabbits_ndktest_FuncAdd.h头文件,如果没有请刷新一下项目。然后把这个头文件包含进ndktest.cpp中。
这里写图片描述

#include <jni.h>
#include "com_mrabbits_ndktest_FuncAdd.h"
JNIEXPORT jint JNICALL Java_com_mrabbits_ndktest_FuncAdd_add
  (JNIEnv *, jclass, jint a , jint b ){
 return a+b;
}

~Android.mk中的内容不需要修改。想要详细了解Android.mk的内容请度娘。

~然后我们就可以在 MainActivity 中调用这个方法了。系统生成的 MainActivity 除了OnCreat()以外的两个方法没什么用,可以删掉。在OnCreat()中调用类FuncAdd中的方法add()。运行发现屏幕上跳出数字3,说明成功了。

package com.mrabbits.ndktest;
import com.mrabbits.ndktest.FuncAdd;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  int x =FuncAdd.add(1, 2);
  Toast.makeText(this, ""+x, Toast.LENGTH_LONG).show();
 }
}

2,编写一个基本的android程序在 MainActivity 中调用 libndktest.so 并实现其中的加法
~我们开始调用第一个工程ndktest里面生成的 libndktest.so 文件,并实现其中的add()方法。
如果我们只拿到一个libndktest.so文件能不能直接调用其中的方法呢?
答案肯定是:不能!
为什么呢?
请看第一个工程 ndktest 中用 javah 生成的 com_mrabbits_ndktest_FuncAdd.h

 JNIEXPORT jint JNICALL Java_com_mrabbits_ndktest_FuncAdd_add
  (JNIEnv *, jclass, jint, jint);

~这个头文件说明了这个接口的方法是在 com.mrabbits.ndktest.FuncAdd 中的 add() 中被调用的。

public static native int add(int a , int b);

~所以如果我们的 Android工程中没有 com.mrabbits.ndktest 这个包,这个包里没有 FuncAdd.java 这个类,这个类里面如果没有调用 native 方法 add();则这个接口拿过来是完全无效的。
那么问题来了,不是挖掘机技术哪家强 - -!
我们如果需要调用一个第三方接口需要哪些文件呢?
答案:
1、libxxx.so库文件,这是必须的。
2、源码用javah生成的头文件,如果没有头文件并且这个库是符合标准jni接口的库,则必须要要到一个接口说明文档。
3、你若是有源码,那不谈 - -。

~假设现在有库文件 libndktest.so 和头文件 com_mrabbits_ndktest_FuncAdd.h,我们的操作方法是:
先打开头文件看一看接口是如何调用的。

JNIEXPORT jint JNICALL Java_com_mrabbits_ndktest_FuncAdd_add
  (JNIEnv *, jclass, jint, jint);

~这个方法告诉我 libndktest.so是在 com.mrabbits.ndktest.FuncAdd 中的 add() 中被调用的。
~所以我们新建一个工程 ndktest2,在libs/下新建一个文件夹 armeabi 并把第一个工程 ndktest 中的libndktest.so文件拷贝到这个文件中。在src/下新建一个包 com.mrabbits.ndktest 在其中新建一个Class ->Name: FuncAdd->OK。
这里写图片描述

~在 FuncAdd.java 中静态载入 libndktest.so,并按头文件的方法声明 native 方法add()。

package com.mrabbits.ndktest;
public class FuncAdd {
 static{
  System.loadLibrary("ndktest");
 }
  public static native int add(int a , int b);
}

~最后我们在MainActivity中调用这个方法。

package com.attention.ndktest2;
import com.mrabbits.ndktest.FuncAdd;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  int x =FuncAdd.add(1, 2);
  Toast.makeText(this, ""+x, Toast.LENGTH_LONG).show();
 }
}

3,对原有的库进行进一步封装,并实现其中的加法。
~提到这个问题其实有点奇怪,为什么我们要去做这么麻烦的事情呢,有库文件,有头文件,看一看说明不就可以按照上面的方法直接调用了吗?
~其实我们还没有遇到另外一种情况,第二个工程 ndktest2 中调用的 libndktest.so 是模拟的对方在已知 java 方法的基础上专门为这个方法做的一个 native 方法,所以这个库才能在 java 中被直接调用。但如果仅仅是外部的一个方法并没有在 java 中声明过呢,也就是外部直接用 ndk-build 生成的库文件该怎么调用呢?
~由于这样生成的库文件并没有留给 java 接口,所以这样的库是不能被直接调用的。为了模拟这种情况,我们放弃上面的 libndktest.so 库文件,在外部生成一个库文件 libadd.so 。源码请下载,我这边就只贴出操作方法了。
~具体在下载的文件中的ndktest_addons中,用cmd在此目录下执行ndk-build(用ubuntu的朋友直接make就可以)。
这里写图片描述
此时在ndktest_addons/libs/armeabi 下就会生成一个libadd.so的库文件。这个库文件是不能被java直接调用的。

~下面我们开始调用 libadd.so 这个库,看一看源码很简单,仅仅是用c语言写的一个加法。我们新建一个工程 ndktest3 并实现这个加法。
跟第一个工程 ndktest 一样创建一个 Android工程并添加 native support 。可以仔细研究一下我的jni下面的目录,Android.mk的调用结构我就不多说了,这样分层的用意是在于把 libadd.so 作为一个SHRAED_LIBRARY给 ndktest3.cpp 调用,注意这里我对add.h做了一些处理,用意是支持c++编译。

#include <string.h>  
#include <jni.h>
#include <android/log.h>
#include <add.h> //包含 libndktest.so 的头文件
#define  JNIDEFINE(fname) Java_com_attention_ndktest3_MainActivity_##fname
extern "C" {
    JNIEXPORT jint JNICALL JNIDEFINE(NdkTestJni)(JNIEnv *env, jobject thiz);
};
/*
 * Class:     com_attention_ndktest3_MainActivity
 * Method:    NdkTestJni
 * Signature: ()Ljava/lang/Int;
 */
//这里我们做一个给 com.attention.ndktest3 下MainActivity.java中NdkTestJni()方法的接口 
JNIEXPORT jint JNICALL Java_com_attention_ndktest3_MainActivity_NdkTestJni
  (JNIEnv *env, jobject thiz)
{
    int ret = Add(9, 6); //这里我们就可以直接调用 libndktest.so 的方法了
    return ret;
}

~然后我们当然要在MainActivity.java中调用这个NdkTestJni()方法了。编译运行后屏幕上跳出15说明运行成功。

package com.attention.ndktest3;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends Activity {
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  int x =NdkTestJni();
  Toast.makeText(this, ""+x, Toast.LENGTH_LONG).show();
 }
    public native int  NdkTestJni ();

    static {
        System.loadLibrary("add");
        System.loadLibrary("ndktest");
    }
}

4,实现对系统库的调用
~实话说一开始我也困惑过如何调用系统库,网上有说用 LOCAL_LDLIBS := -llog -lz 向这样就可以调用liblog.so libz.so之类的,但是我试了好多次没有成功,于是我仔细研究了一下 System.loadLibrary() 这个方法发现在Runtime.loadLibrary()实现mLibPaths的初始化的过程中就有这么一段:

  String pathList = System.getProperty("java.library.path", ".");
  String pathSep = System.getProperty("path.separator", ":");
  String fileSep = System.getProperty("file.separator", "/"); 
  mLibPaths = pathList.split(pathSep);

实际上这和pathLIst就是来自System Property中的

 pathList = /vendor/lib:/system/lib

也就是说即使你不声明lib的位置 System 默认就会去 /vendor/lib /system/lib 这两个路径下面去找,也就是说 System.loadLibrary() 默认的就是去调用系统的库文件,我们没有必要去做多余的操作。

demo下载链接:
http://download.csdn.net/detail/m_rabbits/8865409

在Qt for Android中,调用第三方库可以通过以下步骤实现: 1. 将第三方库的文件(通常是一个文件或者是一个.so文件)添加到Qt项目的目录中,可以放在项目的根目录下或者是一个单独的文件夹中。 2. 在项目的.pro文件中添加文件的路径和依赖项。可以通过使用LIBS参数来指定文件路径,例如:LIBS += -L/path/to/library -lmylibrary。另外,如果文件有依赖文件,可以使用DEPLOYMENT参数指定依赖文件的路径,例如:DEPLOYMENT += path/to/dependency。 3. 在Qt代码中引用第三方库。使用#include指令将的头文件引入到Qt代码中,例如:#include <mylibrary.h>。然后就可以使用中提供的函数和类了。 需要注意的是,调用第三方库时,需要确保文件的兼容性和正确性。特别是在使用跨平台的时,需要检查是否支持Android平台,并且要确保所使用的文件与Qt版本和Android NDK版本兼容。 此外,Qt for Android还提供了JNI(Java Native Interface)的支持,可以通过JNI来调用Java代码和使用Java。这种方式可以更深入地与Android系统交互,并且能够访问更多的Android API和功能。 总而言之,Qt for Android调用第三方库需要将文件添加到项目中,并在项目配置文件中设置路径和依赖项。然后使用#include指令引入的头文件,并通过提供的函数和类来使用第三方库的功能。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值