NDK开发JNI动态注册与静态注册

21 篇文章 0 订阅
3 篇文章 0 订阅

下面联系就是ndk开发联系,为了熟悉java和C/C++交互。

首先Native方法的注册方式有两种:
静态声明,即隐式注册
函数格式要求:Java_包名_类名_方法名
动态注册,即显式注册
JNI_OnLoad函数注册

然后我使用的是动态注册,然后遇到的坑是初学ndk开发,忘记把自己的函数添加进数组里,就是下面的gMethods[] ,然后一直报错No implementation found for native,然后由于不熟悉,在网上搜错误,发现别人用的都是隐式注册,然后排插包名之类有没有一样,对我来说没用,看了半天,还有就是脑子抽了,把加载库的,System.loadLibrary和JNI_OnLoad给混了,写了个native函数,然后System.loadLibrary取加载函数,一直报错找不到“函数.so”,还去查找了半天为什么找不到这个动态库Orz,坑了自己。

static JNINativeMethod gMethods[] = {
    {"testC", "()Ljava/lang/String;", (void*)testC},//这里不用写包名和类名了,因为上面已经通过findclass找到{}
    {"doTest", "()V", (void*)doTest}
};

下面贴关键代码片段

 这个是java层的

package com.example.applicationandjni;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {


	static{
		System.loadLibrary("test_c");
		System.loadLibrary("test_cpp");
	}
	
	public static native String testStaticC();
	public static native String testStaticCPP();
	public native String testC();
	public native String testCPP();
	
	private void logWrapper(String tag, String msg){
		Log.i(tag, msg);
	}
	public native void doTest();
//	private void doTest(){
//		logWrapper("MF", testStaticC());
//		logWrapper("MF", testStaticCPP());
//		logWrapper("MF", testC());
//		logWrapper("MF", testCPP());
//	}
	private int doCompute(int a, int b){
		return 0;
	}
	
	private Button btn;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Log.i("MF","Mainactivity onCreate Call");
		btn = (Button)findViewById(R.id.main_btn);
		btn.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				testC();
			}
		});

		doTest();
		doCompute(3, 5);
	}
}

下面是native层C代码的,C++类似就不贴了,注意点就是一些细节,比如c++要extern “C”,还有一些传参有别与C

#include <string.h>
#include <jni.h>

JNIEXPORT jstring JNICALL
Java_com_example_applicationandjni_MainActivity_testStaticC( JNIEnv* env,
                                                  jclass thiz )
{
    return (*env)->NewStringUTF(env, "Hello from static Method C");
}

static void doTest
        ( JNIEnv* env,
          jobject* thiz );

static jstring testC(JNIEnv* env){
	return (*env)->NewStringUTF(env, "Hello from Method C");
};

#define JNIREG_CLASS "com/example/applicationandjni/MainActivity"

static JNINativeMethod gMethods[] = {
    {"testC", "()Ljava/lang/String;", (void*)testC},//这里不用写包名和类名了,因为上面已经通过findclass找到{}
    {"doTest", "()V", (void*)doTest}
};

/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods)
{
	jclass clazz;
	clazz = (*env)->FindClass(env, className);//要确定想哪个类注册所以先去找class
	if (clazz == NULL) {
		return JNI_FALSE;
	}
	if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {//后面两个参数是数组和个数,因为不止一个方法
		return JNI_FALSE;
	}

	return JNI_TRUE;
}

static int registerNatives(JNIEnv* env)
{
	if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
                                 sizeof(gMethods) / sizeof(gMethods[0])))
		return JNI_FALSE;

	return JNI_TRUE;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
	JNIEnv* env = NULL;
	jint result = -1;

	if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
		return -1;
	}
	if(env == NULL)
		return -1;

	if (!registerNatives(env)) {
		return -1;
	}
	result = JNI_VERSION_1_4;

	return result;
}

JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved){
	JNIEnv* env = NULL;

	if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
		return;
	}
	(*env) -> UnregisterNatives(env, JNIREG_CLASS);
}


