Android 简单使用第三方提供的.so和.h

前言:

刚来公司,接了一个项目(具体项目不便透露),其中涉及到一个socket数据传输的问题,这里当然就不可避免的用到了.so动态库进行实现。接触过jni的应该都知道,Android调用.so库其实很简单,不就是直接static { System.loadLibrary(“aaron”); }吗?其实我救算这么想的,但是直到我拿到.so和.h文件之后才发现我错了。好了,废话不说了,进入正题:

实现步骤:

一、 分析你手上的.so文件(你可以看.h头文件,我的理解就是,这个.h文件其实就是c的一个接口类,c才是具体实现功能的):
	 如果.h文件中的方法名类似 Java_com_aaron_demo_JniUtil_getString ,那么恭喜你,你可以直接到最后一步(五、使用符合Java规范的.so库);
	 如果.h文件中的方法名类似 getString ,你还是老老实实的看第二步吧
二 、大家都知道,Android想要调用C,C暴露给我们的方法名就必须按照我们的规范来写,如第一步中给出的 Java_com_aaron_demo_JniUtil_getString ,AndroidStudio调用C方法的介绍(JNI),所以,我们如果拿到方法名不规范的C代码(或者.so文件),就需要把方法名变成规范的名称。那么问题来了,很多时候第三方就是不想让别人看到他的源代码,所以才打成.so库,我们没有C/C++的源代码,又怎么去修改他的方法名呢? 针对这个问题,答案是,我也没有办法去修改。
三、 我们写java的应该都知道“封装”,对,要改变这个方法名,我们其实只需要在这中间架一座桥梁,就是我们接下来需要介绍的 NDK对第三方.so库的二次封装
四、NDK对第三方.so库的二次封装:
1. 新建Android项目,用来生成我们要的.so库

新建一个Android Project ,命名随意,这里的包名还是需要注意一下,最好是用你实际项目中的包名,比如我公司的项目包名是 “com.xyw.aaron” ,那么你new Android Project的时候也改成这个,当然,不该也不会有什么影响(为什么?后面再说 1

2.新建一个类JniUtil

我们这里以 JniUtil 为例(类的名字没有规定,个人喜好),这个类的位置也是有讲究的(后面再说 2),声明几个方法,以public static native 开头(static非必要),具体方法看第三方的.h文件(test.h),我这里给一个简单的demo代码:

#pragma once

extern   int add(int a, int b); 
extern   int sub(int a, int b);

我建的JniUtil类代码(这个方法其实是我们要用的,也没有必要完全和test.h一样,我们只需要在自己的C里面调test.h的方法即可):

package com.xyw.aaron;

public class JniUtil {
    static {
        System.loadLibrary("aaron");
    }

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

    public static native int jianfa(int a, int b,String className);
}
3.编译JniUtil类

Android 简单使用第三方提供的.so和.h_第1张图片
如图所示,点击按钮即可,完成之后在build文件夹里面找到生成的JniUtil.class文件(低版本的Android studio在 build->intermediates->classes里面)
Android 简单使用第三方提供的.so和.h_第2张图片

4.生成自己的.h文件(com_xyw_aaron_JniUtil.h

右键复制其路径(Copy Path):
D:\as_space…\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes\com\xyw\aaron\JniUtil.class
在编辑器里面改成
D:\as_space…\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes com.xyw.aaron.JniUti
打开window或mac终端,cd到项目的main目录下面,执行下面的命令
javah -d jni -classpath D:\as_space…\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes com.xyw.aaron.JniUti
src/main/jni下面生成了 com_xyw_aaron_JniUtil.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_xyw_aaron_JniUtil */

#ifndef _Included_com_xyw_aaron_JniUtil
#define _Included_com_xyw_aaron_JniUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_xyw_aaron_JniUtil
 * Method:    jiafa
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_xyw_aaron_JniUtil_jiafa
  (JNIEnv *, jclass, jint, jint);

/*
 * Class:     com_xyw_aaron_JniUtil
 * Method:    chengfa
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_xyw_aaron_JniUtil_chengfa
  (JNIEnv *, jclass, jint, jint,jstring);

#ifdef __cplusplus
}
#endif
#endif
5.编写自己的C文件(aaron.c)
#include "jni.h"
#include "com_example_createsodemo_SoUtil.h"
#include "test.h"

JNIEXPORT jint JNICALL Java_com_example_createsodemo_SoUtil_jiafa(JNIEnv *env, jclass jz, jint a, jint b){
    return add(a,b);
}

JNIEXPORT jint JNICALL Java_com_example_createsodemo_SoUtil_chengfa(JNIEnv *env, jclass jz, jint a, jint b,jstring classname){
    return mul(a,b);
 }

这个文件其实就是实现com_example_createsodemo_SoUtil.h里面的两个方法jiafa()jianfa(),注意第三行代码,这里引入了第三方的test.h文件,因为我们的aaron.c会用到tesh.h里面暴露的方法add(a,b)mul(a,b)
记得把第三方的.so文件(libtest.so)复制到jni文件夹里面。


注意:这里的so文件必须是和你开发设备abi一致的,我设备是x86的


6.新建Android.mk和Application.mk文件

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := test						// 根据下面的.so文件来就行
LOCAL_SRC_FILES := libtest.so 				// jni下的第三方.so文件
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := aaron						//	你要生成的so文件的名称
LOCAL_SRC_FILES := aaron.c					//	你的.c文件
LOCAL_LDLIBS += -L$(SYSROOT)/lib -llog
LOCAL_LDLIBS := -llog -ljnigraphics
LOCAL_CFLAGS := -g

LOCAL_SHARED_LIBRARIES := test				// 第一个test
include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := x86
APP_PLATFORM := android-16
7.编译生成新的.so库

在终端,cd到Android.mk所在的目录,执行ndk-build命令,如果配置没有问题,那么,你将会在main目录下看到libsobj两个文件夹,我们要的东西就在libs里面,见图:
Android 简单使用第三方提供的.so和.h_第3张图片

五、使用符合Java规范的.so库:

打开你将要开发的项目,在main下面建一个文件夹,文件夹的名称为“com.xyw.aaron”,这个目录必须和我们新生成的.so文件中的Java_com_xyw_aaron_JniUtil 匹配,这里我在前面(1. 新建Android项目,用来生成我们要的.so库)就已经提到了,这就是为什么我们在二次封装的时候,文件夹名称一定要和实际项目的相同的原因。如果相同,那么你就不要再建这个文件夹了,好了不废话了。
将(2.新建一个类JniUtil)提到的JniUtil类复制到该文件夹里面(如果你要自己写也未尝不可,哈哈)。
ok,到这里你就可以随意的在项目中使用.so库里面的方法了(直接调JniUtil里面的方法即可!)

大功告成,希望能帮到大家,愿大家编写顺利,少遇坑!

有什么问题大家一起探讨!
留个邮箱:[email protected]

【转】JNI开发学习之C反射调用java方法

你可能感兴趣的:(Android,.h,.so)