android开发之jni调用(java与c++相互调用)

1.写java jni调用类文件

package com.example.android.toyvpn;

public class JniSocket {
	JniSocket(ToyVpnService toyVpnService)
	{
		vpnService = toyVpnService;
	}
	static 
	{
		System.loadLibrary("jnisocket");
	}
	ToyVpnService vpnService;
	public native int connectServer(String ip, int port);
	public boolean protectFd(int fd)
	{
		return vpnService.protect(fd);
	}
	
	public void print(int fd)
	{
		System.out.println("fd " + fd);
	}
	
	public boolean printBoolean(int fd)
	{
		System.out.println("fd " + fd);
		return true;
	}
}

2.生成本地方法头文件

2.1利用ide工具(如eclipse)生成.class文件

2.2 命令行进入工程目录,输入以下命令即可:

Javah -classpath bin\classes -d jni com.example.android.toyvpn.JniSocket

(ps: -classpath bin\classes 指定 jni调用类.class文件的路径  -d jni 指定本地方法头文件生成的目录,

如果楚翔:

错误: 找不到类android.net.VpnService。

可以换成在src目录输入如下命令:

Javah -encoding UTF-8 -d ../jni com.example.android.toyvpn.JniSocket

加入-encoding UTF-8是因为我运行这条命令是有如下错误:

错误: 编码GBK的不可映射字符

生成的头文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_android_toyvpn_JniSocket */

#ifndef _Included_com_example_android_toyvpn_JniSocket
#define _Included_com_example_android_toyvpn_JniSocket
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_android_toyvpn_JniSocket
 * Method:    connectServer
 * Signature: (Ljava/lang/String;I)I
 */
JNIEXPORT jint JNICALL Java_com_example_android_toyvpn_JniSocket_connectServer
  (JNIEnv *, jobject, jstring, jint);

  int getCurrentENVInfo(JNIEnv *env);

#ifdef __cplusplus
}
#endif
#endif

其中 “int getCurrentENVInfo(JNIEnv *env);”这一行为后面添加的方法声明,其余的均为javah自动生成,

(JNIEnv *, jobject, jstring, jint);中JNIEnv* 指向虚拟机环境, jobject为调用此方法的java对象(当在本地方法中回调java非静态方法的时候会用到),后面两个为java方法所带的参数



3.实现本地方法

