JNI – Đối tượng – Phần 1

5/5 - (3 votes)

Trong phần này chúng ta sẽ tìm hiểu cách đọc và ghi thuộc tính của một đối tượng/lớp.

Đọc thuộc tính với kiểu dữ liệu cơ bản

Đầu tiên chúng ta có đoạn code main trong Java như sau:

class AccessField {
    static { System.loadLibrary("AccessField"); }
 
    public String s;
 
    private native void accessField();
    public static void main(String args[]) {
        AccessField af = new AccessField();
        af.s = "Lorem ipsum";
        af.accessField();
    }
}

Lớp AccessField có một thuộc tính là s kiểu String, phương thức native accessField() sẽ làm công việc hiển thị giá trị của thuộc tính đó lên màn hình.

Bên native chúng ta có đoạn code sau:

#include <iostream>
#include "AccessField.h"

JNIEXPORT void JNICALL Java_AccessField_accessField(JNIEnv *e, jobject obj) { 
    jclass cls = e->GetObjectClass(obj); 
    jfieldID fid = e->GetFieldID(cls, "s", "Ljava/lang/String;"); 
    jstring jstr = (jstring)e->GetObjectField(obj, fid); 
 
    const char *str = e->GetStringUTFChars(jstr, NULL); 
    std::cout << str;
    e->ReleaseStringUTFChars(jstr, str);
}

Để đọc một thuộc tính thì chúng ta làm theo các bước sau:

  • Lấy đối tượng jclass bằng phương thức GetObjectClass()
  • Lấy đối tượng jfieldID bằng phương thức GetFieldID(), phương thức này nhận vào đối tượng jclass, tên thuộc tính cần lấy, và tên lớp của thuộc tính đó, có đủ đường dẫn package
  • Lấy đối tượng đó bằng phương thức GetObjectField(), phương thức này nhận vào đối tượng jobjectjfieldID, phương thức này trả về đối tượng kiểu jobject, nếu thuộc tính cần lấy là kiểu dữ liệu cơ bản thì chúng ta có thể ép kiểu trực tiếp, đối với kiểu dữ liệu đối tượng thì chúng ta cũng làm tương tự (ví dụ ở dưới).
Lorem Ipsum

Tên đường dẫn lớp trong phương thức GetFieldID() có dạng:

"L/<đường_dẫn>/<package>/<tên_lớp;"

Nếu là các kiểu dữ liệu cơ bản thì chúng ta chỉ cần ghi ngắn gọn là:

  • int: "I"
  • float: "F"
  • double: "D"
  • boolean: "Z"

Không cần ghi tên package và tên lớp.

Đọc thuộc tính với kiểu dữ liệu đối tượng

Ví dụ chúng ta có lớp Point trong Java như sau:

class Point {
    private int x;
    private int y;
 
    public Point(int x, int y) { 
        this.x = x; 
        this.y = y;
    } 
 
    public void setX(int x) { this.x = x; }
    public void setY(int y) { this.y = y; }
 
    public int getX() { return x; }
    public int getY() { return y; }
}

Tiếp theo chúng ta có đoạn code main như sau:

class AccessField {
    static { System.loadLibrary("AccessField"); }
 
    private Point p;
    private native void accessFieldObject();
 
    public static void main(String args[]) { 
        AccessField af = new AccessField();
        af.p = new Point(3, 5);
        af.accessFieldObject();
    }
}

Lớp AccessField có một thuộc tính kiểu Pointp, phương thức accessFieldObject() sẽ lấy thuộc tính p này rồi hiển thị 2 thuộc tính con của xy, sau đó hiển thị lên màn hình.

Chúng ta làm việc đó như sau:

#include <iostream>
#include "AccessField.h"
using namespace std;

JNIEXPORT void JNICALL Java_AccessField_accessFieldObject(JNIEnv *e, jobject obj) { 
    jclass cls = e->GetObjectClass(obj);
    jfieldID fid = e->GetFieldID(cls, "p", "LPoint;");
 
    jobject pointObj = e->GetObjectField(obj, fid);
    jclass pointCls = e->GetObjectClass(pointObj);
    jfieldID xFid = e->GetFieldID(pointCls, "x", "I");
    jfieldID yFid = e->GetFieldID(pointCls, "y", "I");
 
    jint x = e->GetIntField(pointObj, xFid);
    jint y = e->GetIntField(pointObj, yFid);
 
    cout << "Point: " << x << ", " << y;
}

Chúng ta cũng lần lượt lấy jclass của đối tượng obj, tức là lấy được lớp AccessField. Sau đó lấy đối tượng jfieldID của thuộc tính p, rồi lấy đối tượng jobject của thuộc tính p này, từ đó chúng ta bắt đầu lại lấy jclass của thuộc tính p này, cứ thế cho đến khi lấy 2 thuộc tính xy.

