본문 바로가기
모바일/안드로이드프로그래밍

[C/C++] 안드로이드에서 C/C++로 개발해보기 - 자바 클래스 메소드를 호출해서 더하기 출력하기(w. 필드 아이디로 데이터 읽고 입력하기)

by 푸_푸 2022. 8. 19.
728x90

두 개의 정수를 받아 자바 클래스 내에 존재하는 메소드를 호출하여 합산한 결과 출력하기


1. Native C++로 먼저 프로젝트를 만들어준다. 

프로젝트명 : HelloWorld

 

2. activity_Main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:stretchColumns="*"
        android:padding="10dp">
        <TableRow>
            <EditText
                android:id="@+id/i"
                android:layout_width="wrap_content"
                android:layout_height="40dp"
                android:inputType="number" />

            <EditText
                android:id="@+id/j"
                android:layout_width="wrap_content"
                android:layout_height="40dp"
                android:inputType="number"/>
        </TableRow>
    </TableLayout>

    <Button
        android:layout_gravity="center"
        android:id="@+id/sum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="합계" />

    <TextView
        android:layout_gravity="center"
        android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="결과" />

</LinearLayout>

정수를 입력 받을 EditText 2개, 합계 Button 한 개, 결과를 나타낼 TextView 하나를 배치한다. 

 

3. MainActivity.java

package com.example.helloworld;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;


public class MainActivity extends AppCompatActivity {

    public int i = 0, j = 0;
    private static final String TAG = "MyActivity";
    TextView textView;
    EditText ei;
    EditText ej;

    static {
        System.loadLibrary("helloworld");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.label);
        ei = findViewById(R.id.i);
        ej = findViewById(R.id.j);

        Button button = (Button)findViewById(R.id.sum);
        //사용자가 버튼을 클릭할 때 호출하는 콜백 메소드를 설정한다.
        button.setOnClickListener(saveListener);
    }

    private View.OnClickListener saveListener = new View.OnClickListener(){
        public void onClick(View v){
            try{
                i = Integer.parseInt(ei.getText().toString());
            }catch (NumberFormatException e){
                i = 0;  //숫자가 아닌 문자가 있다면 0으로 처리한다.
            }
            try{
                j = Integer.parseInt(ej.getText().toString());
            }catch (NumberFormatException e){
                j = 0;  //숫자가 아닌 문자가 있다면 0으로 처리한다.
            }

            JNICallBackMethod m = new JNICallBackMethod(i, j);
            String s = "return method " + m.PrinttoString();

            JNICallBackField test = new JNICallBackField(i + j, s);
            test.doFieldAccess();

            Log.v(TAG, s);
            textView.setText(s);
        }
    };
}

1) setContentView로 액티비티 연결. TextView, EditText, Button 연결한다.
2) 버튼 클릭 리스너로 JNICallBackMethod 클래스의 생성자를 호출하고 받아온 정수를 생성자의 매개변수로 전달한다.

3) JNICallBackField  클래스의 생성자를 호출하고 받아온 정수와 문자열을 매개변수로 전달한다.
4) 반환된결과를 화면에 출력한다.

 

4. JNICallBackMethod.java 생성

package com.example.helloworld;

public class JNICallBackMethod {
    private int x, y, sum;

    static{
        System.loadLibrary("helloworld");
    }

    public native String PrinttoString();

    public JNICallBackMethod(){
        this.x = 0;
        this.y = 0;
        this.sum = 0;
    }

    public JNICallBackMethod(int x, int y){
        this.x = x;
        this.y = y;
        this.sum = x + y;
    }

    @Override
    public String toString(){
        String str = "JNICallBackMethod [ " + x + " + " + y + " = " + sum + " ] " ;
        return str;
    }
}

 


toString() 메소드는 자바 클래스 가운데 최상위 클래스인 Object 클래스에서 제공하는 메소드로

JNICallBackMethod 클래스는 toString() 메소드를 오버라이드 해서 사용한다.

 

5. JNICallBackField.java 생성

package com.example.helloworld;

import android.util.Log;
public class JNICallBackField {
    private static int intField;
    private String stringField;
    private static final String TAG= "JNICallBackField";

    static{
        System.loadLibrary("helloworld");
    }

    public native void fieldAccess(); //네이티브 메서드 선언

    public JNICallBackField(int intField, String stringField){
        this.intField = intField;
        this.stringField= stringField;
    }

    public JNICallBackField(){
        this.intField = 0;
        this.stringField = "stringField";
    }

    public void doFieldAccess(){
        fieldAccess();  //네이티브 메소드 호출

        Log.i(TAG, "In Java");
        Log.i(TAG, "intField = " + intField);
        Log.i(TAG, "stringField = " + stringField);

    }
}

네이티브 메서드 선언. LogCat 출력.

 

6. native-lib.cpp

