DXライブラリ Android版を使用した Androidアプリで Java のコードで GPS を使用する


 すべての処理を C++ で実行できれば良いのですが、Android の基本言語が Java である関係で、
今のところ Java を使用しないと Android端末に搭載されている GPS を使用することができません。

 なので、ここでは『DXライブラリ Android版』には無い GPS による位置情報の取得を Java を使用して
行うための手順を記します。


 1.『AndroidManifest.xml』の内容を GPS の使用と Java のコードを実行できるように変更する

 2.プロジェクトのターゲット API レベルを 23 に変更する

 3.プロジェクトに Java のソースファイルを追加する

 4.Java のコードを入力する

 5.Java の情報を取得する C++ のコードを入力する



1.プロジェクトの設定と『『AndroidManifest.xml』の内容を GPS の使用と Java のコードを実行できるように変更する

  VisualStudio Community 2017 ( 又は 2015 )『DXライブラリ Android版』を使用する Androidアプリのプロジェクトを開き
 『ソリューションエクスプローラー』と書かれているリストに表示されている『AndroidManifest.xml』
 クリックして、内容を表示します。

  『AndroidManifest.xml』の内容は、使い方ページに沿って編集した場合は以下のようになっていると思いますが、
<?xml version="1.0" encoding="utf-8"?> <!-- Changes made to Package Name should also be reflected in the Debugging - Package Name property, in the Property Pages --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.$(ApplicationName)" android:versionCode="1" android:versionName="1.0"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/> <!-- This .apk has no Java code itself, so set hasCode to false. --> <application android:label="@string/app_name" android:hasCode="false" android:theme="@android:style/Theme.NoTitleBar"> <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="android.app.NativeActivity" android:label="@string/app_name" android:configChanges="orientation|screenSize" android:launchMode="singleInstance"> <!-- Tell NativeActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="$(AndroidAppLibName)"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>

  この中の以下のように変更します。(色が緑や赤の部分が変更箇所や追加箇所で、6箇所です)
<?xml version="1.0" encoding="utf-8"?> <!-- Changes made to Package Name should also be reflected in the Debugging - Package Name property, in the Property Pages --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.$(ApplicationName)" android:versionCode="1" android:versionName="1.0"> <!-- This is the platform API where NativeActivity was introduced. --> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <!-- This .apk has no Java code itself, so set hasCode to false. --> <application android:label="@string/app_name" android:hasCode="true" android:theme="@android:style/Theme.NoTitleBar"> <!-- Our activity is the built-in NativeActivity framework class. This will take care of integrating with our NDK code. --> <activity android:name="com.(プロジェクト名).Packaging.(プロジェクト名)" android:label="@string/app_name" android:configChanges="orientation|screenSize" android:launchMode="singleInstance"> <!-- Tell NativeActivity the name of our .so --> <meta-data android:name="android.app.lib_name" android:value="$(AndroidAppLibName)"/> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



2.プロジェクトのターゲット API レベルを 23 に変更する

  次に 以下の手順で Java プロジェクトのAPIレベルを 23 に変更します。
  初期状態では APIレベルが 19 ですが、アプリが GPS を使う際に必要な権限取得の処理が、Android 6.0 ( APIレベル 23 )のときに
 大きく変更されたので、それに対応する処理を行うためには APIレベルを 23 以上にする必要がある為です。

  @ 『ソリューションエクスプローラー』と書かれているリストに表示されている『(プロジェクト名).Packaging』
    を右クリックして、表示されるプルダウンメニューから『プロパティ(R)』を選択して、
    『(プロジェクト名) プロパティページ』ダイアログを表示します。

  A 次にダイアログの左側のリストから『構成プロパティ』→『全般』を選びます。

  B ダイアログ右側に表示されている『ターゲット API レベル』の項目を『KitKat 4.4 - 4.4.4、(android-19)』から
    『Marshmallow 6.0、(android-23)』に変更した後、ダイアログ右下にある『OK』ボタンを押してダイアログを閉じます。

  これでプロジェクトで使用する Java の API レベルが 23 になります。



