在jni开发默认的情况下,用的就是静态注册,静态注册是最简单的方式,在NDK开发过程中,基本上使用静态注册,静态注册比动态注册要简单,但是在诸多的系统源码中,会发现会有大量都是采用动态注册,因为动态注册虽然麻烦,但是比静态注册安全性高,不用暴露包名类名等。
静态注册的优点:
(1)开发简单;
(2)使用方便;
(3)便于阅读。
缺点:
(1)、JNI的函数名过长;
(2)、捆绑上层的包名和类名;
(3)、运行期才会去匹配JNI函数,性能上低于动态注册。
例子:
1、MainActivity
public native String stringFromJNI(); // 默认生成的,其实也是静态注册方式
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG, "onCreate stringFromJNI:" + stringFromJNI());
}
2、native-lib.cpp
#include #define TAG "JNISTUDY" // __VA_ARGS__ 代表 ...的可变参数 #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__); #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__); #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__); #include二、动态注册#include #include // 在AS上 pthread不需要额外配置,默认就有 extern "C" JNIEXPORT jstring JNICALL Java_com_derry_as_1jni_1project_MainActivity_stringFromJNI( JNIEnv *env, jobject ) { std::string hello = "默认就是静态注册哦"; return env->NewStringUTF(hello.c_str()); }
在系统源码中,基本上都会采用动态注册,动态注册比静态注册麻烦,但是效率高,在被编译后安全性更高,并且在native中调用,函数名简洁,便于用户阅读。
1、MainActivity.java
public native void dynamicJavaMethod01(); // 动态注册1
public native int dynamicJavaMethod02(String valueStr); // 动态注册2
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void dynamic01(View view) {
dynamicJavaMethod01();
}
public void dynamic02(View view) {
dynamicJavaMethod02("你好,JNI!");
}
2、native-lib.cpp
JavaVM *jVm = nullptr;//定义全局的javavm变量
const char *mainActivityClassName = "com/myapplication/MainActivity";//指定java的类名
//无参类型动态注册函数
void dynamicMethod01(){
LOGD("第一个动态注册的函数!")
}
//有参类型动态注册函数
int dynamicMethod02(JNIEnv *env,jobject mainthis,jstring jstr){
const char *text = env->GetStringUTFChars(jstr, nullptr);
LOGD("得到了java层的值:%s",text)
env->ReleaseStringUTFChars(jstr,text);
return 200;
}
static const JNINativeMethod jniNativeMethod[] = {
{"dynamicJavaMethod01","()V",(void *)(dynamicMethod01)},
{"dynamicJavaMethod02","(Ljava/lang/String;)I",(int *)dynamicMethod02}
};
extern "C"
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *unused) {//必须重写默认的构造函数
::jVm = vm;
JNIEnv *jniEnv = nullptr;
int result = vm->GetEnv(reinterpret_cast(&jniEnv), JNI_VERSION_1_6);
//result等于0就是成功
if (result != JNI_OK){
return -1;
}
//找到java层的类型
jclass mainActivityClass = jniEnv->FindClass(mainActivityClassName);
//进行动态注册
jniEnv->RegisterNatives(mainActivityClass,jniNativeMethod,sizeof(jniNativeMethod)/sizeof(JNINativeMethod));
return JNI_VERSION_1_6;
}
三、JNI子线程的使用
1、MainActivity.java
public native void naitveThread(); // Java层 调用 Native层 的函数,完成JNI线程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void nativeCallJava(View view) {
naitveThread();
}
public void updateActivityUI() {
Log.d("java", "恭喜您,开启子线程成功!");
runOnUiThread(new Runnable() { // 哪怕是异步线程 UI操作 正常下去 runOn
@Override
public void run() {
// 可以在子线程里面 操作UI
new AlertDialog.Builder(MainActivity.this)
.setTitle("子线程")
.setMessage("我是子线程")
.setPositiveButton("确定", null)
.show();
}
});
}
2、native-lib.cpp
//由于不能跨线程,所以必须定义一个全局的JNIEnv和jobject
class MyContext{
public:
JNIEnv *jniEnv = nullptr;
jobject object = nullptr;
};
//执行异步线程调用java层的方法
void *myThreadTashAction(void *pVoid){
LOGE("myThreadTashAction");
MyContext * myContext = static_cast(pVoid);
//安卓进程中只有一个JavaVM,是全局的,可以跨线程
JNIEnv * jniEnv = nullptr;//全新的jnieNV异步线程里面操作
jint attachResult = ::jVm->AttachCurrentThread(&jniEnv, nullptr);//绑定当前线程
if (attachResult != JNI_OK){
return 0;
}
//调用java层的函数
//1、拿到class
jclass mainActivityClass = jniEnv->GetObjectClass(myContext->object);
//2、拿到方法
jmethodID updateActivityUI = jniEnv->GetMethodID(mainActivityClass,"updateActivityUI","()V");
//3、调用
jniEnv->CallVoidMethod(myContext->object,updateActivityUI);
::jVm->DetachCurrentThread();//必须解除绑定当前线程,否则报错
return nullptr;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_myapplication_MainActivity_naitveThread(JNIEnv *env, jobject thiz) {
MyContext * myContext = new MyContext;
myContext->jniEnv = env;
myContext->object = env->NewGlobalRef(thiz);//提升全局引用
//开启子线程
pthread_t pid;
pthread_create(&pid, nullptr,myThreadTashAction,myContext);
pthread_join(pid, nullptr);
}
3、jni线程总结:
(1)jni的线程与java的一样,默认是在主线程执行的,子线程需要另外开启;
(2)JavaVM对整个jni层来说是全局的,可以跨程线,也就是在任何地方都可以绑定当前线程,并且只有一个地址;
(3)JNIEnv是局步变量,不能跨线程,主线程与子线程绑定地址不一样;
(4)jobject不能跨线程,java层中谁调用了JNI函数,谁的实例就会给jobject;



