--------------- 前言 -------------------------------------------------------------
基本上,從 C程式透過JNI而呼叫Java 物件之函數,其情境有三:
1) 獨立的C程式呼叫Java 函數
---- 這範例如下述michtw之回應
2) Java 線程進入Native C函數,再回頭呼叫Java 函數
3) Java 線程進入Native C函數,誕生子線程,由子線程呼叫Java 函數
---- 這範例請看下述由高老師寫的範例。
------------------------------------------------------------------------------------------
1. albert_espresso的原來問題:
小弟目前必須使用JNI的invocation API去cearteVM再找到特定的object並呼叫其方法, 呼叫物件方法如果由java call C的時候是很容易實作的因為與JVM的通道是由java打開 , 但是如果今天倒過來要由C呼叫java中的物件(與JVM的通道是由C打開)這樣的話就不是很清楚該怎麼做 , 因為C的部分是在linux下執行所以有想過應該是要create一個thread讓他可以使用invocation API指回JVM 再去找java物件 , 因為小弟我不熟linux的東西 , 不曉得各位是否可以推薦相關的網站或是資料可以給小弟參考 , 或是知道這種issue應該要由怎樣的方向下手會比較好 , 請給小弟一些建議
2. michtw回應的範例:
#include
#include
#include
JNIEnv* create_vm(JavaVM ** jvm)
{
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[2];
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=."; /* user classes */
vm_args.version = JNI_VERSION_1_4; //JDK version.
vm_args.options = options;
vm_args.nOptions = 2;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, &env, &vm_args);
if(ret < 0)
printf("\nUnable to Launch JVM %d\n",ret);
return env;
}
int main(int argc, char* argv[])
{
JNIEnv *env;
JavaVM * jvm;
env = create_vm(&jvm);
if (env == NULL)
return 1;
jclass clsH =NULL;
jmethodID midMain = NULL;
jmethodID midCalling = NULL;
clsH = env->FindClass("Hello");
if (clsH != NULL)
{
midMain = env->GetStaticMethodID(clsH, "main", "([Ljava/lang/String;)V");
midCalling = env->GetStaticMethodID(clsH,"TestCallMethod","(Ljava/lang/String;)V");
}
else
{
printf("\nUnable to find the requested class\n");
}
/************************************************************************/
/* Now we will call the functions using the their method IDs */
/************************************************************************/
if(midMain != NULL)
env->CallStaticVoidMethod(clsH, midMain, NULL); //Calling the main method.
if (midCalling!=NULL)
{
jstring StringArg = env->NewStringUTF("\nTestCall:Called from the C Program\n");
//Calling another static method and passing string type parameter
env->CallStaticVoidMethod(clsH,midCalling,StringArg);
}
//Release resources.
int n = jvm->DestroyJavaVM();
return 0;
}
3. albert_espresso的再度說明問題:
你所提出的解答是屬於C要呼叫一般的java的做法 , 在android平台上不太一樣 , 在android上可以直接經由JNI_OnLoad(JavaVM *vm,void *reserved)直接取得指向android dalvik vm的ponter , 但現在我的問題是卡在 , 我將該pointer存在一個static global JavaVM* , 讓其他的thread可以使用 , 在一般的C呼叫java可行但是在android卻不行 , 找過很多相關的資料也都是相同的做法當然都不是android平台就是 , 換句話說android平台在invocation API這部分似乎和一般的JNI寫法應該有不同的地方 , 畢竟android的VM是自家做的跟一般的java VM有不同 , 不曉得有沒有人知道這個問題怎解 , 或是大家有不同的想法可以提出來討論一下
4. 高煥堂撰寫的範例:
// JTX05.java
package com.misoo.thread;
import java.lang.ref.WeakReference;
import com.misoo.pk01.ac01;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
public class JTX05 {
private long refer;
private static Handler h;
private static int count;
static {
System.loadLibrary("JTX05_jni");
}
public JTX05(){
Init(new WeakReference(this));
count = 0;
h = new Handler(){
public void handleMessage(Message msg) {
count++;
if(count == 1) {
ac01.ref.setTitle("env: " + String.valueOf(msg.arg1));
}
else if(count == 2)
ac01.tv.setText("env: " + String.valueOf(msg.arg1));
}
};
}
public long calculate(){
execute(this);
//this.ssetTitle(ss);
//ac01.ref.setTitle("ss: " + ss);
return 0;
}
@SuppressWarnings("unused")
private static void callback(int a, int b){
Message m = h.obtainMessage(1, a, 3, null);
h.sendMessage(m);
}
private native void Init(Object weak_this);
private native String execute(Object oSync);
//private native String execute(int x);
}
/* com_misoo_thread_JTX05.h */
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_misoo_thread_JTX05 */
#ifndef _Included_com_misoo_thread_JTX05
#define _Included_com_misoo_thread_JTX05
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_misoo_thread_JTX05
* Method: nativeSetup
* Signature: (Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_com_misoo_thread_JTX05_Init
(JNIEnv *, jobject, jobject);
/*
* Class: com_misoo_thread_JTX05
* Method: execute
* Signature: (J)J
*/
JNIEXPORT jstring JNICALL Java_com_misoo_thread_JTX05_execute
(JNIEnv *, jobject, jobject);
#ifdef __cplusplus
}
#endif
#endif
/* com_misoo_thread_JTX05.cpp */
#include "com_misoo_thread_JTX05.h"
#include
#include
#include
#include "android_runtime/AndroidRuntime.h"
using namespace android;
JavaVM *gJavaVM;
jmethodID mid;
jclass mClass; // Reference to JTX05 class
jobject mObject; // Weak ref to JTX05 Java object to call on
char sTid[20];
unsigned int e1;
int x;
int sum;
pthread_t thread;
void* trRun( void* );
typedef multimap *> MapOfObjects;
//--------------------------------------------------------------
void Thread_sleep(int t)
{
timespec ts;
ts.tv_sec = t;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
return;
}
JNIEXPORT void JNICALL
Java_com_misoo_thread_JTX05_Init(JNIEnv *env, jobject thiz, jobject weak_this)
{
jclass clazz = env->GetObjectClass(thiz);
mClass = (jclass)env->NewGlobalRef(clazz);
mObject = env->NewGlobalRef(weak_this);
mid = env->GetStaticMethodID(mClass, "callback","(II)V");
return;
}
JNIEXPORT jstring JNICALL
Java_com_misoo_thread_JTX05_execute(JNIEnv *env, jobject thiz, jobject syncObj){
//-----------------------------------------------------------
env->CallStaticVoidMethod(mClass, mid, 111, 0);
//-----------------------------------------------------------
int rr = pthread_create( &thread, NULL, trRun, NULL);
//-----------------------------------------------------------
long pid = getpid();
sprintf(sTid, "%lu", pid);
jstring ret = env->NewStringUTF(sTid);
return ret;
}
//--------------------------------------------------------------------
static const char *classPathName = "com/misoo/thread/JTX05";
static JNINativeMethod methods[] = {
{"init", "(Ljava/lang/Object;)V",
(void *)Java_com_misoo_thread_JTX05_Init},
{"execute", "(Ljava/lang/Object;)Ljava/lang/String;",
(void *)Java_com_misoo_thread_JTX05_execute}
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods) / sizeof(methods[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
// ----------------------------------------------------------------------------
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv *env;
gJavaVM = vm;
int result;
LOGI("JNI_OnLoad called");
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("Failed to get the environment using GetEnv()");
return -1;
}
if (registerNatives(env) != JNI_TRUE) {
LOGE("ERROR: registerNatives failed");
goto bail;
}
result = JNI_VERSION_1_4;
bail:
return result;
}
//--------------------------------------------------------------------------------
void* trRun( void* )
{
int status;
JNIEnv *env;
bool isAttached = false;
Thread_sleep(1);
status = gJavaVM->GetEnv((void **) &env, JNI_VERSION_1_4);
if(status < 0) {
LOGE("callback_handler: failed to get JNI environment, "
"assuming native thread");
status = gJavaVM->AttachCurrentThread(&env, NULL);
if(status < 0) {
LOGE("callback_handler: failed to attach "
"current thread");
return NULL;
}
isAttached = true;
}
//-----------------------------------------------------------
env->CallStaticVoidMethod(mClass, mid, 222, 0);
//-----------------------------------------------------------
if(isAttached)
gJavaVM->DetachCurrentThread();
return NULL;
}
~~ END ~~
(misoo)