在这一章中我们将会通过例子了解如何在本地代码中修改和访问Java中的实例变量和静态变量。静态变量所有的实例对象中都只有一份相同的实例变量。
下面看一个例子:
我们首先创建一个ClassField的类:
/** * Created by blueZhang on 16/7/1. * * @Author: BlueZhang * @date: 16/7/1 */
public class ClassField {
private static int num;
private String str;
public static int getNum() {
return num;
}
public static void setNum(int num) {
ClassField.num = num;
}
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
在这个类中我们可以看到创建了一个私有的静态变量、一个私有的成员变量。下面我们将会创建C文件通过native方法,对于静态变量通过反射找到Fidld然后将其值改变,对于成员变量我们通过反射机制创建对象,调用构造函数,调用set方法赋值。
首先同样的顺序在gradle.properties中添加android.useDeprecatedNdk=true,然后在local.properties中添加自己的NDK路径(我这里没有在AS中设置不想总是添加的人可以到AS设置中添加NDK路径Configer->Project Defaults->Project structure添加)然后生成.h文件
class_field.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_bluezhang_ndk5fieldtest_FieldNativeTest */
#ifndef _Included_com_example_bluezhang_ndk5fieldtest_FieldNativeTest
#define _Included_com_example_bluezhang_ndk5fieldtest_FieldNativeTest
#ifdef __cplusplus
extern "C" {
#endif
/* * Class: com_example_bluezhang_ndk5fieldtest_FieldNativeTest * Method: accessInstanceField * Signature: (Lcom/example/bluezhang/ndk5fieldtest/ClassField;)V */
JNIEXPORT void JNICALL Java_com_example_bluezhang_ndk5fieldtest_FieldNativeTest_accessInstanceField
(JNIEnv *, jobject, jobject);
/* * Class: com_example_bluezhang_ndk5fieldtest_FieldNativeTest * Method: accessStaticField * Signature: ()V */
JNIEXPORT void JNICALL Java_com_example_bluezhang_ndk5fieldtest_FieldNativeTest_accessStaticField
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
然后将生成的.h文件从debug目录中拷贝出来,放到我们的jni目录中现在需要做的就是创建相对用的.c文件
class_field.c
#include <jni.h>
#include "change.h"
#include <stdio.h>
JNIEXPORT void JNICALL
Java_com_example_bluezhang_ndk5fieldtest_FieldNativeTest_accessInstanceField(JNIEnv *env,
jobject cls,
jobject obj) {
jclass clazz;
jfieldID fid;
jstring j_str;
jstring j_newStr;
const char *c_str = NULL;
// 1.To get the AccessField's class quote
clazz = (*env)->GetObjectClass(env,obj);
if (clazz == NULL) {
return;
}
// 2. To get AccessField's class str param's ID
fid = (*env)->GetFieldID(env,clazz,"str", "Ljava/lang/String;");
if (clazz == NULL) {
return;
}
// 3. Get the value of the instance variables STR
j_str = (jstring)(*env)->GetObjectField(env,obj,fid);
// 4. Make unicode style java String to C String
c_str = (*env)->GetStringUTFChars(env,j_str,NULL);
if (c_str == NULL) {
return;
}
printf("In C--->ClassField.str = %s\n", c_str);
(*env)->ReleaseStringUTFChars(env, j_str, c_str);
// 5. modify the value of STR
j_newStr = (*env)->NewStringUTF(env, "This is C String");
if (j_newStr == NULL) {
return;
}
(*env)->SetObjectField(env, obj, fid, j_newStr);
// 6.Delete quote
(*env)->DeleteLocalRef(env, clazz);
(*env)->DeleteLocalRef(env, j_str);
(*env)->DeleteLocalRef(env, j_newStr);
}
JNIEXPORT void JNICALL
Java_com_example_bluezhang_ndk5fieldtest_FieldNativeTest_accessStaticField(JNIEnv *env, jclass cls)
{
jclass clazz;
jfieldID fid;
jint num;
//1.Get ClassField's Class quote
clazz = (*env)->FindClass(env,"com/study/jnilearn/ClassField");
if (clazz == NULL) { // have not this class
return;
}
//2.Get ClassField class static variable num attribute ID
fid = (*env)->GetStaticFieldID(env, clazz, "num", "I");
if (fid == NULL) {
return;
}
// 3.Get static variablenum's value
num = (*env)->GetStaticIntField(env,clazz,fid);
printf("In C--->ClassField.num = %d\n", num);
// 4.Modify static variable num's value
(*env)->SetStaticIntField(env, clazz, fid, 80);
// 5.Delete quote
(*env)->DeleteLocalRef(env,clazz);
}
好了现在我们要做的就是生成并使用.so文件 对我们的方法进行测试,要注意的事情是在通过反射机制获取类的时候或者是分配资源的时候一定要进行判空操作,否在将会出现严重饿错误。
下面再重新说一下生成so文件的简便的方法:在build.gradle中作如下配置:
ndk {
moduleName “math”
abiFilters “armeabi”, “armeabi-v7a”, “x86”,”arm64-v8a”,”x86_64”
}
生成后的.so文件位于app/build/intermediates/jni下面 然后粘贴到jniLibs目录下,这样就完成了一大半。
现在完成我们的测试类:
package com.example.bluezhang.ndk5fieldtest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("math");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView intView = (TextView) findViewById(R.id.int_tv);
TextView stringView = (TextView) findViewById(R.id.string_tv);
ClassField obj = new ClassField();
obj.setNum(10);
obj.setStr("Hello");
FieldNativeTest fieldNativeTest = new FieldNativeTest();
// 本地代码访问和修改ClassField为中的静态属性num
fieldNativeTest.accessStaticField();
fieldNativeTest.accessInstanceField(obj);
// 输出本地代码修改过后的值
System.out.println("In Java--->ClassField.num = " + obj.getNum());
System.out.println("In Java--->ClassField.str = " + obj.getStr());
intView.setText(ClassField.getNum()+"@@@@@@@@@");
stringView.setText(obj.getStr()+"@@@@@@@@@");
}
}
上面我们可以看到在初始化的时候我们将两个分别赋值(10,Hello)然后调用我们的native方法修改两个变量的值,并且将其输出显示在TextView上。好了本篇就讲这么多了下一篇我们将会继续深入。