Android NDK学习笔记3:JNI访问Java属性、方法
轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/119209444
本文出自【趙彥軍的博客】
文章目錄
- Java 類型和JNI符號對比
- JNI 訪問屬性
- native方法,非晶態和靜態區別
- 多種方式獲得 jclass
- JNI 方法Java方法
- 無參無返回值
- 有參數有返回值
- 靜態方法
- 方式1:非靜態 native 方法
- 方式2:靜態 native 方法
- 實戰演練: 調用Android Log
- 實戰演練: 彈 Toast
- 實戰演練:數組傳值
Java 類型和JNI符號對比
對應基礎類型字段的轉換:
| Boolean | Z |
| Byte | B |
| Char | C |
| Short | S |
| Int | I |
| Long | J |
| Float | F |
| Double | D |
對于引用類型的字段簽名轉換,是大寫字母 L 開頭,然后是類的簽名轉換,最后以 ; 結尾。
| String | Ljava/lang/String; |
| Class | Ljava/lang/Class; |
| Throwable | Ljava/lang/Throwable; |
| int[] | [I |
| Object[] | [Ljava/lang/Object; |
對于方法簽名描述的轉換,首先是將方法內所有參數轉換成對應的字段描述,并全部寫在小括號內,然后在小括號外再緊跟方法的返回值類型描述。
| String f(); | ()Ljava/lang/String; |
| long f(int i, Class c); | (ILjava/lang/Class;)J |
| String(byte[] bytes); | ([B)V |
這里要注意的是在 JNI 對應的描述轉換中不要出現空格。
JNI 訪問屬性
創建 Util.java 類
/*** @author : zhaoyanjun* @time : 2021/7/29* @desc :*/ public class Util {String usernName = "zhaoyanjun";int age = 10;static float key = 2f;//修改類string屬性native void changeNameValue();//修改類int屬性native void changeAgeValue();//改變靜態變量值native void changeStaticValue(); }訪問 String 屬性、int 屬性
class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)val util = Util()util.changeNameValue() //修改stringutil.changeAgeValue() //修改intutil.changeStaticValue() //修改靜態變量binding.sampleText.text = "value:${util.usernName} ${util.age} ${Util.key}"}companion object {// Used to load the 'native-lib' library on application startup.init {System.loadLibrary("native-lib")}} }native-lib.cpp
//修改字符串屬性 extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_changeNameValue(JNIEnv *env, jobject thiz) {//獲取classjclass cls = env->GetObjectClass(thiz);//獲取字段jfieldID fid = env->GetFieldID(cls, "usernName", "Ljava/lang/String;");//創建新值jstring str = env->NewStringUTF("niu b");//給字段賦值env->SetObjectField(thiz, fid, str); }//修改int屬性 extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_changeAgeValue(JNIEnv *env, jobject thiz) {//獲取classjclass cls = env->GetObjectClass(thiz);//獲取字段jfieldID fid = env->GetFieldID(cls, "age", "I");//給字段賦值env->SetIntField(thiz, fid, 100); }//修改靜態屬性 extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_changeStaticValue(JNIEnv *env, jobject thiz) {//獲取classjclass cls = env->GetObjectClass(thiz);//獲取字段jfieldID fid = env->GetStaticFieldID(cls, "key", "F");//獲取靜態字段值jfloat value = env->GetStaticFloatField(cls, fid);//給字段賦值env->SetStaticFloatField(cls, fid, value + 100); }運行起來,看看效果
值已經改變。
native方法,非晶態和靜態區別
Util.java
public class Util {native void fun1();native static void fun2(); }native-lib.cpp
//非靜態方法 extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_fun1(JNIEnv *env, jobject thiz) {}//靜態方法 extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_fun2(JNIEnv *env, jclass clazz) {}可以看到,非靜態方法,參數是 jobject; 靜態方法,參數是jclass。
多種方式獲得 jclass
extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_fun1(JNIEnv *env, jobject thiz) {//方式一:FindClass,參數傳入類全路徑jclass cls = env->FindClass("com/example/myapplication/Util");//方式二:GetObjectClassjclass cls2 = env->GetObjectClass(thiz); }JNI 方法Java方法
無參無返回值
/*** @author : zhaoyanjun* @time : 2021/7/29* @desc :*/ public class Util {void run() {Log.d("util-", "runing");}String speak(String message) {Log.d("util-", "speak " + message);return message + " java";}//無參無返回值native void callRun(); }native-lib.cpp
extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_callRun(JNIEnv *env, jobject thiz) {//獲取jclassjclass cls = env->GetObjectClass(thiz);//獲取方法jmethodID method = env->GetMethodID(cls, "run", "()V");//調用方法env->CallVoidMethod(thiz, method); }無參符號:() , 無返回值符號:V , 結合起來就是 ()V
有參數有返回值
native String callSpeak();native-lib.cpp
//調用帶有 extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_Util_callSpeak(JNIEnv *env, jobject thiz) {//獲取jclassjclass cls = env->GetObjectClass(thiz);//獲取方法jmethodID method = env->GetMethodID(cls, "speak", "(Ljava/lang/String;)Ljava/lang/String;");jstring str = env->NewStringUTF("jni hello");//方法調用,并接收返回值jobject result = env->CallObjectMethod(thiz, method, str);//返回結果return static_cast<jstring>(result); }靜態方法
方式1:非靜態 native 方法
public class Util {static void run() {Log.d("util-", "runing");}native void callRun(); }native-lib.cpp
extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_callRun(JNIEnv *env, jobject thiz) {jclass cls = env->GetObjectClass(thiz);jmethodID method = env->GetStaticMethodID(cls, "run", "()V");env->CallStaticVoidMethod(cls, method); }調用:
val util = Util() util.callRun()需要注意的是
- 獲取靜態方法用 GetStaticMethodID
- 調用靜態方法用 CallStaticVoidMethod
方式2:靜態 native 方法
public class Util {static void run() {Log.d("util-", "runing");}//靜態的native static void callRun(); }native-lib.cpp
extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_callRun(JNIEnv *env, jclass clazz) {jmethodID method = env->GetStaticMethodID(clazz, "run", "()V");env->CallStaticVoidMethod(clazz, method); }調用:
Util.callRun()需要注意的是:靜態的 native 方法,必須用 類名調用。非靜態的 nativie方法,需要用對象調用。
實戰演練: 調用Android Log
我們首先來看系統Log類
要確認兩點信息
第一:類的包名是:android.util
第二:d() 方法,兩個參數,都是 String 類型。返回值是 int
下面我們就可以寫代碼了。
首先創建 native 方法,有兩個參數,一個是 tag , 一個是 message
public class Util {//靜態的native static void logd(String tag, String message);}native-lib.cpp
extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_logd(JNIEnv *env, jclass clazz, jstring tag, jstring message) {//獲取Log classjclass cls = env->FindClass("android/util/Log");//獲取 d() 方法jmethodID method = env->GetStaticMethodID(cls, "d","(Ljava/lang/String;Ljava/lang/String;)I");//調用方法env->CallStaticIntMethod(cls, method, tag, message); }具體使用:
class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)//調用c++輸出日志Util.logd("china--", "good")}companion object {// Used to load the 'native-lib' library on application startup.init {System.loadLibrary("native-lib")}} }實戰演練: 彈 Toast
c 代碼
/*** 顯示一個 Toast* Toast.makeText(this,"message",Toast.LENGTH_SHORT).show();*/ extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_showToast(JNIEnv *env, jclass clazz, jobject context,jstring message) {//獲取Toastjclass cls = env->FindClass("android/widget/Toast");//獲取 makeText 方法jmethodID makeText = env->GetStaticMethodID(cls, "makeText","(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");//調用makeText方法, Toast.LENGTH_SHORT=0jobject toast = env->CallStaticObjectMethod(cls, makeText, context, message, 0);//獲取 show 方法jmethodID show = env->GetMethodID(cls, "show", "()V");//調用 show 方法env->CallVoidMethod(toast, show); }java 方法聲明
public class Util {native static void showToast(Context context, String message); }方法調用:
//顯示一個toast Util.showToast(this, "今天是周一")實戰演練:數組傳值
Util.java 類
public class Util {native void run();//這個方法時c調用public void show(String[] array) {for (int i = 0; i < array.length; i++) {Log.d("show-", "" + i + " " + array[i]);}} }可以看到 Util 有兩個方法,一個是 run , 一個是 show 方法,參數是一個 String 數組。
我們現在來做,java 調用 run , 然后 c 實現的 run 再調用 show方法,并且傳值 。
c 代碼如下:
extern "C" JNIEXPORT void JNICALL Java_com_example_myapplication_Util_run(JNIEnv *env, jobject thiz) {jclass cls = env->GetObjectClass(thiz);//獲取show反復jmethodID show = env->GetMethodID(cls, "show", "([Ljava/lang/String;)V");//定義string數組int size = 3;jclass strClass = env->FindClass("java/lang/String");jobjectArray resultArray = env->NewObjectArray(size, strClass, nullptr);//string數組賦值jstring strItem;for (int i = 0; i < size; ++i) {strItem = env->NewStringUTF("string in native");env->SetObjectArrayElement(resultArray, i, strItem);}//調用 show 方法env->CallVoidMethod(thiz, show, resultArray); }java 調用 run , 代碼如下:
Util().run()輸出結果如下:
D/show-: 0 string in native D/show-: 1 string in native D/show-: 2 string in native總結
以上是生活随笔為你收集整理的Android NDK学习笔记3:JNI访问Java属性、方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android NDK学习笔记2:数组
- 下一篇: Android NDK学习笔记4:JNI