上一篇我介绍了传递参数和返回参数。而有时候我们的native code可能要直接操作java中的变量。JNI提供了一套简单的机制来实现这个要求。
1.public class FieldTest extends Activity {
2. public native void setField(); //设置实域
3. public native void setStaticField(); //设置动态域
4. private String myString;
5. private static String myStaticStirng;
6. TextView textView;
7. @Override
8. public void onCreate(Bundle savedInstanceState) {
9. super.onCreate(savedInstanceState);
10. setContentView(R.layout.main);
11. textView = (TextView)findViewById(R.id.textView);
12. myString = "Hello from java";
13. myStaticStirng = new String("hello form java static");
14. setField();
15. setStaticField();
16. textView.setText( myString+ "\n");
17. textView.append( myStaticStirng);
18. }
19. static{
20. System.loadLibrary("mylib");
21. }
22.}
上面定义了一个String变量myString、一个静态String域myStaticString。然后分别定义了一个native 方法将它们改变。native函数如下:
1.Java_com_tsiannian_gmail_xp_FieldTest_setField(JNIEnv* env, jobject obj){
2. jfieldID fid;
3. jstring jstr;
4. const char *str;
5. jclass jcls =(*env)->GetObjectClass(env, obj); //获得class的引用
6. fid = (*env)->GetFieldID(env, jcls, "myString", "Ljava/lang/String;");//获得域ID
7. if( fid==NULL ){
8. return;
9. }
10. jstr =(*env)->GetObjectField(env, obj, fid);//获得域的引用
11. jstring _str = (*env)->NewStringUTF(env, "JNI Field Test!");
12. if(_str == NULL){
13. return;
14. }
15. (*env)->SetObjectField(env, obj, fid, _str);//设置域的值
16.}
17.
18.Java_com_tsiannian_gmail_xp_FieldTest_setStaticField(JNIEnv* env, jobject obj){
19. jfieldID fid;
20. jstring jstr;
21. const char *str;
22. jclass jcls =(*env)->GetObjectClass(env, obj);
23. fid = (*env)->GetStaticFieldID(env, jcls, "myStaticStirng", "Ljava/lang/String;");
24. if( fid==NULL ){
25. return;
26. }
27. jstr =(*env)->GetStaticObjectField(env, jcls, fid);
28. jstring _str = (*env)->NewStringUTF(env, "JNI Static Field Test!");
29. (*env)->SetStaticObjectField(env, jcls, fid, _str);
30.}
前面说过要操作java虚拟机中的数据要通过env指向函数表中的函数。在这里分别是:
1.(*env)->SetObjectField(env, obj, fid, _str);//设置域的值
2.(*env)->SetStaticObjectField(env, jcls, fid, _str);
因为String在java中是一个对象,所以函数是SetObjectField。类似的函数组有Set<Type>Field();
_str是我们要设置的新值,fid是要修改的域的ID。它由下面的函数获得:
1.fid = (*env)->GetFieldID(env, jcls, "myString", "Ljava/lang/String;");//获得域ID
2.fid = (*env)->GetStaticFieldID(env, jcls, "myStaticStirng", "Ljava/lang/String;");
第三个参数是变量名,第四个参数叫做JNI field descriptor,JNI域描述。比如说"java.lang.String"类型,就是"Ljava/lang/String;"它以L开头,并且要以“;”结尾。如果是数组的话就要以“["开头,就像前面介绍的"[I"样,他表示int数组。
第二个参数是jclass类型。它由下面的函数得到:
1.jclass jcls =(*env)->GetObjectClass(env, obj);
在本程序中,原来的字符串是”Hello form java"调用native 方法之后变为了“JNI Test Field。”
运行结果如下: