プロジェクトにc++を持つモジュールを加える
Android Studio3.6.2(Windows)
2020.4.19 Coskx Lab
1 はじめに
Android Studioで,オリジナルモジュールをプロジェクトに追加します。追加するモジュールにはc++で記述された関数も含むことにします。
単純化した作業を行うアプリケーションを作り,上記のシチュエーションを3つの段階を経て目的の形にします。
作成するアプリケーションは,画面上EditTextに整数を入力し,計算ボタンを押すとTextViewに2倍された値を表示することとします。
(1)基本モジュールappのみで作成します。
モジュールappのMainActivityは,与えられた値を2倍にして表示します。
(2)オリジナルモジュールをプロジェクトに追加します。
オリジナルモジュール中に与えた値を2倍にして返すクラスをjavaのみで作ります。 モジュールappのMainActivityは自分で2倍の計算を行わず,このクラスを利用して2倍の値を得ます。
(3)オリジナルモジュール中にC++のファイルを使えるようにします。
与えた値を2倍にして返すクラスでは,自分で2倍の計算を行わず,
C++の関数に2倍の値を計算させます。すなわちMainActivityはモジュールのクラスに計算をさせようとしますが,
モジュールのクラスでは,C++の関数に計算させて,値をMainActivityに返します。
2 使用環境
- Windows 10 64-bit
- Android Studio 3.6.2
3 MainActivityのみで作成
Android Studioで新規プロジェクトをEmptyActivityで作ります。プロジェクト名は「MdlCpptest」とします。
res/layout/activity_main.xmlでボタンとEditText,TextViewを作ります。主要部分は次の通りです。
activity_main.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
<Button
android:id="@+id/button_show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="12dp"
android:text="calculate the double value" />
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Hello World!" />
</LinearLayout>
MainActivity.javaは,画面上EditTextに整数を入力し,計算ボタンを押すとTextViewに2倍された値を表示するだけなので,次の通りです。
MainActivity.java (パッケージ名はjp.gr.java_conf.coskx.mdlcpptestになっています。)
package jp.gr.java_conf.coskx.mdlcpptest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = (Button) findViewById(R.id.button_show);
button1.setOnClickListener(MainActivity.this);
}
@Override
public void onClick(View v) {
TextView textView = (TextView) findViewById(R.id.text_view);
EditText editText = (EditText) findViewById(R.id.edit_text);
// 入力された文字を取得
String str = editText.getText().toString();
int num = Integer.parseInt(str);
int num2=num*2;
str=""+num2;
// 2倍値をTextViewにセット!
textView.setText(str);
}
}
ここで第1段階の動作確認ができます。
4 オリジナルモジュールの追加(ここでモジュールも作ります)
オリジナルモジュールを追加して,受け取った整数値を2倍にして返すクラスを作成します。
(1)Android Studioの「File」「New」「New Module...」「Android Library」でオリジナルモジュールを「testlibrary」の名前で作ります。
Projectの表示で,appと同じレベルにtestlibraryができます。
(2)Android Studioの「File」「Project
Structure」で「Dependency」「Module」でappを選び,「Declared
Dependency」の「+」を選び,「Module Dependency」を選ぶと,testlibraryが表示されるのでチェックしてOK。
これでモジュール「testlibrary」(まだ空ですが)がプロジェクト「MdlCpptest」に取り込まれたことになります。
(3)Projectの「testlibrary」を開いて「java」「jp.gr.java_conf.coskx.testlibrary」のところで右クリックして「New」「Java class」で新規クラス「Calcvalue」を作ります。
この中身は,受け取った整数値を2倍にして返すように次のようにします。
Calcvalue.java
package jp.gr.java_conf.coskx.testlibrary;
public class Calcvalue {
private int value=0;
public void setValue(int v) {
value=v;
}
public int getValueDbl() {
int ret;
ret=value*2;
return ret;
}
}
(4)このクラスを使って,2倍の計算をさせるためにappのMainActivity.javaを次のように変更します。
(MainActivityとは別のモジュールtestlibraryを使用するので,importが増えています。)
MainActivity.java
package jp.gr.java_conf.coskx.mdlcpptest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import jp.gr.java_conf.coskx.testlibrary.Calcvalue;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Calcvalue mcalcvalue = new Calcvalue();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = (Button) findViewById(R.id.button_show);
button1.setOnClickListener(MainActivity.this);
}
@Override
public void onClick(View v) {
TextView textView = (TextView) findViewById(R.id.text_view);
EditText editText = (EditText) findViewById(R.id.edit_text);
// 入力された文字を取得
String str = editText.getText().toString();
int num = Integer.parseInt(str);
mcalcvalue.setValue(num);
int num2=mcalcvalue.getValueDbl();
str=""+num2;
// 2倍値をTextViewにセット!
textView.setText(str);
}
}
ここで第2段階の動作確認ができます。
5 オリジナルモジュールにC++の関数を加える
C++の関数はnative関数として,CPUに依存したライブラリになります。どのCPUをサポートするかを指示できるのですが,ここではデフォルト通りにします。
(1)NDKのインストール Android Studioの「Tools」「SDK Manager」を選び,開いたダイアログで「SDK Tools」タブを選びます。[NDK (Side by side)] と [CMake]
のチェックボックスをオンにします。OKOKFinishで戻っていきます。
(2)NDKフォルダの指示 Android Studioの「File」「Project Structure」で「SDK LOcation」を選び,「Android NDK location」のところに場所を指示します。
Android SDK Locationのフォルダの中にあります。通常は
C:\Users\tommy\AppData\Local\Android\Sdk\ndk
と思いますが複数のバージョンがインストールされている場合は更にその中の最新の方のフォルダを選びます。例えば
C:\Users\tommy\AppData\Local\Android\sdk\ndk\21.0.6113669
です。
(3)フォルダ「cpp」を「testlibrary」内に作成 Projectの表示方法を「Android」から「Project
Files」に変更します。「testlibrary/src/main」の階層構造が見えるので,「main」のところで右クリックして「new」
「Directory」で名前を「cpp」にします。
(4)フォルダ「cpp」に新規cppファイル「doubler.cpp」を作成 作成した「cpp」のところで右クリックして「new」「File」で名前を「doubler.cpp」とします。
関数名をfuncDoubleとすると通常のcppファイルなら
doubler.cpp
int funcDouble(int a)
{
return a * 2;
}
ですが,Javaから呼び出される関数なので,記述が特別です。
関数名もパッケージ名を含むようになります。(命名規則によるようです。.が_に変更され,_は直後に1がついてエスケープされています。)パラメータの並びも独特です。jintはintのことです。
doubler.cpp
#include <jni.h>
extern "C" JNIEXPORT jint JNICALL
Java_jp_gr_java_1conf_coskx_testlibrary_Calcvalue_funcDouble(
JNIEnv *env,
jobject /* this */,
jint a
) {
return a * 2;
}
(5)CMakeLists.txt(この名前は固定)の作成 C++プログラムをコンパイルしてライブラリ化してJavaから呼び出せるようにする設定を記述します。このファイルはcppファイルと同じ場所に置きます。
「cpp」のところで右クリックして「new」「File」で名前を「CMakeLists.txt」とします。doubler.cppから生成されるライブラリ名は何でもよいのですが,ここでは「doubler_lib」としました。この名前は後で使われます。
CMakeLists.txt
# ビルドに必要なCMakeの最小バージョン
cmake_minimum_required(VERSION 3.4.1)
# ビルドするライブラリの定義
add_library(
# 作成するライブラリ名
doubler_lib
# 作成するライブラリの属性
SHARED
# 作成するライブラリのソースファイル名
doubler.cpp) #.cppと)の間にスペースがあってはいけない(?)
# 指定されたビルド済みライブラリを検索し、パスを変数として保存します。 ここはデフォルト
find_library(
# パスの変数
log-lib
# NDK library 名
log)
# 作成されたライブラリをリンクします
target_link_libraries(
# 作成されたライブラリ
doubler_lib
# リンク先ライブラリ名
${log-lib})
(6)CMakeLists.txtの登録 (3)..(5)で作成したフォルダ「cpp」はまだプロジェクトには登録されていません。ここで,Projectの表示方法を「Project
Files」から「Android」に戻します。作成された「testlibrary」内の「cpp」がまだプロジェクトに登録されていないので,見えなくなります。
Projectの「testlibrary」で右クリックして「Link C++ Project with Gradle」で,先ほど作成した「CMakeLists.txt」を指示します。(結構深いところにあります。)Syncが終了して完了です。
(7)Calcvalue.javaの変更 Calcvalue.javaでは自分で2倍の計算をしていたのですが,こ
れをC++の関数に行わせます。次のように変更します。(5)で指示したライブラリー「doubler_lib」を使えるようにします。関数を呼び出すと
ころはint funcDouble(int a)として扱っています。最終行は関数int funcDouble(int
a)のプロトタイプの役割です。
Calcvalue.java
package jp.gr.java_conf.coskx.testlibrary;
public class Calcvalue {
static {
System.loadLibrary("doubler_lib");
}
private int value=0;
public void setValue(int v) {
value=v;
}
public int getValueDbl() {
int ret;
//ret=value*2;
ret=funcDouble(value);
return ret;
}
public native int funcDouble(int a);
}
これで目標のプロジェクトになりました。
build.gradle(testlibrary)には,CMakeLists.txtの場所についての次のような記述がありますので,確認しておきましょう。
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
}
}
5 まとめ
appのみのプロジェクトにオリジナルモジュールを追加し,さらにオリジナルモジュール注にC++の関数を導入する手順を3つの段階に分けて説明しました。