[Back to HOME]
Android JNI:
C(Native)スレッドからのJavaメソッドの呼び出し
By Tom Kao(高煥堂) 2009.09.28
E-mail: misoo.tw@gmail.com
Taipei, Taiwan
[prev] Android JNI: Javaオブジェクトの属性値へのアクセス方法
前の記事(Android JNI: NativeからのJavaメソッドの呼び出し)でNativeコードからJavaメソッドまでのコールバック方法はわかった。このシナリオで、コールバックを実行する現在のスレッドはJava側から来ます。今度は、コールバックを実行する現在のスレッドはNative側で作成されます。
// Java側のサンプルコード
package com.misoo.pk01;
import com.misoo.thread.JTX05;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
private final int FP = LinearLayout.LayoutParams.FILL_PARENT;
private Button btn, btn2, btn3;
public static TextView tv;
private JTX05 obj;
private long javaTID;
public static ac01 ref;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
ref = this;
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
btn = new Button(this);
btn.setId(101);
btn.setText("go");
btn.setBackgroundResource(R.drawable.heart);
btn.setOnClickListener(this);
LinearLayout.LayoutParams param
= new LinearLayout.LayoutParams(120, 50);
param.topMargin = 10;
layout.addView(btn, param);
btn3 = new Button(this);
btn3.setId(103);
btn3.setText("exit");
btn3.setBackgroundResource(R.drawable.heart);
btn3.setOnClickListener(this);
layout.addView(btn3, param);
tv = new TextView(this);
tv.setTextColor(Color.WHITE);
tv.setText("");
LinearLayout.LayoutParams param2 =
new LinearLayout.LayoutParams(FP, WC);
param2.topMargin = 10;
layout.addView(tv, param2);
setContentView(layout);
}
public void onClick(View v) {
if(v == btn){
obj = new JTX05();
int cs = (int)obj.calculate();
}
else if(v == btn3){
finish();
}
}}
// 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<JTX05>(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);
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);
}
l execute()が呼ばれるとき、JavaスレッドはNativeコードと呼ぶためにVMに入ることです。
l 今、現在のスレッドは、新しいサブスレッドを作成するためにpthread_create()と呼びます。
l このサブスレッドを VM へ接続しして、それがVMのサービスを使用できるようにします。
l そして、このサブスレッドは、Javaコードを呼び出すのにVMのサービスを使用します。
Nativeメソッドは以下のようなソースになります。
/* com_misoo_thread_JTX05.h */
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* 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 <utils/Log.h>
#include <utils/IPCThreadState.h>
#include <utils/ProcessState.h>
#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<string, JNIGlobalRef<jobject> *> 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;
}
ソースコードの要点について説明するために
Java_com_misoo_thread_JTX05_execute(JNIEnv *env, jobject thiz, jobject syncObj){
……
int rr = pthread_create( &thread, NULL, trRun, NULL);
……
}
l pthread_create()を使用して、現在のプロセスへ新しいサブスレッドを追加します。
l trRun()は新しく作成したスレッドで実行するメソッドです。
l pthread_create()が正常終了すると、生成されたスレッドのIDが threadに格納されます。
l pthread_create()が正常終了すると、0 を返します。
void* trRun( void* )
{
…….
JNIEnv *env;
……..
status = gJavaVM->AttachCurrentThread(&env, NULL);
…….
env->CallStaticVoidMethod(mClass, mid, 222, 0);
…….
}
l gJavaVM ポインターは、スレッドが付加されるVMを識別します。
l 現在のスレッドを VM へ接続しして、それがVMのサービスを使用できるようにします。
l env引数で JNIインタフェース・ポインターを返します。
l 最終的に、コールバックは、envポインターを使用することで完成します。◆
~~ END ~~