(jni 数据类型及基本方法可参考我转发的文章:JNI基本数据类型

#include "com_example_android_toyvpn_JniSocket.h"
#include "javamethodctrl.h"
#include "javamacro.h"
#include <assert.h>
#include <fcntl.h>
#include<errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <android/log.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <netdb.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <linux/rtc.h>
#include <setjmp.h>
#include <net/if.h>
#include <net/route.h>
#include <sys/types.h>
#include <sys/stat.h>

JavaVM *gs_jvm = NULL;
jobject g_LivingProxyNotify = NULL;
const int Ret_Success = 0;
const int Ret_Failed = 1;
//static jclass JniSocket;

//JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) {  
//    jclass tmp = env->FindClass("com/example/company/MyClass");  
//    myClass = (jclass)env->NewGlobalRef(tmp);
//    return JNI_VERSION_1_6;  
//}

static jclass JniSocket;
static jclass tmp;
static jclass print;
static jclass printBoolean;

JNIEXPORT jint JNICALL Java_com_example_android_toyvpn_JniSocket_connectServer
  (JNIEnv * env, jobject obj, jstring ip, jint port)
  {
        int tmpSocket = -1;
        do
        {
            if(0 != getCurrentENVInfo(env))
            {
                return -5;
            }

            const char * servIP = NULL;
            servIP = env->GetStringUTFChars(ip, 0);      

             //对IP和端口进行转换
            struct sockaddr_in stRemote;
            struct sockaddr_in stLocal;
            memset(&stRemote, 0x00, sizeof(sockaddr_in));
            memset(&stLocal,  0x00, sizeof(sockaddr_in));

            stRemote.sin_family = AF_INET;
            stLocal.sin_family  = AF_INET;
            stRemote.sin_port = htons(port);
            stLocal.sin_addr.s_addr = htonl(INADDR_ANY);
            stLocal.sin_port = htons(22389);

            if(inet_pton(AF_INET, servIP, &stRemote.sin_addr) <= 0)
            {
                return -6;
            }
            
            tmpSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
            if(tmpSocket < 0)
            {
                return -8;
            }

            //绑定指定IP和端口
            if(bind(tmpSocket, (sockaddr*)&stLocal, sizeof(sockaddr)) < 0)
            {
                return -9;
            }

//            print = env->FindClass("com/example/android/toyvpn/JniSocket");
//            if(NULL == print)
//            {
                Koolsee_LOGDebug("FindClass com/example/android/toyvpn/JniSocket");
//                return -11;
//            }
//            jmethodID jmethodIDprint = NULL;
//            jmethodIDprint = env->GetMethodID(print, "print", "(I)V");
//            if (NULL == jmethodIDprint)
//            {
                Koolsee_LOGDebug("get jmethodIDreceiveNotify failed!");
//                return -12;
//            }
            return -12;
//            env->CallVoidMethod(obj, jmethodIDprint, tmpSocket);

             JniSocket = env->FindClass("com/example/android/toyvpn/JniSocket");
//             JniSocket = (jclass)(env->NewGlobalRef(tmp));
            if(NULL == JniSocket)
            {
//                Koolsee_LOGDebug("FindClass com/example/android/toyvpn/JniSocket");
                return -11;
            }

            jmethodID jmethodIDNewInstance = NULL;
            <pre name="code" class="cpp">           //第二个参数为方法名,第三个参数括号内为参数类型(可以有多个),括号外为返回值类型
  jmethodIDNewInstance = env->GetMethodID(JniSocket, "protectFd", "(I)Z"); if (NULL == jmethodIDNewInstance) { return -12; } bool flag = env->CallBooleanMethod(obj, jmethodIDNewInstance, tmpSocket); if(!flag) { return -13; } //设置非阻塞模式 //连接服务器 // fcntl(tmpSocket, F_SETFL, fcntl(tmpSocket, F_GETFL, 0) | O_NONBLOCK); // errno = 0; if(0 != connect(tmpSocket, (sockaddr*)&stRemote, sizeof(stRemote))) { return -20; } }while(0); return 0; }int getCurrentENVInfo(JNIEnv *env){if(NULL != env){env->GetJavaVM(&gs_jvm);}if(NULL != gs_jvm){return Ret_Success;} return Ret_Failed;}
 

上面类似于 static jclass JniSocket; 这样的声明可以视情况而定。

4.编译成libjnisocket.so库

目录结构为:

jni 目录下有如下文件和目录:

android.mk, Application.mk, socket(为目录)

jni/android.mk:

LOCAL_PATH:= $(call my-dir)
include $(call all-subdir-makefiles)

jni/Application.mk:

# The ARMv7 is significanly faster due to the use of the hardware FPU
APP_ABI := armeabi-v7a armeabi mips x86 
#APP_ABI := x86
#APP_PLATFORM := android-8

APP_ABI:希望编译低平台架构类型,一般选armeabi-v7a就ok了

socket目录下有如下文件和目录:

android.mk,socket_source(目录)

jni/socket/socket_source目录下存放所有源文件(*.cpp, *.h)

jni/socket/android.mk:

include $(CLEAR_VARS)
APP_STL := stlport_static
LOCAL_MODULE    := jnisocket
LOCAL_CFLAGS := -Wall -pipe -D_POSIX_SOURCE -D_REENTRANT -D_THREAD_SAFE -D_GNU_SOURCE -DWORDS_LITTLEENDIAN -DNO_FTW_H
LOCAL_CFLAGS += -funwind-tables -DANDROID -DARMANDROID -DTIXML_USE_STL
LOCAL_CFLAGS += --sysroot=$(ANDROID_NDK_ROOT)/platforms/android-5/arch-arm

LOCAL_CPPFLAGS += -fno-rtti -DTIXML_USE_STL

LOCAL_C_INCLUDES := $(ANDROID_NDK_ROOT)/sources/cxx-stl/stlport/stlport 

ifeq ($(TARGET_ARCH_ABI),x86)
    LOCAL_C_INCLUDES += $(ANDROID_NDK_ROOT)/platforms/android-9/arch-x86/usr/include/
    LOCAL_LDFLAGS := --sysroot=$(ANDROID_NDK_ROOT)/platforms/android-9/arch-x86
    LOCAL_LDLIBS    := -L./socket/lib/x86
else ifeq ($(TARGET_ARCH_ABI),mips)
    LOCAL_C_INCLUDES += $(ANDROID_NDK_ROOT)/platforms/android-9/arch-mips/usr/include/
    LOCAL_LDFLAGS := --sysroot=$(ANDROID_NDK_ROOT)/platforms/android-9/arch-mips
    LOCAL_LDLIBS    := -L./socket/lib/mips
else
    LOCAL_C_INCLUDES += $(ANDROID_NDK_ROOT)/platforms/android-9/arch-arm/usr/include/
    LOCAL_LDFLAGS += -Wl,--fix-cortex-a8
    LOCAL_LDFLAGS := --sysroot=$(ANDROID_NDK_ROOT)/platforms/android-9/arch-arm
    LOCAL_LDLIBS    := -L./socket/lib/armeabi
endif

LOCAL_SRC_FILES += socket/socket_source/com_example_android_toyvpn_JniSocket.cpp \
			 socket/socket_source/javamethodctrl.cpp

#LOCAL_STATIC_LIBRARIES := otherCommonAPI

LOCAL_LDLIBS    += -L$(ANDROID_NDK_ROOT)/sources/cxx-stl/stlport/libs/$(TARGET_ARCH_ABI) \
                   -lstlport_static -llog

include $(BUILD_SHARED_LIBRARY)

5.然后在jni目录下输入 ndk-build 即可在jni同一级目录libs(自动生成的目录)中生成所需要的.so文件

6.在android工程目录下新建文件夹libs\armeabi-v7a 在此文件夹放入相应的动态库文件(libjnisocket.so)即可


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值