3.プロジェクトに Java のソースファイルを追加する

  次に Java のソースファイルを以下の手順でプロジェクトに追加します。

  @ 『ソリューションエクスプローラー』と書かれているリストに表示されている『(プロジェクト名).Packaging』
    を右クリックして、表示されるプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、名前を『src』としてください。

  A 次に追加した『src』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『com』としてください。

  B 次に追加した『com』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『(プロジェクト名)』( (プロジェクト名) はお手元のプロジェクトの名前に置き換えてください )としてください。

  C 次に追加した『(プロジェクト名)』フォルダーを右クリックして、再びプルダウンメニューから『追加(D)』→『新しいフォルダー(D)』を選択して、
    『新しいフォルダー』を追加して、今度は名前を『Packaging』としてください。

    と、要は『(プロジェクト名).Packaging』以下に『src』→『com』→『(プロジェクト名)』→『Packaging』というフォルダ
    を作ります。次に漸く Java ファイルの追加です。

  D 追加した『Packaging』フォルダーを右クリックして、今度は『新しい項目(W)...』を選択して『新しい項目の追加』ダイアログを表示します。

  E ダイアログ左側のリストから『インストール済み』→『Corss Platform』→『Android』を選び、
    右側のリストから『Java』を選びます。

  F ダイアログ下側の『名前(N):』の項目に『(プロジェクト名).java』と入力して『追加(A)』ボタンを押します。

  G 次に追加された『(プロジェクト名).java』を開いた状態でメニューから『ファイル(F)』→『名前を付けて(プロジェクト名).javaを保存(A)...』を
    選択して、『名前を付けてファイルを保存』ダイアログを表示します。
    ( VisualStudio 2015 の場合はメニューから『ファイル(F)』→『保存オプションの詳細設定(V)...』を選択して、『保存オプションの詳細設定』ダイアログを表示します )

  H 『名前を付けてファイルを保存』ダイアログの右下にある『上書き保存(S)』の項目を『エンコード付きで保存(V)...』
    に変更します。すると変更した瞬間に『名前を付けて保存の確認』ダイアログが表示されますので『はい(Y)』を選択してください。
    ( VisualStudio 2015 の場合はこの工程が無いので、手順Iに進んでください )

  I 次に『保存オプションの詳細設定』ダイアログの『エンコード(E):』の項目を『日本語 (シフト JIS) - コードページ 932』から
    『Unicode (UTF-8 シグネチャなし) - コードページ 65001』に変更して『OK』ボタンを押します。
    ( 『Unicode (UTF-8 シグネチャ付き) - コードページ 65001』と間違えないようにしてください、
     『Unicode (UTF-8 シグネチャなし) - コードページ 65001』はリストのかなり下の方にあります )


 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )


  これで java ファイルの追加は完了です。



4.Java のコードを入力する

  追加した『(プロジェクト名).java』には最初から数行書かれていますが、それを全部消して、代わりに
 今回の『GPS による位置情報の取得』を行うための以下のコードを入力します。
