下面联系就是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