출처 : 안드로이드(Android) 유니티(Unity3D) 연동 ,값전달, Toast, Dialog 등...
[android] 메시지 핸들러(Handler) 사용하기
[안드로이드] 유니티 C# 클래스와 자바 클래스간의 연동
Trying to create a simple AlertDialog plugin, crash occurs
Platform Dependent Compilation
20. 카드 짝찾기 게임(7) - 안드로이드용으로 변환 (Keystore 사용 빌드)
Unity에서 Android Activity와 연동하기 위해 Unity C# 소스 작성
Unity의 AndroidJavaObject 객체를 이용해서 Android에 호출하고 호출한 결과값은 바로 받을 수 없어 로직이 끝나고 다시 Android Activity에서 Unity C# 함수를 호출해서 결과 값을 받을 수 있습니다.
아래 예제는 Cs_CubeManager을 싱글톤 객체로 만들어서 AndroidJavaObject 객체를 Unity 전체에서 사용하도록 했으며, Cs_AndroidManager에서 AndroidJavaObject 객체를 할당 받도록 했습니다.
Android호출시 다른 Activity를 생성하거나 Toast 메시지 출력시 아래와 같이 오류가 발생하므로 Handle 사용해야 합니다.
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
개발시나 iOS에서는 동작하지 않도록 preprocessor로 UNITY_ANDROID를 사용하여, Android에서만 동작할 수 있도록 소스작성 합니다.
#if UNITY_ANDROID
// 유니티가 동작하는 액티비티를 저장하는 변수.
public AndroidJavaObject activity = null;
#endif
Android Activity와 연동하기 위해서 C# AndroidJavaObject 객체를 아래와 같이 할당 받아야 합니다.
AndroidJavaObject activity = null;
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
cubeMng.activity = jc.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject 객체의 Call 메소드를 호출 하여 Android Activity의 메소드를 호출합니다.
public void NativeAlert(string message) {
#if UNITY_ANDROID
// 유니티가 동작하는 액티비티를 저장하는 변수.
activity.Call("AlertMessage", message);
#endif
}
Cs_AndroidManager.cs
using UnityEngine;
public class Cs_AndroidManager : MonoBehaviour {
//private static Cs_AndroidManager _instance = null;
private Cs_CubeManager cubeMng;
public string androidLog = "No Log";
void Start() {
cubeMng = Cs_CubeManager.getInstance();
cubeMng.device_model = "test";
#if UNITY_ANDROID
AndroidCall();
#endif
}
#if UNITY_ANDROID
// 유니티가 동작하는 액티비티를 저장하는 변수.
//public AndroidJavaObject activity = null;
void AndroidCall()
{
// 현재 실행 중인 유니티 액티비티를 가져와서 변수에 저장.
//AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
cubeMng.activity = jc.GetStatic<AndroidJavaObject>("currentActivity");
cubeMng.activity.Call("initActivity", "Cs_AndroidManager");
}
#endif
// 자바 클래스로부터 전달받은 로그를 보여줍니다.
public void AndroidLog(string newAndroidLog) {
androidLog = newAndroidLog;
}
public void SetCountryCode(string code) {
cubeMng.country_code = code;
}
public void SetDeviceID(string did) {
cubeMng.device_id = did;
}
public void SetDeviceModel(string dModel) {
cubeMng.device_model = dModel;
}
/*
public static Cs_AndroidManager Instance
{
get
{
if (_instance == null)
{
Debug.Log("------------- _instance.0 = " + _instance);
_instance = FindObjectOfType(typeof(Cs_AndroidManager)) as Cs_AndroidManager;
Debug.Log("------------- _instance.1 = " + _instance);
if (_instance == null)
{
_instance = new GameObject("Cs_AndroidManager").AddComponent<Cs_AndroidManager>();
}
Debug.Log("------------- _instance.2 = " + _instance);
}
return _instance;
}
}
*/
}
Cs_CubeManager.cs
using UnityEngine;
using System;
public class Cs_CubeManager {
// 싱글톤 인스턴스
private static Cs_CubeManager instance = null;
public string country_code = "KOR";
public string device_id = "";
public string device_model = "";
#if UNITY_ANDROID
// 유니티가 동작하는 액티비티를 저장하는 변수.
public AndroidJavaObject activity = null;
#endif
private Cs_CubeManager() {
/*
if (Camera.mainCamera != null && Camera.mainCamera.GetComponent("WWWWiki") != null)
wWWWiki = Camera.mainCamera.GetComponent("WWWWiki") as WWWWiki;
*/
}
public static Cs_CubeManager getInstance() {
if (instance == null) {
instance = new Cs_CubeManager();
}
return instance;
}
public void NativeAlert(string message) {
#if UNITY_ANDROID
// 유니티가 동작하는 액티비티를 저장하는 변수.
activity.Call("AlertMessage", message);
#endif
}
}
Unity3D 에서 Android 소스로 Export 하기
1. 메뉴 [Build Settings...] 선택합니다.

2. [Build Settings] 화면에서 Platform 목록에서 Android 항목을 선택합니다.

3. Bundle Identifier에 Android에서 사용할 Package 명을 입력합니다.

4. 다시 [Build Settings] 화면에서 Create Eclipse project 체크 박스를 선택하고, 버튼 [Export]을 선택합니다.

5. Export 받을 경로를 선택하고, 버튼 [폴더 선택]을 선택합니다.

