NDK開發JNI動態註冊與靜態註冊

下面聯繫就是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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章