C03_c 基 於 軟 硬 整 合 觀 點 JNI: 从 C 调 用 Java 函 数 ( c) By 高 煥 堂
3 How-to: 基 於 軟 硬 整 合 觀 點 从 C 调 用 Java 函 数
如 果 控 制 点 摆 在 本 地 C 层, 就 会 常 常 1. 从 本 地 C 函 数 去 调 用 Java 函 数 ; 2. 从 本 地 C 函 数 去 存 取 Java 层 对 象 的 属 性 值 ; 3. 从 本 地 C 函 数 去 创 建 Java 层 的 对 象
关 于 JNI, 大 家 都 知 道 如 何 从 Java 调 用 C 函 数 然 而, 在 Android 里, 反 而 由 C 呼 叫 Java 的 情 形 才 更 具 关 键 性 例 如,Activity 的 跨 进 程 沟 通 如 下 :
当 App 里 的 Activity 透 过 IBinder 接 口 来 与 Service 进 行 IPC 沟 通 时, 事 实 上 是 由 Java 层 的 Activity 调 用 C/C++ 模 块 去 进 行 IPC 沟 通, 再 由 C 模 块 调 用 Java 层 的 Service 所 以,Java 与 C 函 数 的 双 向 调 用 都 是 Android 平 台 的 重 要 机 制
举 例 说 明
在 CounterNative 类 别 里, 有 3 个 本 地 函 数 : 静 态 (static) 的 nativeexecute() 和 一 般 的 nativesetup() 及 nativeexec() 其 中, 静 态 nativeexecute() 会 调 用 Java 层 的 一 般 的 setv() 函 数 ; 而 一 般 的 nativeexec() 会 调 用 Java 层 的 静 态 setvalue() 函 数
// ac01.java //.. public class ac01 extends Activity implements OnClickListener { private CounterNative cn; @Override public void oncreate(bundle savedinstancestate){ //.. cn = new CounterNative(); } @Override public void onclick(view v) { switch(v.getid()){ case 101: cn.nativeexec(10); break; case 102: CounterNative.nativeExecute(11); break; case 103: finish(); break; }}}
指 令 :cn = new CounterNative(); 其 调 用 CounterNative() 建 构 函 数 执 行 到 nativesetup() 函 数, 转 而 调 用 本 地 C 函 数 : com_misoo_counter_counternative_nativesetup() 这 个 函 数 只 负 责 将 m_class m_object m_static_mid 和 m_mid 储 存 在 C 模 块 的 静 态 区 域 里 而 已
执 行 指 令 : cn.nativeexec(10); 就 呼 叫 C 函 数 :nativeexec(), 计 算 出 sum 值 之 后, 透 过 VM 的 CallVoidMethod() 函 数 而 调 用 到 目 前 Java 对 象 的 setvalue() 函 数, 把 sum 值 传 入 Java 层, 并 显 示 出 来
// CounterNative.java // public class CounterNative { private static Handler h; static { System.loadLibrary("MyCounter"); } public CounterNative(){ h = new Handler(){ public void handlemessage(message msg) { ac01.ref.settitle(msg.obj.tostring()); }}; nativesetup(); } private static void setvalue(int value){ String str = "Value(static) = " + String.valueOf(value); Message m = h.obtainmessage(1, 1, 1, str); h.sendmessage(m); }
} private void setv(int value){ String str = "Value = " + String.valueOf(value); Message m = h.obtainmessage(1, 1, 1, str); h.sendmessage(m); } private native void nativesetup(); public native static void nativeexecute(int n); public native void nativeexec(int n);
ac01 调 用 CounterNative 类 的 建 构 函 数, 此 函 数 诞 生 了 一 个 Handler 对 象, 并 且 调 用 本 地 的 nativesetup() 函 数 随 后,ac01 将 调 用 静 态 的 nativeexecute() 函 数, 此 函 数 则 反 过 来 调 用 Java 层 一 般 的 setv() 函 数 接 着,ac01 调 用 一 般 的 nativeexec() 函 数, 此 函 数 则 反 过 来 呼 叫 Java 层 的 静 态 setvalue() 函 数
留 意 : 目 前 该 行 代 码 正 由 那 一 个 线 程 所 执 行 请 记 得, 在 学 习 Android 时, 从 第 一 秒 钟 就 持 着 优 雅 的 素 养 : 对 于 每 一 行 代 码, 都 必 须 能 准 确 而 正 确 地 说 出 来, 目 前 该 行 代 码 正 由 那 一 个 线 程 (Thread) 所 执 行 的
/* com.misoo.counter.counternative.c */ #include "com_misoo_counter_counternative.h" jclass m_class; jobject m_object; jmethodid m_mid_static, m_mid; JNIEXPORT void JNICALL Java_com_misoo_counter_CounterNative_nativeSetup (JNIEnv *env, jobject thiz) { jclass clazz = (*env)->getobjectclass(env, thiz); m_class = (jclass)(*env)->newglobalref(env, clazz); m_object = (jobject)(*env)->newglobalref(env, thiz); m_mid_static = (*env)->getstaticmethodid(env, m_class, "setvalue", "(I)V"); m_mid = (*env)->getmethodid(env, m_class, "setv", "(I)V"); return; }
JNIEXPORT void JNICALL Java_com_misoo_counter_CounterNative_nativeExecute (JNIEnv *env, jclass clazz, jint n) { int i, sum = 0; for(i=0; i<=n; i++) sum+=i; (*env)->callvoidmethod(env, m_object, m_mid, sum); return; } JNIEXPORT void JNICALL Java_com_misoo_counter_CounterNative_nativeExec (JNIEnv *env, jobject thiz, jint n) { int i, sum = 0; for(i=0; i<=n; i++) sum+=i; (*env)->callstaticvoidmethod(env, m_class, m_mid_static, sum); return; }
说 明 nativesetup() 函 数 的 内 容 上 述 的 nativesetup() 函 数 之 定 义 : JNIEXPORT void JNICALL Java_com_misoo_counter_CounterNative_nativeSetup (JNIEnv *env, jobject thiz) { //.. }
其 中 的 第 2 个 参 数 thiz 就 是 Java 层 目 前 对 象 的 参 考 (Reference) 所 谓 目 前 对 象 就 是 正 在 调 用 此 本 地 函 数 的 Java 层 对 象 例 如, 在 此 范 例 里, 就 是 CounterNative 类 的 对 象 参 考
指 令 : jclass clazz = (*env)->getobjectclass(env, thiz); 向 VM(Virtual Machine) 询 问 这 thiz 所 参 考 对 象 的 类 ( 即 CounterNative 类 别 )
由 于 这 class 是 这 本 地 函 数 的 区 域 (Local) 变 量, 当 此 函 数 执 行 完 毕 后, 这 个 class 变 量 及 其 所 参 考 的 值 都 会 被 删 除 因 此, 使 用 指 令 : m_class = (jclass)(*env)->newglobalref(env, clazz); 来 将 区 域 型 的 class 参 考 转 换 为 全 域 (Global) 型 的 参 考, 并 将 此 全 域 参 考 存 入 到 这 本 地 C 模 块 的 全 域 变 数 m_class 里
如 此, 当 函 数 执 行 完 毕 后, 这 个 m_class 变 量 及 其 所 参 考 的 值 都 不 会 被 删 除 掉 同 理, thiz 也 是 区 域 变 量, 函 数 执 行 完 毕, 这 个 thiz 及 其 值 都 会 被 删 除 因 此, 使 用 指 令 : m_object = (jobject)(*env)->newglobalref(env, thiz); 将 区 域 型 的 class 参 考 转 换 为 全 域 (Global) 型 的 参 考, 并 将 此 全 域 参 考 存 入 到 这 本 地 C 模 块 的 全 域 变 数 m_object 里
接 着, 指 令 : m_mid_static = (*env)->getstaticmethodid(env, m_class, "setvalue", "(I)V"); 这 要 求 VM 去 取 得 m_class 所 参 考 的 类 ( 就 是 CounterNative 类 ) 的 setvalue() 函 数 的 ID 值 并 将 此 ID 值 存 入 到 这 本 地 C 模 块 的 全 域 变 数 m_mid_static 里
同 理, 指 令 : m_mid = (*env)->getmethodid(env, m_class, "setv", "(I)V"); 这 找 到 CounterNative 类 的 setv() 函 数 的 ID, 并 将 此 ID 值 存 入 到 这 本 地 C 模 块 的 全 域 变 数 m_mid 里
由 于 m_class 和 m_object 两 者 都 是 参 考 (Reference), 其 必 须 透 过 VM 的 NewGlobalRef() 来 转 换 出 全 域 性 的 参 考 至 于 m_static_mid 和 m_mid 则 是 一 般 的 整 数 值, 直 接 储 存 于 静 态 变 量 里 即 可 了
说 明 nativeexecute() 和 nativeexec() 函 数 的 内 容 于 此, 这 nativesetup() 函 数 已 经 将 m_class m_object m_static_mid 和 m_mid 储 存 妥 当 了, 准 备 好 让 后 续 调 用 nativeexecute() 和 nativeexec() 函 数 时 能 使 用 之
例 如 : JNIEXPORT void JNICALL Java_com_misoo_counter_CounterNative_nativeExecute (JNIEnv *env, jclass clazz, jint n) { // (*env)->callvoidmethod(env, m_object, m_mid, sum); }
这 个 m_object 正 指 向 Java 层 的 目 前 对 象, 而 m_mid 则 是 其 setv() 函 数 的 ID 依 据 这 两 项 资 料, 就 能 透 过 VM 的 CallVoidMethod() 函 数 而 调 用 到 目 前 Java 对 象 的 setv() 函 数, 而 把 数 据 传 送 到 Java 层
Summary:How To 拿 目 前 对 象 指 针 换 取 它 的 类 ( 目 前 类 )ID: jclass clazz = (*env)->getobjectclass(env, thiz); 拿 目 前 类 ID 换 取 某 函 数 ID: m_mid = (*env)->getmethodid(env, m_class, "setv", "(I)V");
依 据 类 ID 和 函 数 ID, 调 用 这 指 定 的 类 里 的 指 定 的 函 数 : (*env)->callvoidmethod(env, m_object, m_mid, sum);
~ Continued ~