package com.(プロジェクト名).Packaging; import android.app.NativeActivity; import android.os.Bundle; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.widget.Toast; import android.Manifest; import android.content.pm.PackageManager; import android.support.v4.app.ActivityCompat; public class (プロジェクト名) extends NativeActivity { // 緯度 double mLatitude ; // 経度 double mLongitude ; // 位置情報の状態( 0:使用可能かチェック中 1:使用可能 2:使用不可能 ) int mGPSState ; // 使用するGPSプロバイダ String mUseGPSProvider ; // 位置マネージャ LocationManager mLocationManager ; // 位置情報取得の権限をリクエストする際に使用する識別番号 static final int PERMISSIONS_REQUEST_LOCATION = 1 ; // 位置マネージャの初期化を行う private void InitializeLocationManager() { // 位置マネージャの取得 mLocationManager = ( LocationManager )getSystemService( this.LOCATION_SERVICE ) ; // 使用するGPSプロバイダの取得 // 端末のGPSの位置情報が取得できる場合は端末のGPSを使用 Location location = mLocationManager.getLastKnownLocation( LocationManager.GPS_PROVIDER ); if( location != null ) { mUseGPSProvider = LocationManager.GPS_PROVIDER; } else { // ネットワークからの位置情報が取得できる場合はネットワークの位置情報を使用 location = mLocationManager.getLastKnownLocation( LocationManager.NETWORK_PROVIDER ); if( location != null ) { mUseGPSProvider = LocationManager.NETWORK_PROVIDER; } else { // どちらからも位置情報が取得できない場合はOSに選択してもらう Criteria criteria = new Criteria() ; criteria.setAccuracy( Criteria.ACCURACY_FINE ) ; // 位置精度優先 mUseGPSProvider = mLocationManager.getBestProvider( criteria, true ) ; } } // 位置情報取得の開始 mLocationManager.requestLocationUpdates( mUseGPSProvider, 3000, // 位置情報更新を行う最低更新時間間隔( 単位:ミリ秒 ) 0, // 位置情報更新を行う最小距離間隔( 単位:メートル ) new LocationListener() { // 位置情報が更新されたときに呼ばれる関数 @Override public void onLocationChanged( Location location ) { // 位置情報取得権限のチェック if( ActivityCompat.checkSelfPermission( (プロジェクト名).this, Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( (プロジェクト名).this, Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { // 位置情報取得の権限が無い場合は何もせずに終了 return ; } // ここに来たらGPSが使用可能ということにする mGPSState = 1 ; // 経度と緯度を取得 mLatitude = location.getLatitude() ; mLongitude = location.getLongitude() ; // 位置情報が更新された旨を表示 Toast.makeText( (プロジェクト名).this, "位置情報が更新されました", Toast.LENGTH_SHORT ).show() ; } // 使用しているGPSプロバイダが無効になった場合 @Override public void onProviderDisabled( String provider ) { Toast.makeText( (プロジェクト名).this, "GPSプロバイダが無効になりました", Toast.LENGTH_SHORT ).show() ; } // 使用しているGPSプロバイダが有効になった場合 @Override public void onProviderEnabled( String provider ) { Toast.makeText( (プロジェクト名).this, "GPSプロバイダが有効になりました", Toast.LENGTH_SHORT ).show() ; } // 使用しているGPSプロバイダの状態が変化した場合 @Override public void onStatusChanged(String provider, int status, Bundle extras) { Toast.makeText( (プロジェクト名).this, "GPSプロバイダの状態が変化しました", Toast.LENGTH_SHORT ).show() ; } } ); } // GPSの処理を開始する public void StartGPS() { // UIスレッドで実行する処理を登録する runOnUiThread( new Runnable() { // UIスレッドで呼ばれる関数 @Override public void run() { // Android のバージョンチェック if( android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M ) { // Android 6.0以上の場合はアプリ実行中に位置情報取得の権限があるかをチェックする // 位置情報取得の権限があるか判定 if( checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { // 位置情報取得の権限があれば位置マネージャを初期化 Toast.makeText( (プロジェクト名).this, "このアプリは位置情報取得の権限が既にあります", Toast.LENGTH_SHORT ).show() ; InitializeLocationManager() ; } else { // 位置情報取得の権限が無ければ権限を求めるダイアログを表示 requestPermissions( new String[]{ Manifest.permission.ACCESS_FINE_LOCATION }, PERMISSIONS_REQUEST_LOCATION ) ; } } else { // Android 6.0未満の場合はアプリ実行時には位置情報取得の権限が許可されているので // 無条件で位置マネージャの初期化を行う InitializeLocationManager() ; } } // 権限の許可を求めるダイアログで許可か不許可が選択されたら呼ばれる関数 public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults ) { // 位置情報取得の権限を求めるリクエストに対する結果の場合のみ処理を行う if( requestCode == PERMISSIONS_REQUEST_LOCATION ) { // 許可されたのかどうかを判定 if( grantResults[ 0 ] == PackageManager.PERMISSION_GRANTED ) { // 許可されたら位置マネージャを初期化 Toast.makeText( (プロジェクト名).this, "位置情報取得が許可されました", Toast.LENGTH_SHORT ).show() ; InitializeLocationManager() ; } else { // 許可されなかったらその旨を表示する Toast.makeText( (プロジェクト名).this, "位置情報取得が拒否されました", Toast.LENGTH_SHORT ).show() ; // GPS使用不可の状態にする mGPSState = 2 ; } } } } ) ; } }
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



5.Java の情報を取得する C++ のコードを入力する

  次に Java の GPS の処理を開始する関数を呼び出したり、GPS の状態や GPS の緯度・経度を画面に表示したりする C++ 側のプログラムです。
  『DXライブラリ Android版』でのみ使用できる GetNativeActivity という関数を使用しています。
#include "DxLib.h" #include <string.h> int android_main( void ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; int Counter = 0 ; double Latitude = 0.0 ; double Longitude = 0.0 ; int GPSState = 0 ; // 背景を灰色にする SetBackgroundColor( 128,128,128 ) ; // DXライブラリの初期化 if( DxLib_Init() < 0 ) return -1 ; // 描画先を裏画面に変更 SetDrawScreen( DX_SCREEN_BACK ) ; // アプリの NativeActivity を取得しておく NativeActivity = GetNativeActivity() ; // Java の関数 StartGPS の呼び出し { // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス (プロジェクト名) を取得 jclass jclass_(プロジェクト名) = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス (プロジェクト名) のメンバー関数 StartGPS の ID を取得 jmethodID jmethodID_StartGPS = env->GetMethodID( jclass_(プロジェクト名), "StartGPS", "()V" ) ; // Java のクラス (プロジェクト名) のメンバー関数 StartGPS の呼び出し env->CallVoidMethod( NativeActivity->clazz, jmethodID_StartGPS ) ; // Java のクラス (プロジェクト名) の参照を削除 env->DeleteLocalRef( jclass_(プロジェクト名) ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; } // メインループ while( ProcessMessage() == 0 ) { // 裏画面の内容をクリア ClearDrawScreen() ; // GPSの位置情報は頻繁には更新されないので、10フレームに1回の間隔で情報を更新する Counter ++ ; if( Counter >= 10 ) { // カウンタをリセット Counter = 0 ; // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス (プロジェクト名) を取得 jclass jclass_(プロジェクト名) = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス (プロジェクト名) のメンバー変数 mLatitude の ID を取得 jfieldID jfieldID_mLatitude = env->GetFieldID( jclass_(プロジェクト名), "mLatitude", "D" ) ; // Java のクラス (プロジェクト名) のメンバー変数 mLatitude の値をローカル変数 Latitude に代入 Latitude = env->GetDoubleField( NativeActivity->clazz, jfieldID_mLatitude ) ; // Java のクラス (プロジェクト名) のメンバー変数 mLongitude の ID を取得 jfieldID jfieldID_mLongitude = env->GetFieldID( jclass_(プロジェクト名), "mLongitude", "D" ) ; // Java のクラス (プロジェクト名) のメンバー変数 mLongitude の値をローカル変数 Longitude に代入 Longitude = env->GetDoubleField( NativeActivity->clazz, jfieldID_mLongitude ) ; // Java のクラス (プロジェクト名) のメンバー変数 mGPSState の ID を取得 jfieldID jfieldID_mGPSState = env->GetFieldID( jclass_(プロジェクト名), "mGPSState", "I" ) ; // Java のクラス (プロジェクト名) のメンバー変数 mGPSState の値をローカル変数 GPSState に代入 GPSState = env->GetIntField( NativeActivity->clazz, jfieldID_mGPSState ) ; // Java のクラス (プロジェクト名) の参照を削除 env->DeleteLocalRef( jclass_(プロジェクト名) ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; } // GPSの状態によって画面に表示する内容を変更 switch( GPSState ) { case 0 : // GPSが使用可能かチェック中 DrawString( 0, 80, "GPSが使用可能かチェック中", GetColor( 255,255,255 ) ) ; break ; case 1 : // GPSが使用可能 DrawString( 0, 80, "GPSは使用可能", GetColor( 255,255,255 ) ) ; // 経度と緯度を画面に描画 DrawFormatString( 0, 120, GetColor( 255,255,255 ), "経度 %f", Latitude ) ; DrawFormatString( 0, 140, GetColor( 255,255,255 ), "緯度 %f", Longitude ) ; break ; case 2 : // GPSは使用不可能 DrawString( 0, 80, "GPSは使用不可能", GetColor( 255,255,255 ) ) ; break ; } // 裏画面の内容を表画面に反映 ScreenFlip() ; } // DXライブラリの後始末 DxLib_End() ; // ソフトの終了 return 0 ; }
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )

  以上です。
  これでプロジェクトを実行すると、画面に GPS が使用可能かや、使用可能な場合は緯度・経度が表示されます。

  詳細は割愛しますが、C++ から Java にアクセスする基本的な流れは。

   @ AttachCurrentThreadAsDaemon でソフト実行用スレッドと JavaVM を関連付け、JNIEnv も取得。

   A GetObjectClass でアプリの Java クラスを取得。

   B GetMethodID や GetFieldID で Java の関数や変数の ID を取得。

   C Call???Method や Get???Field で Java の関数の呼び出しや変数の値を取得。

   D DeleteLocalRef で Get???Field で取得したオブジェクト系の変数の参照を削除。

   E DeleteLocalRef で GetObjectClass で取得したアプリの Java クラスの参照を削除。

   F DetachCurrentThread でソフト実行用スレッドと JavaVM の関連付けを終了。

  となります。
  C++ と Java でのやり取りを行う機能( JNI( Java Native Interface ) ) の詳しい扱い方については、検索サイトで
 『Android Java JNI』などのキーワードで検索して調べてみてください。





戻る