static void doTest
( JNIEnv* env,
jobject* thiz )
{
	//logWrapper("MF", testStaticC());
	//拆解起来就是先调testStaticC,然后MF,然后外层
	//剩下几个需要函数一样,调那些函数,要先findClass
	jclass m_class=(*env)->FindClass(env,JNIREG_CLASS);
	//先申请一个java的字符串
	jstring m_str=(*env)->NewStringUTF(env, "MF");
	//然后findMethod id
	//private void logWrapper(String tag, String msg);
	//static jstring testC(JNIEnv* env)
	//JNIEXPORT jstring JNICALL  Java_com_example_applicationandjni_MainActivity_testStaticC( JNIEnv* env,jclass thiz )
	//static jstring testCPP(JNIEnv* env, jobject thiz)
	//extern "C" JNIEXPORT jstring JNICALL Java_com_example_applicationandjni_MainActivity_testStaticCPP( JNIEnv* env,jclass thiz )
	jmethodID m_logWrapper=(*env)->GetMethodID(env,m_class,"logWrapper","(Ljava/lang/String;Ljava/lang/String;)V");
	jmethodID m_testC=(*env)->GetMethodID(env,m_class,"testC","()Ljava/lang/String;");
	jmethodID m_testStaticC=(*env)->GetStaticMethodID(env,m_class,"testStaticC","()Ljava/lang/String;");
	jmethodID m_testCPP=(*env)->GetMethodID(env,m_class,"testCPP","()Ljava/lang/String;");
	jmethodID m_testStaticCPP=(*env)->GetStaticMethodID(env,m_class,"testStaticCPP","()Ljava/lang/String;");

	//下面开始call函数
	//logWrapper("MF", testStaticC());,先call里层testStaticC,因为返回值jString,所以用CallObjectMethod,外层void,用CallVoidMethod
	jstring str_ret=(*env)->CallStaticObjectMethod(env,m_class,m_testStaticC);//这里原函数第二个参数是对象所以这里是对象
	(*env)->CallVoidMethod(env,thiz,m_logWrapper,m_str,str_ret);

	//logWrapper("MF", testStaticCPP());
	str_ret=(*env)->CallStaticObjectMethod(env,m_class,m_testStaticCPP);
	(*env)->CallVoidMethod(env,thiz,m_logWrapper,m_str,str_ret);

	//logWrapper("MF", testC());
	str_ret=(*env)->CallObjectMethod(env,thiz,m_testC);
	(*env)->CallVoidMethod(env,thiz,m_logWrapper,m_str,str_ret);
	//logWrapper("MF", testCPP());
	str_ret=(*env)->CallObjectMethod(env,thiz,m_testCPP);
	(*env)->CallVoidMethod(env,thiz,m_logWrapper,m_str,str_ret);
}

然后log日志如下

2019-06-18 03:27:47.733 25600-25600/com.example.applicationandjni I/MF: Application attachBaseContext Call
2019-06-18 03:27:47.736 25600-25600/com.example.applicationandjni I/InstantRun: starting instant run server: is main process
2019-06-18 03:27:47.743 25600-25600/com.example.applicationandjni I/MF: Application onCreate Call
2019-06-18 03:27:48.087 25600-25600/com.example.applicationandjni D/OpenGLRenderer: Skia GL Pipeline
2019-06-18 03:27:48.118 25600-25600/com.example.applicationandjni I/MF: Mainactivity onCreate Call
2019-06-18 03:27:48.119 25600-25600/com.example.applicationandjni I/MF: Hello from static Method C
2019-06-18 03:27:48.119 25600-25600/com.example.applicationandjni I/MF: Hello from static Method CPP
2019-06-18 03:27:48.119 25600-25600/com.example.applicationandjni I/MF: Hello from Method C
2019-06-18 03:27:48.119 25600-25600/com.example.applicationandjni I/MF: Hello from Method CPP
2019-06-18 03:27:48.203 25600-25657/com.example.applicationandjni I/Adreno: QUALCOMM build                   : 984b9a6, Ibe1bf21abc
    Build Date                       : 06/04/18
    OpenGL ES Shader Compiler Version: EV031.24.00.00
    Local Branch                     : googldrp
    Remote Branch                    : 
    Remote Branch                    : 
    Reconstruct Branch               : 
2019-06-18 03:27:48.204 25600-25657/com.example.applicationandjni I/Adreno: Build Config                     : S L 4.0.10 AArch64
2019-06-18 03:27:48.207 25600-25657/com.example.applicationandjni I/Adreno: PFP: 0x005ff110, ME: 0x005ff066
2019-06-18 03:27:48.214 25600-25657/com.example.applicationandjni I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 0
2019-06-18 03:27:48.214 25600-25657/com.example.applicationandjni I/ConfigStore: android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasHDRDisplay retrieved: 0
2019-06-18 03:27:48.215 25600-25657/com.example.applicationandjni I/OpenGLRenderer: Initialized EGL, version 1.4
2019-06-18 03:27:48.215 25600-25657/com.example.applicationandjni D/OpenGLRenderer: Swap behavior 1

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值