6. 탐색기로 아래와 같이 Export 받은 파일 목록을 확인 합니다.
파일 목록에서 classes.jar 파일을 확인 할 수 있는데,
C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin 폴더의 classes.jar 파일과 동일한 파일로
Unity에서 Export 받으면 자동으로 classes.jar 파일을 복사해서 빌드 라이브러리에 자동으로 추가 됩니다.

7. [Package Explorer] 에서 오른쪽 버튼을 선택해서, 팝업 메뉴에서 메뉴 [Import...]를 선택합니다.

8. 메뉴 [Existing Projects into Workspace]를 선택합니다.

9. 버튼 [Browse...]을 선택합니다.

10. Unity에서 Export 받은 폴더를 선택합니다.

11. 버튼 [Finish]을 선택합니다.

12. Import 된 파일 목록을 확인합니다.

Android 소스 작성
Unity에서 Export 받은 소스에는 UnityPlayerActivity 를 상속 받지 않아서,
UnityPlayerActivity 겍체를 상속 받아 CubeMainActivity 소스를 작성하고, AndroidManifest.xml에서 Main Activity를 CubeMainActivity 수정했습니다.
Unity에서 호출 받은 로직을 처리하기 위해서 Handler 상속 받아서 Unity3DHandler.java 소스를 작성합니다.
CubeMainActivity.java
package com.azanghs.cube;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Log;
import com.example.android.trivialdrivesample.util.IabHelper;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
public class CubeMainActivity extends UnityPlayerActivity {
private static String TAG = CubeMainActivity.class.getSimpleName();
private TelephonyManager m_telephonyManager;
String androidId = "KOR";
// The helper object
IabHelper mHelper;
private Unity3DHandler handler = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new Unity3DHandler(this);
m_telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
Log.i(TAG, "Build.MODEL = " + Build.MODEL);
Log.i(TAG, "Build.DEVICE = " + Build.DEVICE);
Log.i(TAG, "Build.PRODUCT = " + Build.PRODUCT);
}
@Override
public void onBackPressed() {
this.finishMessage();
}
public void QuitApplication() {
handler.sendEmptyMessage(0);
}
public void AlertMessage(final String message) {
Message msg = handler.obtainMessage(1);
msg.obj = message;
handler.sendMessage(msg);
}
public void finishMessage() {
new AlertDialog.Builder(this).setMessage("프로그램을 종료하시겠습니까?")
.setPositiveButton("확인", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//System.exit(0);
UnityPlayer.UnitySendMessage("GameObject", "FinishApp", "");
}
}).setNegativeButton("취소", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
}).show();
}
//요 부분입니다. 이걸... Thread가 불필요하다면
public void initActivity(final String messageFromUnity)
{
Log.i(TAG, "여기 호출 Build.MODEL = " + Build.MODEL);
UnityPlayer.UnitySendMessage("GameObject", "SetCountryCode", getResources().getConfiguration().locale.getISO3Country());
UnityPlayer.UnitySendMessage("GameObject", "SetDeviceModel", Build.MODEL);
// 디바이스 ID
String androidId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
UnityPlayer.UnitySendMessage("GameObject", "SetDeviceID", androidId);
//
//UnityPlayer.UnitySendMessage("Cs_AndroidManager", "AndroidLog", "4/[" + tagFromUnity + "]" + messageFromUnity);
}
public void alert(String message) {
AlertDialog.Builder bld = new AlertDialog.Builder(this);
bld.setMessage(message);
bld.setNeutralButton("OK", null);
Log.d(TAG, "Showing alert dialog: " + message);
bld.create().show();
}
}
Unity3DHandler.java
package com.azanghs.cube;
import android.os.Handler;
import android.os.Message;
public class Unity3DHandler extends Handler {
private CubeMainActivity mainActivity = null;
public Unity3DHandler(CubeMainActivity mainActivity) {
this.mainActivity = mainActivity;
}
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
mainActivity.finishMessage();
break;
case 1:
mainActivity.alert((String)msg.obj);
break;
default:
break;
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" android:theme="@android:style/Theme.NoTitleBar" package="com.azanghs.cube" android:versionName="1.0" android:versionCode="1">
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:anyDensity="true" />
<application android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false">
<activity android:name=".CubeMainActivity" android:launchMode="singleTask" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|uiMode|touchscreen" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--
<activity android:name="com.azanghs.cube.CubeProxyActivity" android:launchMode="singleTask" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.azanghs.cube.CubeActivity" android:launchMode="singleTask" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="portrait">
</activity>
<activity android:name="com.azanghs.cube.CubeNativeActivity" android:launchMode="singleTask" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="portrait">
<meta-data android:name="android.app.lib_name" android:value="unity" />
<meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
</activity>
-->
</application>
<uses-feature android:glEsVersion="0x00020000" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-feature android:name="android.hardware.touchscreen" />
<uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false" />
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17" />
</manifest>
Android 프로젝트를 Unity Plugin Jar로 Export 하기
1. 해당 프로젝트를 선택하고 메뉴 [Export...] 선택합니다.

2. 메뉴 [JAR file] 선택합니다.

3. 아래와 같이 선택합니다.
Jar 파일이 생성되는 파일 경로 Unity 소스 폴더의 %소스경로%\Assets\Plugins\Android 로 합니다. (변경불가)

4. 버튼 [Next >] 버튼 선택합니다.

5. 버튼 [Finish] 버튼 선택합니다.

6. Unity에서 Build 하면 Android 폰에 실행된 결과를 확인 할 수 있습니다.
- 설명 작성하려니 귀찬니즘이 발생해서 나중에 시간이 나면 다시 작성하도록 하겠음