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


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

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


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

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

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



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

  Android Studio『DXライブラリ Android版』を使用する Androidアプリのプロジェクトを開き
 画面左側のリストの『app』→『manifests』の中にある『AndroidManifest.xml』
 ダブルクリックして、内容を表示します。

  『AndroidManifest.xml』の内容は、使い方ページに沿って編集した場合は以下のようになっていると思いますが、
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.dxlibtest_androidstudio"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:hasCode="false"> <activity android:name="android.app.NativeActivity"> <meta-data android:name="android.app.lib_name" android:value="native-lib" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>

  この中の以下のように変更します。(色が緑の部分が変更箇所や追加箇所で、4箇所です)
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.dxlibtest_androidstudio"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" android:hasCode="true"> <activity android:name=".MainActivity"> <meta-data android:name="android.app.lib_name" android:value="native-lib" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
 


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

  Android Studio の画面左側のリストの『app』→『java』の中には使い方ページに沿ってプロジェクト名を
 『test』にした場合は『com.example.test』があり、更にその中に『MainActivity』がありますので、
 ダブルクリックして内容を表示すると最初から30行ほど書かれていますが、それを全部消して、代わりに
 今回の『GPS による位置情報の取得』を行うための以下のコードを入力します。
package com.example.(プロジェクト名); 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 androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; public class MainActivity 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( ContextCompat.checkSelfPermission( MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission( MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) { // 位置情報取得の権限が無い場合は何もせずに終了 return ; } // ここに来たらGPSが使用可能ということにする mGPSState = 1 ; // 経度と緯度を取得 mLatitude = location.getLatitude() ; mLongitude = location.getLongitude() ; // 位置情報が更新された旨を表示 Toast.makeText( MainActivity.this, "位置情報が更新されました", Toast.LENGTH_SHORT ).show() ; } // 使用しているGPSプロバイダが無効になった場合 @Override public void onProviderDisabled( String provider ) { Toast.makeText( MainActivity.this, "GPSプロバイダが無効になりました", Toast.LENGTH_SHORT ).show() ; } // 使用しているGPSプロバイダが有効になった場合 @Override public void onProviderEnabled( String provider ) { Toast.makeText( MainActivity.this, "GPSプロバイダが有効になりました", Toast.LENGTH_SHORT ).show() ; } // 使用しているGPSプロバイダの状態が変化した場合 @Override public void onStatusChanged(String provider, int status, Bundle extras) { Toast.makeText( MainActivity.this, "GPSプロバイダの状態が変化しました", Toast.LENGTH_SHORT ).show() ; } } ); } // 権限の許可を求めるダイアログで許可か不許可が選択されたら呼ばれる関数 public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults ) { // 位置情報取得の権限を求めるリクエストに対する結果の場合のみ処理を行う if( requestCode == PERMISSIONS_REQUEST_LOCATION ) { // 許可されたのかどうかを判定 if( grantResults[ 0 ] == PackageManager.PERMISSION_GRANTED ) { // 許可されたら位置マネージャを初期化 Toast.makeText( MainActivity.this, "位置情報取得が許可されました", Toast.LENGTH_SHORT ).show() ; InitializeLocationManager() ; } else { // 許可されなかったらその旨を表示する Toast.makeText( MainActivity.this, "位置情報取得が拒否されました", Toast.LENGTH_SHORT ).show() ; // GPS使用不可の状態にする mGPSState = 2 ; } } } // 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( MainActivity.this, "このアプリは位置情報取得の権限が既にあります", Toast.LENGTH_SHORT ).show() ; InitializeLocationManager() ; } else { // 位置情報取得の権限が無ければ権限を求めるダイアログを表示 requestPermissions( new String[]{ Manifest.permission.ACCESS_FINE_LOCATION }, PERMISSIONS_REQUEST_LOCATION ) ; } } else { // Android 6.0未満の場合はアプリ実行時には位置情報取得の権限が許可されているので // 無条件で位置マネージャの初期化を行う InitializeLocationManager() ; } } } ) ; } }
 ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



3.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 のクラス MainActivity を取得 jclass jclass_MainActivity = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス MainActivity のメンバー関数 StartGPS の ID を取得 jmethodID jmethodID_StartGPS = env->GetMethodID( jclass_MainActivity, "StartGPS", "()V" ) ; // Java のクラス MainActivity のメンバー関数 StartGPS の呼び出し env->CallVoidMethod( NativeActivity->clazz, jmethodID_StartGPS ) ; // Java のクラス MainActivity の参照を削除 env->DeleteLocalRef( jclass_MainActivity ) ; // 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 のクラス MainActivity を取得 jclass jclass_MainActivity = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス MainActivity のメンバー変数 mLatitude の ID を取得 jfieldID jfieldID_mLatitude = env->GetFieldID( jclass_MainActivity, "mLatitude", "D" ) ; // Java のクラス MainActivity のメンバー変数 mLatitude の値をローカル変数 Latitude に代入 Latitude = env->GetDoubleField( NativeActivity->clazz, jfieldID_mLatitude ) ; // Java のクラス MainActivity のメンバー変数 mLongitude の ID を取得 jfieldID jfieldID_mLongitude = env->GetFieldID( jclass_MainActivity, "mLongitude", "D" ) ; // Java のクラス MainActivity のメンバー変数 mLongitude の値をローカル変数 Longitude に代入 Longitude = env->GetDoubleField( NativeActivity->clazz, jfieldID_mLongitude ) ; // Java のクラス MainActivity のメンバー変数 mGPSState の ID を取得 jfieldID jfieldID_mGPSState = env->GetFieldID( jclass_MainActivity, "mGPSState", "I" ) ; // Java のクラス MainActivity のメンバー変数 mGPSState の値をローカル変数 GPSState に代入 GPSState = env->GetIntField( NativeActivity->clazz, jfieldID_mGPSState ) ; // Java のクラス MainActivity の参照を削除 env->DeleteLocalRef( jclass_MainActivity ) ; // 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 も取得。

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

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

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

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

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

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

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





戻る