#include <jni.h>
#include <string>
#include <android/log.h>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_helloworld_JNICallBackMethod_PrinttoString(
        JNIEnv* env,
        jobject obj /* this */) {
    //위의 메소드내 obj 인스턴스를 사용하여 JNICallBackMethod 클래스를 얻는다.
    jclass cls = env->GetObjectClass(obj);
    //'toString()'이란 메소드이 아이디를 얻는다.
    jmethodID mid = env->GetMethodID(cls, "toString", "()Ljava/lang/String;");

    //메소드 아이디를 사용하여 함수를 호출하고 자바 문자열을 반환 받는다.
    //jstring s = (jstring) env->CallNonvirtualObjectMethod(obj, cls, mid);
    jstring s = (jstring) env->CallObjectMethod(obj, mid);

    //NULL을 추가하여 C 언어 스타일의 문자로 변경한다.
    const char *buf = env ->GetStringUTFChars(s, 0);

    //buf의 내용을 LogCat 화면에 출력시킨다.
    __android_log_print(ANDROID_LOG_INFO, "JNICallBackMethod_PrinttoString", "%s", buf);

    //사용한 버퍼를 삭제한다
    env ->ReleaseStringUTFChars(s, buf);

    //toSting() 함수로 부터 반환받은 문자열을 그대로 반환한다.
    return s;
}

//자바의 문자열을 화면에 출력시킬 때 사용한다.
//공동으로 사용하거나 또는 많이 사용하는 기능은 아래와 같이 별도 함수로 만들어 사용하면 편리하다.
static void printString(JNIEnv* env, jstring jstr){
    //자바의 문자열을 C 언어 UTF-8 로 변셩
    const char * str = env->GetStringUTFChars(jstr, 0);;
    //LogCat 에 출력
    __android_log_print(ANDROID_LOG_INFO, "JNICallBackField_FieldAccess", "StingField = %s\n", str);

    env->ReleaseStringUTFChars(jstr, str);
}

extern "C" JNIEXPORT void JNICALL
Java_com_example_helloworld_JNICallBackField_fieldAccess(
        JNIEnv* env,
        jobject obj) {
    //인스턴스로 부터 클래스를 얻는다.
    jclass cls = env->GetObjectClass(obj);

    //intField 라는 정적 필드 아이디를 얻는다.
    jfieldID  fid = env->GetStaticFieldID(cls, "intField", "I");
    if(!fid) {
        __android_log_print(ANDROID_LOG_INFO, "JNICallBackField_FieldAccess", "Error getting fid for intField\n");

        return;
    }
    //필드 아이디를 통해 정적 변수 내 데이터를 읽는다.
    jint i = env-> GetStaticIntField(cls, fid);
    __android_log_print(ANDROID_LOG_INFO, "JNICallBackField_FieldAccess", "intField = %d\n", i);    //화면에 출력

    //필드 아이디를 통해 정적 변수 내 200을 입력한다.
    env->SetStaticIntField(cls, fid, 200);

    //자바 클래스 내 stringField 변수 아이디를 얻는다.
    fid = env->GetFieldID(cls, "stringField", "Ljava/lang/String;");
    if(!fid) {
        __android_log_print(ANDROID_LOG_INFO, "JNICallBackField_FieldAccess", "Error getting fid gor stringField\n");
        return;
    }

    //문자열을 읽는다.
    jstring jstr = (jstring) env ->GetObjectField(obj, fid);
    //자바의 문자열을 화면에 출력시킨다.
    //printString(env, jstr);

    //UTF-8 타입으로 변수에 입력할 문자를 만든다.
    jstr = env->NewStringUTF("NewString");
    env->SetObjectField( obj, fid, jstr); //새로운 문자를 입력한다.

}

 


메소드 아이디 사용 : Java_com_example_helloworld_JNICallBackMethod_PrinttoString

1) GetObjectClass() : 인스턴스로부터 클래스를 얻는다.

2) GetMethodId() : 메소드 아이디를 얻는다.

3) CallObjectMethod() : 메소드 아이디를 사용하여 함수를 호출하고 자바 문자열을 반환받는다.

 

필드 아이디 사용 : Java_com_example_helloworld_JNICallBackField_fieldAccess

1) GetObjectClass() : 인스턴스로 부터 클래스를 얻는다.

2) GetStaticFieldID() : 정적 필드 아이디를 얻는다.

3) GetStaticIntField() : 필드 아이디를 통해 정적 변수 내 데이터를 읽는다.

4) SetStaticIntField() : 필드 아이디를 통해 정적 변수 내 데이터를 입력한다.

5) GetFieldID() : 자바 클래스 내 변수 아이디를 얻는다.

6) GetObjectField() : 변수 아이디를 통해 데이터를 읽는다.

7) SetObjectField() : 새로운 문자를 입력한다.


 

위의 프로그램을 실행하면 화면과 LogCat이 잘 출력되는 것을 확인할 수 있다.

728x90

댓글