Ở đây khi lấy thuộc tính xy thì chúng ta không dùng phương thức GetObjectField() rồi ép kiểu nữa, mà chúng ta dùng phương thức GetIntField() đế lấy thẳng đối tượng jint luôn. Trong JNI thì intjint là giống nhau nên chúng ta có thể dùng cout để in ra luôn.

Point: 3, 5

Tương tự, JNI có một số phương thức lấy dữ liệu khác là:

  • GetFloatField(): jfloat
  • GetBooleanField(): jboolean
  • GetByteField(): jbyte
  • GetCharField(): jchar
  • GetShortField(): jshort
  • GetLongField(): jlong
  • GetDoubleField(): jdouble

Đọc thuộc tính static

Đọc thuộc tính static cũng giống như thuộc tính thường, chỉ khác là một số phương thức sẽ có tên ‘Static’ trong đó.

Ví dụ:

Chúng ta có code main Java như sau:

class AccessField {
    static { System.loadLibrary("AccessField"); }
   
    private static float PI = 3.14159265359f;
    private native void accessStaticField();
 
    public static void main(String args[]) { 
        new AccessField().accessStaticField();
    }
}

Thuộc tính PI có kiểu là float, đây là thuộc tính static.

Code native như sau:

#include <iostream>
#include "AccessField.h"

using namespace std;

JNIEXPORT void JNICALL Java_AccessField_accessStaticField (JNIEnv *e, jobject obj) {
    jclass cls = e->GetObjectClass(obj);
    jfieldID fid = e->GetStaticFieldID(cls, "PI", "F");
    jfloat PI = e->GetStaticFloatField(cls, fid);
 
    printf("PI: %.9f", PI);
}

Phương thức lấy jfieldIDGetStaticFieldID(), lấy dữ liệu là GetStaticFloatField().

PI: 3.141592741

Tương tự, các phương thức lấy dữ liệu khác là

  • GetStaticObjectField()
  • GetStaticBooleanFielđ()
  • GetStaticByteField()
  • GetStaticCharField()
  • GetStaticShortField() 
  • GetStaticIntField() 
  • GetStaticLongField() 
  • GetStaticDoubleField()

Ghi dữ liệu vào thuộc tính

Ví dụ chúng ta có đoạn code main như sau:

class AccessField {
    static { System.loadLibrary("AccessField"); }
 
    private static float PI = 3.14159265359f;
    private static float radius;
 
    private native void setRadius();
 
    public static void main(String args[]) { 
        AccessField af = new AccessField();
        af.setRadius();
        System.out.println("Circle area: " + (radius * radius * PI));
    }
}

Lớp AccessField() có thuộc tính radius kiểu float, phương thức native setRadius() sẽ thiết lập giá trị cho thuộc tính này.

Code native như sau:

#include <iostream>
#include "AccessField.h"

using namespace std;

JNIEXPORT void JNICALL Java_AccessField_setRadius (JNIEnv *e, jobject obj) {
    jclass cls = e->GetObjectClass(obj);
    jfieldID fid = e->GetStaticFieldID(cls, "radius", "F");
    jfloat radius = e->GetStaticFloatField(cls, fid);
 
    radius = 5.0f;
    e->SetStaticFloatField(cls, fid, radius);
}

Mọi quá trình vẫn giống như cũ, chúng ta lấy jclass, jfieldID, lấy biến, sau đó gán giá trị cho biến.

Cuối cùng để thiết lập giá trị này cho thuộc tính ở phía Java thì chúng ta gọi phương thức SetStaticFloatField(), phương thức này nhận vào đối tượng jclass, nếu thuộc tính không phải là static thì chúng ta dùng phương thức SetFloatField() và truyền vào một đối tượng jobject, sau đó là jfieldID và tới giá trị.

Circle area: 78.53982

Các phương thức tương tự là:

STATIC NON STATIC KIỂU DỮ LIỆU
SetStaticObjectField() SetObjectField() jobject
SetStaticBooleanField() SetBooleanField() jboolean
SetStaticByteField() SetByteField() jbyte
SetStaticCharField() SetCharField() jchar
SetStaticShortField() SetShortField() jshort
SetStaticIntField() SetIntField() jint
SetStaticLongField() SetLongField() jlong
SetStaticFloatField() SetFloatField() jfloat
SetStaticDoubleField() SetDoubleField() jdouble
5 1 vote
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Inline Feedbacks
View all comments
Quỳnh
Quỳnh
7 năm trước

Phần này khá hay. Cảm ơn bạn đã chia sẻ. Mình cũng có 1 số vấn đề liên quan tới PureMVC C++ và java android.