20.Eclipse下Ndk開發(pthread開啟執行緒呼叫Java方法)
本專案最終的目的是在pthread執行緒中,呼叫Java一個工具類得到多個uuid,然後呼叫類中另一個方法彈出toast,實現在c中獲取安卓上下文物件Context
編譯native方法,生成標頭檔案的一系列過程不再贅述,直接上程式碼,都在註釋中
PosixUtils:
package com.example.ndk_pthread;
public class PosixUtils {
static{
System.loadLibrary("ndk_pthread");
}
/**
* pthread開啟子執行緒前的一些初始化操作,比如獲取本類的jclass物件,生成需要的
* 全域性引用等等,在子執行緒中無法獲取到類的jclass物件,就是這行程式碼,獲取class必須要在主執行緒中
* jclass uuidutils_class_tmp = (*env)->FindClass(env,"com/example/ndk_pthread/UUIDUtils");
*/
public native void init();
/**
* 執行一些善後的操作,比如init方法中生成的全域性引用的銷燬等等
*/
public native void destroy();
/**
* 子執行緒操作
*/
public native void pthread();
}
UUIDUtils:
package com.example.ndk_pthread;
import java.util.UUID;
import android.content.Context;
import android.widget.Toast;
public class UUIDUtils {
public static String get(){
return UUID.randomUUID().toString();
}
public static void showToast(Context context){
Toast.makeText(context, "c端彈出了toast", 0).show();
}
}
編譯生成的標頭檔案com_example_ndk_pthread_UUIDUtils.h:
package com.example.ndk_pthread;
import java.util.UUID;
import android.content.Context;
import android.widget.Toast;
public class UUIDUtils {
public static String get(){
return UUID.randomUUID().toString();
}
public static void showToast(Context context){
Toast.makeText(context, "c端彈出了toast", 0).show();
}
}
MainActivity:
package com.example.ndk_pthread;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MainActivity extends Activity {
private PosixUtils p;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
p = new PosixUtils();
p.init();
}
public void start(View btn){
p.pthread();
}
@Override
protected void onDestroy() {
super.onDestroy();
//頁面銷燬時同時銷燬c端的一些東西
p.destroy();
}
}
ndk_pthread.c
#include "com_example_ndk_pthread_PosixUtils.h"
#include <stdio.h>
#include <pthread.h>
#include <android/log.h>
#include <unistd.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"renzhenming",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"renzhenming",FORMAT,##__VA_ARGS__);
JavaVM *javaVM;
jobject uuidutils_jcls;
jmethodID get_mid;
jmethodID toastId;
jobject jcontext;
//動態庫載入時會被呼叫執行,不需要我們手動呼叫
//相容Android SDK 2.2之後,2.2沒有這個函式
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
LOGI("%s","JNI_OnLoad");
javaVM = vm;
return JNI_VERSION_1_4;
}
/**
* 目標:呼叫UUIDUtils類中的方法得到一個uuid
*/
void thread_fun(void* arg){
//得到這個類UUIDUtils,需要用到JNIEnv,但是這是一個子執行緒,每個執行緒都有獨立的JNIEnv,所以我們需要獲取到
//這個執行緒的JNIEnv,通過JavaVM關聯當前執行緒,獲取當前執行緒的JNIEnv,(*javaVM)->AttachCurrentThread(javaVM,&env,NULL);
//那麼就需要先獲取到JavaVM
//如何獲取JavaVM?
//1.在JNI_OnLoad函式中獲取
//2.(*env)->GetJavaVM(env,&javaVM);
//的到JavaVM後,會得到env並賦值給變數
JNIEnv* env = NULL;
(*javaVM)->AttachCurrentThread(javaVM,&env,NULL);
char* no = (char*)arg;
int i;
for (i = 0; i < 5; ++i) {
LOGI("thread %s, i:%d",no,i);
jobject uuid = (*env)->CallStaticObjectMethod(env,uuidutils_jcls,get_mid);
char* uuid_char = (*env)->GetStringUTFChars(env,uuid,NULL);
LOGI("%s",uuid_char);
if(i == 4){
goto end;
}
(*env)->ReleaseStringUTFChars(env,uuid,uuid_char);
sleep(1);
}
end:
//如果直接將這兩行寫在迴圈之後是有問題的,當i=4的時候,執行緒直接退出,導致沒有執行DetachCurrentThread,報異常
(*javaVM)->DetachCurrentThread(javaVM);
pthread_exit((void*)0);
}
/**
* 當在JNI呼叫Android自帶的類時,經常需要傳入Context引數,那怎麼在JNI層獲取Context呢?
* 我們知道Application和Activity是Context的子類,由於每個Activity對應的Context是不一樣的,
* 所以一般情況下我們使用Application的Context,它在整個程式中只有一個例項。所以現在問題就變成了
* 怎麼在JNI中獲取Application呢?
* Android APP在啟動時會建立一個Activity Thread作為主執行緒,只要程式存活,這個執行緒就一直存在,
* 所以我們可以考慮從Activity Thread中獲取Application,檢視Activity Thread的原始碼發現,
* 它提供了一個方法可以獲取Application,如下:
*
* public Application getApplication() {
* return mInitialApplication;
* }
*
* 也就是說我們只需要獲取到Activity Thread的物件即可,Activity Thread提供了一個靜態方法用於獲取其例項,如下:
*
* public static ActivityThread currentActivityThread() {
* return sCurrentActivityThread;
* }
*
* 至此獲取Context的步驟已經很清晰了
*/
JNIEXPORT void JNICALL Java_com_example_ndk_1pthread_PosixUtils_init
(JNIEnv *env, jobject jobj){
/**
* 列印uuid
*/
//獲取class必須要在主執行緒中
jclass uuidutils_class_tmp = (*env)->FindClass(env,"com/example/ndk_pthread/UUIDUtils");
//建立全域性引用
uuidutils_jcls = (*env)->NewGlobalRef(env,uuidutils_class_tmp);
//獲取jmethodId也可以在子執行緒中
get_mid = (*env)->GetStaticMethodID(env,uuidutils_jcls,"get","()Ljava/lang/String;");
/**
* show toast
*/
//獲取jmethodId也可以在子執行緒中
toastId = (*env)->GetStaticMethodID(env,uuidutils_jcls,"showToast","(Landroid/content/Context;)V");
//獲取Activity Thread的例項物件
jclass activityThread = (*env)->FindClass(env,"android/app/ActivityThread");
jmethodID currentActivityThread = (*env)->GetStaticMethodID(env,activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
jobject at = (*env)->CallStaticObjectMethod(env,activityThread, currentActivityThread);
//獲取Application,也就是全域性的Context
jmethodID getApplication = (*env)->GetMethodID(env,activityThread, "getApplication", "()Landroid/app/Application;");
jobject context = (*env)->CallObjectMethod(env,at, getApplication);
jcontext = (*env)->NewGlobalRef(env,context);
}
JNIEXPORT void JNICALL Java_com_example_ndk_1pthread_PosixUtils_destroy
(JNIEnv *env, jobject jobj){
//釋放全域性引用
(*env)->DeleteGlobalRef(env,uuidutils_jcls);
}
JNIEXPORT void JNICALL Java_com_example_ndk_1pthread_PosixUtils_pthread
(JNIEnv *env, jobject jobj){
LOGI("%s","begin");
//獲取JavaVM的第一種方式,本專案中我們採用在JNI_OnLoad中獲取的方式
//(*env)->GetJavaVM(env,&javaVM);
pthread_t tid;
pthread_create(&tid,NULL,thread_fun,(void*)"NO1");
//呼叫Java函式show toast
(*env)->CallStaticVoidMethod(env,uuidutils_jcls,toastId,jcontext);
}
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ndk_pthread
LOCAL_SRC_FILES := ndk_pthread.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
相關文章
- Java執行緒篇——執行緒的開啟Java執行緒
- Android Studio NDK開發-JNI呼叫Java方法AndroidJava
- 執行緒、開啟執行緒的兩種方式、執行緒下的Join方法、守護執行緒執行緒
- Chrome 開啟多執行緒下載Chrome執行緒
- [從零開啟 Java 多執行緒 – 1 ]:開胃小菜Java執行緒
- [從零開啟 Java 多執行緒 - 1 ]:開胃小菜Java執行緒
- Android NDK開發之旅15 NDK Eclipse下NDK開發流程AndroidEclipse
- Android Studio NDK開發:JNI呼叫Java函式AndroidJava函式
- Java多執行緒/併發07、Thread.Join()讓呼叫執行緒等待子執行緒Java執行緒thread
- pthread 多執行緒基礎thread執行緒
- Mac下安裝NDK,進行JNI開發Mac
- 使用委託開啟多執行緒(多執行緒深入)執行緒
- java執行緒執行緒休眠,sleep方法Java執行緒
- Java的虛擬執行緒(協程)特性開啟預覽階段,多執行緒開發的難度將大大降低Java執行緒
- [Android開發]Mac下NDK開發(JNI)AndroidMac
- iOS開發基礎——執行緒安全(執行緒鎖)iOS執行緒
- C#多執行緒開發-執行緒同步 02C#執行緒
- C#多執行緒開發-執行緒池03C#執行緒
- Android NDK開發(二) 使用ndk-build構建工具進行NDK開發AndroidUI
- Java中多執行緒啟動,為什麼呼叫的是start方法,而不是run方法?Java執行緒
- 多執行緒(pthread,NSThread,GCD)執行緒threadGC
- JNI/NDK開發指南(8):呼叫構造方法和父類例項方法構造方法
- C#多執行緒開發-執行緒基礎 01C#執行緒
- 自己開發的線上視訊下載工具,基於Java多執行緒Java執行緒
- 【iOS開發】多執行緒 - 概述iOS執行緒
- 多執行緒同步的開發執行緒
- Java開發中Netty執行緒模型原理解析!JavaNetty執行緒模型
- 用Pthread實現多執行緒操作thread執行緒
- 檢視cpu是否開啟超執行緒執行緒
- WindowsMobile下如果進行NativeC++多執行緒的開發WindowsC++執行緒
- 【JAVA併發第二篇】Java執行緒的建立與執行,執行緒狀態與常用方法Java執行緒
- 【java 多執行緒】多執行緒併發同步問題及解決方法Java執行緒
- iOS多執行緒開發—GCD (一)iOS執行緒GC
- iOS開發多執行緒篇-概述iOS執行緒
- iOS 開發中的多執行緒iOS執行緒
- 畫江湖之 PHP 多執行緒開發 【執行緒安全 互斥鎖】PHP執行緒
- 畫江湖之 PHP 多執行緒開發 [執行緒安全 互斥鎖]PHP執行緒
- Java多執行緒-完成Android開發中的某些需求Java執行緒Android