DXライブラリ Android版を使用した Androidアプリで Java のコードで HTTP通信を行う
( Android Studio編 )


 すべての処理を C++ で実行できれば良いのですが、Android の基本言語が Java である関係で、
今のところ Java を使用しないと HTTP通信を行うことができません。( ソケットによる HTTP通信を
行う場合は恐らく C++ のみで実現可能ですが… )

 なので、ここでは『DXライブラリ Android版』には無い HTTP通信処理を Java を使用して
行うための手順を記します。


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

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

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



1.プロジェクトの設定と『『AndroidManifest.xml』の内容を インターネットの使用と 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>

  この中の以下のように変更します。(色が緑の部分が変更箇所や追加箇所で、3箇所です)
<?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.INTERNET"/> <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行ほど書かれていますが、それを全部消して、代わりに
 今回の『Java による HTTP通信』を行うための以下のコードを入力します。
package com.example.(プロジェクト名); import android.app.NativeActivity; import java.net.URL; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.io.InputStream; import java.io.IOException; import java.lang.Thread; public class MainActivity extends NativeActivity { // 接続 URL 受け渡し用変数 static String HTTP_URLString ; // メソッドタイプ受け渡し用変数 static int HTTP_MethodType ; // 通信処理の終了確認用変数 static int HTTP_Connect_Finish ; // HTTPレスポンスコードを代入する変数 static int HTTP_ResponseCode ; // 例外発生時のメッセージを代入する変数 static String HTTP_ExceptionMessage ; // 通信結果のデータを格納するバッファ関連の変数 static boolean HTTP_DataBuffer_Initialize = false ; static byte[] HTTP_DataBuffer ; static int HTTP_DataBufferBytes = 8 * 1024 * 1024 ; // 通信結果を代入するバッファのサイズは 8MB( 足りない場合は増やしてください ) static int HTTP_DataBufferEnableBytes ; // 非同期で HTTP 通信するためのクラス public class HTTP_ConnectTask extends Thread { public void run() { HttpURLConnection HTTPCon = null ; URL url = null ; try { // URLの作成 url = new URL( HTTP_URLString ) ; // 接続用HttpURLConnectionオブジェクト作成 HTTPCon = ( HttpURLConnection )url.openConnection() ; // メソッドタイプによって処理を分岐 switch( HTTP_MethodType ) { case 0: // GET HTTPCon.setRequestMethod( "GET" ) ; HTTPCon.setDoOutput( false ) ; break ; case 1: // POST HTTPCon.setRequestMethod( "POST" ) ; HTTPCon.setDoOutput( true ) ; break ; case 2: // PUT HTTPCon.setRequestMethod( "PUT" ) ; HTTPCon.setDoOutput( true ) ; break ; } HTTPCon.setDoInput( true ) ; // 接続 HTTPCon.connect() ; // レスポンスコードの取得 HTTP_ResponseCode = HTTPCon.getResponseCode(); // 通信結果のデータを格納する配列がまだ未初期化の場合はここで初期化 if( HTTP_DataBuffer_Initialize == false ) { HTTP_DataBuffer_Initialize = true; HTTP_DataBuffer = new byte[ HTTP_DataBufferBytes ] ; } // 有効なデータのサイズを0にセット HTTP_DataBufferEnableBytes = 0 ; // 通信結果のデータを取得 InputStream in = HTTPCon.getInputStream() ; int ReadSize = in.read( HTTP_DataBuffer ) ; while( ReadSize > 0 ) { HTTP_DataBufferEnableBytes += ReadSize ; ReadSize = in.read( HTTP_DataBuffer, HTTP_DataBufferEnableBytes, HTTP_DataBufferBytes - HTTP_DataBufferEnableBytes ) ; } in.close() ; } catch ( MalformedURLException e ) { // MalformedURL例外が発生したらレスポンスコードを代入する変数に -3 を代入 HTTP_ResponseCode = -3 ; // 例外のメッセージを取得する HTTP_ExceptionMessage = e.toString() ; } catch ( IOException e ) { // IO例外が発生したらレスポンスコードを代入する変数に -2 を代入 HTTP_ResponseCode = -2 ; // 例外のメッセージを取得する HTTP_ExceptionMessage = e.toString() ; } // HTTP通信が完了したので 1 を代入する HTTP_Connect_Finish = 1 ; } } // HTTP通信を行う Java の関数 public void HTTP_Connect( String URLString, int Method ) { // HTTP通信の完了を確認するための変数を初期化 HTTP_Connect_Finish = 0 ; // 有効な取得データのサイズを初期化 HTTP_DataBufferEnableBytes = 0 ; // レスポンスコードを初期化 HTTP_ResponseCode = -1 ; // 非同期処理を行うクラスが参照できるようにグローバル変数に引数を代入 HTTP_URLString = URLString ; HTTP_MethodType = Method ; // HTTP通信を非同期で行う為のクラスを作成・実行 HTTP_ConnectTask HTTPTask = new HTTP_ConnectTask() ; HTTPTask.start() ; } }
-->  ( (プロジェクト名)となっている箇所は、お手元のプロジェクトの名前を入力してください )



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

  次に Java の HTTP通信の処理を開始する関数を呼び出したり、HTTP通信の結果を取得したり通信結果を画面に表示したりする C++ 側のプログラムです。
  『DXライブラリ Android版』でのみ使用できる GetNativeActivity という関数を使用しています。
#include "DxLib.h" #include <string.h> // データ取得用バッファ 8MB( 足りない場合は増やしてください ) BYTE HTTP_DataBuffer[ 8 * 1024 * 1024 ] ; // HTTP通信を行う // URL : 接続するパス // Method : "GET", "POST", "PUT" の何れか // 戻り値 : 0:成功 -1:エラー int Android_HTTP_Connect( const char *URL, const char *Method ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; jstring jstring_URL ; int MethodType = 0 ; // アプリの NativeActivity を取得しておく NativeActivity = GetNativeActivity() ; // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // URL から jstring を作成 jstring_URL = env->NewStringUTF( URL ) ; // メソッド名からメソッドタイプの数値をセット if( strcmpDx( "GET", Method ) == 0 ) { MethodType = 0 ; } else if( strcmpDx( "POST", Method ) == 0 ) { MethodType = 1 ; } else if( strcmpDx( "PUT", Method ) == 0 ) { MethodType = 2 ; } // Java のクラス MainActivity を取得 jclass jclass_MainActivity = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス MainActivity のメンバー関数 HTTP_Connect の ID を取得 jmethodID jmethodID_HTTP_Connect = env->GetMethodID( jclass_MainActivity, "HTTP_Connect", "(Ljava/lang/String;I)V" ) ; // Java のクラス MainActivity のメンバー関数 HTTP_Connect の呼び出し env->CallVoidMethod( NativeActivity->clazz, jmethodID_HTTP_Connect, jstring_URL, MethodType ) ; // Java の参照を削除 env->DeleteLocalRef( jclass_MainActivity ) ; env->DeleteLocalRef( jstring_URL ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; // 正常終了 return 0 ; } // HTTP通信の結果を取得する // 戻り値 : 通信の結果( 0:まだ通信中 1以上:HTTPレスポンスコード -2:IO例外発生 -3:MalformedURL例外発生 ) int Android_HTTP_GetResponseCode( void ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; int Result = -1 ; // アプリの NativeActivity を取得しておく NativeActivity = GetNativeActivity() ; // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return -1 ; } // Java のクラス MainActivity を取得 jclass jclass_MainActivity = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス MainActivity のメンバー変数の ID を取得 jfieldID jfieldID_HTTP_ResponseCode = env->GetStaticFieldID( jclass_MainActivity, "HTTP_ResponseCode", "I" ) ; jfieldID jfieldID_HTTP_Connect_Finish = env->GetStaticFieldID( jclass_MainActivity, "HTTP_Connect_Finish", "I" ) ; // Java のクラス MainActivity のメンバー変数の値を取得 jint jint_HTTP_ResponseCode = env->GetStaticIntField( jclass_MainActivity, jfieldID_HTTP_ResponseCode ) ; jint jint_HTTP_Connect_Finish = env->GetStaticIntField( jclass_MainActivity, jfieldID_HTTP_Connect_Finish ) ; // まだ通信処理が終わっていなかったら 0 を返す if( jint_HTTP_Connect_Finish == 0 ) { Result = 0 ; } else { // 終わっていたらレスポンスコードを返す Result = jint_HTTP_ResponseCode ; } // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; // 戻り値を返す return Result ; } // HTTP通信の例外メッセージを所得する // 戻り値 : 例外のメッセージ文字列のポインタ char *Android_HTTP_GetExceptionMessage( void ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; static char HTTP_ExceptionMessage[ 64 * 1024 ] ; // アプリの NativeActivity を取得しておく NativeActivity = GetNativeActivity() ; // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return NULL ; } // Java のクラス MainActivity を取得 jclass jclass_MainActivity = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス MainActivity のメンバー変数の ID を取得 jfieldID jfieldID_HTTP_ExceptionMessage = env->GetStaticFieldID( jclass_MainActivity, "HTTP_ExceptionMessage", "Ljava/lang/String;" ) ; // Java のクラス MainActivity のメンバー変数の値を取得 jstring jstring_HTTP_ExceptionMessage = ( jstring )env->GetStaticObjectField( jclass_MainActivity, jfieldID_HTTP_ExceptionMessage ) ; // jstring の文字列をグローバル変数にコピー const char *utf8_HTTP_ExceptionMessage = env->GetStringUTFChars( jstring_HTTP_ExceptionMessage, NULL ) ; if( utf8_HTTP_ExceptionMessage != NULL ) { strcpyDx( HTTP_ExceptionMessage, utf8_HTTP_ExceptionMessage ) ; } else { HTTP_ExceptionMessage[ 0 ] = '\0' ; } env->ReleaseStringUTFChars( jstring_HTTP_ExceptionMessage, utf8_HTTP_ExceptionMessage ) ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; // メッセージ文字列を返す return HTTP_ExceptionMessage ; } // HTTP通信の結果得られたデータのバッファを取得する // BufferBytes : バッファに格納されているデータのサイズを保存する変数のアドレス // 戻り値 : データのバッファの先頭アドレス void *Android_HTTP_GetBuffer( size_t *BufferBytes ) { JNIEnv *env ; const ANativeActivity *NativeActivity ; void *Result = NULL ; // アプリの NativeActivity を取得しておく NativeActivity = GetNativeActivity() ; // JavaVM とソフト実行用スレッドを関連付け( C++ から Java の機能を使用するために必要 ) if( NativeActivity->vm->AttachCurrentThreadAsDaemon( &env, NULL ) != JNI_OK ) { return NULL ; } // Java のクラス MainActivity を取得 jclass jclass_MainActivity = env->GetObjectClass( NativeActivity->clazz ) ; // Java のクラス MainActivity のメンバー変数の ID を取得 jfieldID jfieldID_HTTP_DataBuffer = env->GetStaticFieldID( jclass_MainActivity, "HTTP_DataBuffer", "[B" ) ; jfieldID jfieldID_HTTP_DataBufferEnableBytes = env->GetStaticFieldID( jclass_MainActivity, "HTTP_DataBufferEnableBytes", "I" ) ; // Java のクラス MainActivity のメンバー変数の内容を取得 jint jint_HTTP_DataBufferEnableBytes = env->GetStaticIntField( jclass_MainActivity, jfieldID_HTTP_DataBufferEnableBytes ) ; // 有効なデータが 1byte でもあれば、バッファの内容を取得する if( jint_HTTP_DataBufferEnableBytes > 0 ) { // Java のクラス MainActivity のメンバー変数 HTTP_DataBuffer の内容を取得 jbyteArray jbyteArray_HTTP_DataBuffer = ( jbyteArray )env->GetStaticObjectField( jclass_MainActivity, jfieldID_HTTP_DataBuffer ) ; jbyte *jbyte_Elements = env->GetByteArrayElements( jbyteArray_HTTP_DataBuffer, NULL ) ; memcpy( HTTP_DataBuffer, jbyte_Elements, jint_HTTP_DataBufferEnableBytes ) ; env->ReleaseByteArrayElements( jbyteArray_HTTP_DataBuffer, jbyte_Elements, 0 ) ; env->DeleteLocalRef( jbyteArray_HTTP_DataBuffer ) ; Result = HTTP_DataBuffer ; } // バッファの有効なデータのサイズを保存 *BufferBytes = jint_HTTP_DataBufferEnableBytes ; // JavaVM とソフト実行用スレッドの関連付け終了 NativeActivity->vm->DetachCurrentThread() ; // バッファのアドレスを返す return Result ; } int android_main( void ) { // DXライブラリの初期化 if( DxLib_Init() < 0 ) return -1 ; // 描画先を裏画面にする SetDrawScreen( DX_SCREEN_BACK ) ; // HTTP通信開始 Android_HTTP_Connect( "https://dxlib.xsrv.jp/index.html", "GET" ) ; // メインループ while( ProcessMessage() == 0 ) { // 画面のクリア ClearDrawScreen() ; // HTTP通信の結果によって処理を分岐 int ResponseCode = Android_HTTP_GetResponseCode() ; switch( ResponseCode ) { case 0 : // 通信が終了していない場合は『通信中』を表示する DrawString( 0, 0, "通信中", GetColor( 255,255,255 ) ) ; break ; case -2 : // IO例外が発生した場合はメッセージを表示する DrawFormatString( 0, 0, GetColor( 255,255,255 ), "IO例外発生 : %s", Android_HTTP_GetExceptionMessage() ) ; break ; case -3 : // MalformedURL例外が発生した場合はメッセージを表示する DrawFormatString( 0, 0, GetColor( 255,255,255 ), "MalformedURL例外発生 : %s", Android_HTTP_GetExceptionMessage() ) ; break ; default : // それ以外の場合は HTTPレスポンスコード // データのサイズとアドレスを取得 size_t DataBytes ; void *Buffer = Android_HTTP_GetBuffer( &DataBytes ) ; DrawFormatString( 0, 16 * 0, GetColor( 255,255,255 ), "HTTPレスポンスコード : %d", ResponseCode ) ; DrawFormatString( 0, 16 * 1, GetColor( 255,255,255 ), "データのサイズ : %d bytes", ( int )DataBytes ) ; DrawFormatString( 0, 16 * 2, GetColor( 255,255,255 ), "データ : %s", Buffer != NULL ? ( char * )Buffer : "None" ) ; break ; } // 裏画面の内容を表画面に反映 ScreenFlip() ; } // DXライブラリの後始末 DxLib_End() ; // ソフトの終了 return 0 ; }
 

  以上です。
  これでプロジェクトを実行すると、『DXライブラリ置き場』のトップページを HTTP通信でダウンロードする処理が実行され、
  画面に HTML のテキストが表示されます。( 通信が成功すれば、ですが… )

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

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

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

   ③ GetMethodID や GetFieldID で Java の関数や変数の ID を取得。( static な変数の場合は GetStaticFieldID )

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

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

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

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

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





戻る