做爲一個Android開發,或多或少都會接觸到JNI,有時候須要建立線程作一些特別的操做。java
使用 pthread 建立線程。android
#include <jni.h>
#include <android/log.h>
//添加頭文件
#include <pthread.h>
#define LOG_TAG "nativethread"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
static void *test(void *data) {
LOGI("test");
//必須加這行代碼,不然會直接崩潰
return nullptr;
}
void createThread() {
pthread_t thread;
/** * 四個參數: * 1. 指向線程標識符的指針 * 2. 設置線程屬性 * 3. 線程運行函數的起始地址 * 4. 運行函數的參數 */
int result = pthread_create(&thread, nullptr, test, nullptr);
if (result != 0) {
LOGE("線程啓動失敗");
}
}
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
LOGI("JNI load---------");
createThread();
return JNI_VERSION_1_6;
}
複製代碼
查看logcat,能夠發現兩條日誌的線程不同:c++
JNI調用java函數,需用用到java虛擬機環境,也就是JNIEnv指針。pthread_create建立的線程是一個c++中的線程,虛擬機並不能識別他們,爲了和java交互,須要把線程附着到java虛擬機上,而後就能夠得到當前線程的JNIEnv指針,由於JNIEnv指針只在當前線程中有效。markdown
具體代碼以下:函數
#include <jni.h>
#include <android/log.h>
#include <pthread.h>
#define LOG_TAG "nativethread"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
static JavaVM *gJavaVM;
static void printLog(JNIEnv *env, char* msg) {
//調用java方法打印日誌
jclass test = env->FindClass("com/francis/test/nativethread/Test");
jmethodID method = env->GetStaticMethodID(test, "printLog","(Ljava/lang/String;)V");
env->CallStaticVoidMethod(test, method, env->NewStringUTF(msg));
}
static void *test(void *data) {
LOGI("test");
int status;
JNIEnv *env;
bool isAttached = false;
status = gJavaVM->GetEnv((void **)(&env), JNI_VERSION_1_6);
if (status == JNI_EDETACHED) {
//將當前線程附着在java虛擬機上
status = gJavaVM->AttachCurrentThread(&env, nullptr);
if (status != JNI_OK) {
LOGE("Failed to attach current thread");
return nullptr;
}
isAttached = true;
}
printLog(env, "new Thread");
if(isAttached) {
//將當前線程從java虛擬機上分離
gJavaVM->DetachCurrentThread();
}
return nullptr;
}
void createThread() {
pthread_t thread;
int result = pthread_create(&thread, nullptr, test, nullptr);
if (result != 0) {
LOGE("線程啓動失敗");
}
}
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
LOGI("JNI load---------");
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
//保存全局變量
gJavaVM = vm;
printLog(env, "JNI_OnLoad");
createThread();
return JNI_VERSION_1_6;
}
複製代碼
java 代碼:ui
public class Test {
private static final String TAG = "Test";
public static void printLog(String msg) {
Log.d(TAG, "printLog: " + msg);
}
}
複製代碼
運行後發現程序崩潰了,找不到java的class。spa
java.lang.ClassNotFoundException: Didn't find class "com.francis.test.nativethread.Test" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
複製代碼
class也須要聲明成全局的變量。線程
#include <jni.h>
#include <android/log.h>
#include <pthread.h>
#define LOG_TAG "nativethread"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
static JavaVM *gJavaVM;
jclass testClass;
static void printLog(JNIEnv *env, char* msg) {
//調用java方法打印日誌
//jclass test = env->FindClass("com/francis/test/nativethread/Test");
jmethodID method = env->GetStaticMethodID(testClass, "printLog","(Ljava/lang/String;)V");
env->CallStaticVoidMethod(testClass, method, env->NewStringUTF(msg));
}
static void *test(void *data) {
LOGI("test");
int status;
JNIEnv *env;
bool isAttached = false;
status = gJavaVM->GetEnv((void **)(&env), JNI_VERSION_1_6);
if (status == JNI_EDETACHED) {
//將當前線程附着在java虛擬機上
status = gJavaVM->AttachCurrentThread(&env, nullptr);
if (status != JNI_OK) {
LOGE("Failed to attach current thread");
return nullptr;
}
isAttached = true;
}
printLog(env, "new Thread");
if(isAttached) {
//將當前線程從java虛擬機上分離
gJavaVM->DetachCurrentThread();
}
return nullptr;
}
void createThread() {
pthread_t thread;
int result = pthread_create(&thread, nullptr, test, nullptr);
if (result != 0) {
LOGE("線程啓動失敗");
}
}
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
LOGI("JNI load---------");
JNIEnv *env;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
//保存全局變量
gJavaVM = vm;
//建立全局引用
jclass test = env->FindClass("com/francis/test/nativethread/Test");
testClass = (jclass)(env->NewGlobalRef(test));
env->DeleteLocalRef(test);
printLog(env, "JNI_OnLoad");
createThread();
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) {
JNIEnv *env;
if (vm->GetEnv((void **)(&env), JNI_VERSION_1_6) != JNI_OK) {
return;
}
//刪除全局變量
if (testClass != nullptr) {
env->DeleteGlobalRef(testClass);
testClass = nullptr;
}
}
複製代碼
運行後日志以下:指針